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

feat: get_last_256_block_hashes #627

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion cairo/ethereum/cancun/blocks.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ from ethereum_types.numeric import U64, U256, Uint, bool
from ethereum_types.bytes import Bytes, Bytes8, Bytes32, TupleBytes, TupleBytes32
from ethereum.cancun.fork_types import Address, Bloom, Root
from ethereum.crypto.hash import Hash32

from ethereum.cancun.transactions_types import LegacyTransaction
struct WithdrawalStruct {
index: U64,
validator_index: U64,
Expand Down Expand Up @@ -88,3 +88,41 @@ struct ReceiptStruct {
struct Receipt {
value: ReceiptStruct*,
}

struct UnionBytesLegacyTransactionEnum {
bytes: Bytes,
legacy: LegacyTransaction,
}

struct UnionBytesLegacyTransaction {
value: UnionBytesLegacyTransactionEnum*,
}

struct TupleUnionBytesLegacyTransactionStruct {
data: UnionBytesLegacyTransaction*,
len: felt,
}

struct TupleUnionBytesLegacyTransaction {
value: TupleUnionBytesLegacyTransactionStruct*,
}

struct BlockStruct {
header: Header,
transactions: TupleUnionBytesLegacyTransaction,
ommers: TupleHeader,
withdrawals: TupleWithdrawal,
}

struct Block {
value: BlockStruct*,
}

struct ListBlockStruct {
data: Block*,
len: felt,
}

struct ListBlock {
value: ListBlockStruct*,
}
102 changes: 98 additions & 4 deletions cairo/ethereum/cancun/fork.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,28 @@ from starkware.cairo.common.cairo_builtins import (
)
from starkware.cairo.common.dict_access import DictAccess
from starkware.cairo.common.math import assert_not_zero, split_felt, assert_le_felt
from starkware.cairo.common.math_cmp import is_le
from starkware.cairo.common.math_cmp import is_le, is_le_felt
from starkware.cairo.common.registers import get_fp_and_pc

from ethereum_rlp.rlp import Extended, ExtendedImpl, encode_receipt_to_buffer
from ethereum_rlp.rlp import Extended, ExtendedImpl, encode_receipt_to_buffer, encode_header
from ethereum_types.bytes import Bytes, Bytes0, BytesStruct, TupleBytes32
from ethereum_types.numeric import Uint, bool, U256, U256Struct, U64
from ethereum.cancun.blocks import Header, Receipt, ReceiptStruct, TupleLog, Log, TupleLogStruct
from ethereum.cancun.blocks import (
Header,
Receipt,
ReceiptStruct,
TupleLog,
Log,
TupleLogStruct,
Block,
ListBlock,
TupleHeader,
)
from ethereum.cancun.bloom import logs_bloom
from ethereum.cancun.fork_types import (
Address,
ListHash32,
ListHash32Struct,
OptionalAddress,
SetAddress,
SetAddressStruct,
Expand Down Expand Up @@ -65,11 +77,12 @@ from ethereum.cancun.vm.gas import (
calculate_blob_gas_price,
)
from ethereum.cancun.vm.interpreter import process_message_call, MessageCallOutput
from ethereum.crypto.hash import keccak256
from ethereum.crypto.hash import keccak256, Hash32
from ethereum.exceptions import OptionalEthereumException
from ethereum.utils.numeric import (
divmod,
min,
is_zero,
U256_add,
U256__eq__,
U256_from_felt,
Expand All @@ -89,6 +102,16 @@ const EMPTY_OMMER_HASH_LOW = 0xd312451b948a7413f0a142fd40d49347;
const EMPTY_OMMER_HASH_HIGH = 0x1dcc4de8dec75d7aab85b567b6ccd41a;
const VERSIONED_HASH_VERSION_KZG = 0x01;

struct BlockChainStruct {
blocks: ListBlock,
state: State,
chain_id: U64,
}

struct BlockChain {
value: BlockChainStruct*,
}

func calculate_base_fee_per_gas{range_check_ptr}(
block_gas_limit: Uint,
parent_gas_limit: Uint,
Expand Down Expand Up @@ -732,3 +755,74 @@ func _check_versioned_hashes_version{range_check_ptr}(

return ();
}

func get_last_256_block_hashes{
range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: KeccakBuiltin*
}(chain: BlockChain) -> ListHash32 {
alloc_locals;

// If no blocks, return empty array
if (chain.value.blocks.value.len == 0) {
let (empty_hashes_alloc: Hash32*) = alloc();
tempvar empty_hashes = ListHash32(new ListHash32Struct(data=empty_hashes_alloc, len=0));
return empty_hashes;
}

// Get last 255 blocks or all blocks if less than 255
let is_le_255 = is_le(chain.value.blocks.value.len, 255);
if (is_le_255 != FALSE) {
tempvar start_idx = 0;
} else {
tempvar start_idx = chain.value.blocks.value.len - 255;
}
tempvar recent_blocks_len = chain.value.blocks.value.len - start_idx;

// Allocate list for hashes
let (hashes: Hash32*) = alloc();

// Get parent hashes from recent blocks
_get_parent_hashes{hashes=hashes}(
chain.value.blocks.value.data + start_idx, recent_blocks_len, 0
);

// Add hash of most recent block
let most_recent_block: Block* = chain.value.blocks.value.data + chain.value.blocks.value.len -
1;
let most_recent_hash = keccak256_header(most_recent_block.value.header);
assert hashes[recent_blocks_len] = most_recent_hash;
tempvar list_hash_32 = ListHash32(new ListHash32Struct(data=hashes, len=recent_blocks_len + 1));
return list_hash_32;
}

// Helper function to get parent hashes using a loop
func _get_parent_hashes{hashes: Hash32*}(blocks: Block*, len: felt, idx: felt) {
tempvar idx = idx;

loop:
let idx = [ap - 1];

let end_loop = is_zero(idx - len);
jmp end if end_loop != 0;

// Get block at current index and store parent hash
let block: Block* = blocks + idx;
assert hashes[idx] = block.value.header.value.parent_hash;

tempvar idx = idx + 1;

jmp loop;

end:
return ();
}

// Helper to compute header hash
func keccak256_header{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: KeccakBuiltin*}(
Eikix marked this conversation as resolved.
Show resolved Hide resolved
header: Header
) -> Hash32 {
// First RLP encode the header
let encoded_header = encode_header(header);

// Then compute keccak256 of the encoded bytes
return keccak256(encoded_header);
}
15 changes: 13 additions & 2 deletions cairo/tests/ethereum/cancun/test_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@
from hypothesis import strategies as st
from hypothesis.strategies import composite, integers

from ethereum.cancun.blocks import Header, Log
from ethereum.cancun.blocks import Block, Header, Log
from ethereum.cancun.fork import (
GAS_LIMIT_ADJUSTMENT_FACTOR,
BlockChain,
calculate_base_fee_per_gas,
check_gas_limit,
check_transaction,
get_last_256_block_hashes,
make_receipt,
process_transaction,
validate_header,
)
from ethereum.cancun.fork_types import Address
from ethereum.cancun.state import set_account
from ethereum.cancun.state import State, set_account
from ethereum.cancun.transactions import (
AccessListTransaction,
BlobTransaction,
Expand Down Expand Up @@ -259,3 +261,12 @@ def test_check_transaction(
state, tx, gas_available, chain_id, base_fee_per_gas, excess_blob_gas
)
assert cairo_state == state

@given(blocks=st.lists(st.builds(Block), max_size=300))
def test_get_last_256_block_hashes(self, cairo_run, blocks):
chain = BlockChain(blocks=blocks, state=State(), chain_id=U64(1))

py_result = get_last_256_block_hashes(chain)
cairo_result = cairo_run("get_last_256_block_hashes", chain)

assert py_result == cairo_result
2 changes: 2 additions & 0 deletions cairo/tests/test_serde.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ from ethereum.cancun.blocks import (
Log,
TupleLog,
Receipt,
ListBlock,
)

from ethereum.cancun.transactions_types import (
Expand All @@ -48,3 +49,4 @@ from ethereum.exceptions import EthereumException
from ethereum.cancun.state import TransientStorage
from ethereum.cancun.vm import Environment
from ethereum.cancun.vm.interpreter import MessageCallOutput
from ethereum.cancun.fork import BlockChain
8 changes: 7 additions & 1 deletion cairo/tests/test_serde.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from starkware.cairo.lang.vm.memory_dict import MemoryDict
from starkware.cairo.lang.vm.memory_segments import MemorySegmentManager

from ethereum.cancun.blocks import Header, Log, Receipt, Withdrawal
from ethereum.cancun.blocks import Block, Header, Log, Receipt, Withdrawal
from ethereum.cancun.fork import BlockChain
from ethereum.cancun.fork_types import Account, Address, Bloom, Root, VersionedHash
from ethereum.cancun.state import State, TransientStorage
from ethereum.cancun.transactions import (
Expand Down Expand Up @@ -261,6 +262,11 @@ def test_type(
List[Tuple[U256, U256]],
ExtendMemory,
MessageCallOutput,
Union[Bytes, LegacyTransaction],
Tuple[Union[Bytes, LegacyTransaction], ...],
Block,
List[Block],
BlockChain,
],
):
assume(no_empty_sequence(b))
Expand Down
12 changes: 11 additions & 1 deletion cairo/tests/utils/args_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
from cairo_addons.vm import DictTracker as RustDictTracker
from cairo_addons.vm import MemorySegmentManager as RustMemorySegmentManager
from cairo_addons.vm import Relocatable as RustRelocatable
from ethereum.cancun.blocks import Header, Log, Receipt, Withdrawal
from ethereum.cancun.blocks import Block, Header, Log, Receipt, Withdrawal
from ethereum.cancun.fork import BlockChain
from ethereum.cancun.fork_types import Account, Address, Bloom, Root, VersionedHash
from ethereum.cancun.state import State, TransientStorage
from ethereum.cancun.transactions import (
Expand Down Expand Up @@ -448,6 +449,15 @@ def __eq__(self, other):
("ethereum", "cancun", "blocks", "Log"): Log,
("ethereum", "cancun", "blocks", "TupleLog"): Tuple[Log, ...],
("ethereum", "cancun", "blocks", "Receipt"): Receipt,
("ethereum", "cancun", "blocks", "UnionBytesLegacyTransaction"): Union[
Bytes, LegacyTransaction
],
("ethereum", "cancun", "blocks", "TupleUnionBytesLegacyTransaction"): Tuple[
Union[Bytes, LegacyTransaction], ...
],
("ethereum", "cancun", "blocks", "Block"): Block,
("ethereum", "cancun", "blocks", "ListBlock"): List[Block],
("ethereum", "cancun", "fork", "BlockChain"): BlockChain,
("ethereum", "cancun", "fork_types", "Address"): Address,
("ethereum", "cancun", "fork_types", "SetAddress"): Set[Address],
("ethereum", "cancun", "fork_types", "Root"): Root,
Expand Down