Skip to content

Commit

Permalink
Merge branch 'main' into update-execution-specs-fix-signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
obatirou authored Feb 13, 2025
2 parents 6b1bce4 + 76a71d9 commit f537aeb
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 96 deletions.
40 changes: 35 additions & 5 deletions cairo/ethereum/cancun/state.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,9 @@ func begin_transaction{
let fp_and_pc = get_fp_and_pc();
local __fp__: felt* = fp_and_pc.fp_val;

// Set original storage tries if not already set
if (cast(state.value.original_storage_tries.value, felt) == 0) {
// Set original storage tries if in root transaction - indicated by a main trie with no parent
// dict
if (cast(state.value._main_trie.value._data.value.parent_dict, felt) == 0) {
let storage_tries = state.value._storage_tries;
let (new_dict_ptr_start, new_dict_ptr_end) = dict_copy(
cast(storage_tries.value._data.value.dict_ptr_start, DictAccess*),
Expand Down Expand Up @@ -969,8 +970,8 @@ func storage_roots{
);

build_map_addr_storage_trie{
map_addr_storage=map_addr_storage, storage_tries_ptr_end=storage_tries_end
}(storage_tries_start);
map_addr_storage=map_addr_storage, storage_tries_ptr_end=squashed_storage_tries_end
}(squashed_storage_tries_start);

// Squash the Mapping[address, trie[bytes32, u256]] to iterate over each address
let (squashed_map_addr_storage_start, squashed_map_addr_storage_end) = dict_squash(
Expand Down Expand Up @@ -1091,6 +1092,12 @@ func build_map_addr_storage_trie{
return ();
}

// Skip all None values, which are deleted trie entries. We don't need them to compute
// the storage roots.
if (cast(storage_tries_ptr.new_value, felt) == 0) {
return build_map_addr_storage_trie(storage_tries_ptr + DictAccess.SIZE);
}

let tup_address_b32 = get_tuple_address_bytes32_preimage_for_key(
storage_tries_ptr.key, storage_tries_ptr_end
);
Expand Down Expand Up @@ -1164,11 +1171,34 @@ func state_root{
raise('AssertionError');
}

// Squash the main trie for unique keys
let main_trie = state.value._main_trie;
let main_trie_start = cast(main_trie.value._data.value.dict_ptr_start, DictAccess*);
let main_trie_end = cast(main_trie.value._data.value.dict_ptr, DictAccess*);

let (squashed_main_trie_start, squashed_main_trie_end) = dict_squash(
main_trie_start, main_trie_end
);

tempvar squashed_main_trie = TrieAddressOptionalAccount(
new TrieAddressOptionalAccountStruct(
secured=bool(1),
default=OptionalAccount(cast(0, AccountStruct*)),
_data=MappingAddressAccount(
new MappingAddressAccountStruct(
dict_ptr_start=cast(squashed_main_trie_start, AddressAccountDictAccess*),
dict_ptr=cast(squashed_main_trie_end, AddressAccountDictAccess*),
parent_dict=cast(0, MappingAddressAccountStruct*),
),
),
),
);

let storage_roots_ = storage_roots(state);

tempvar trie_union = EthereumTries(
new EthereumTriesEnum(
account=state.value._main_trie,
account=squashed_main_trie,
storage=TrieBytes32U256(cast(0, TrieBytes32U256Struct*)),
transaction=TrieBytesOptionalUnionBytesLegacyTransaction(
cast(0, TrieBytesOptionalUnionBytesLegacyTransactionStruct*)
Expand Down
52 changes: 44 additions & 8 deletions cairo/ethereum/cancun/trie.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,11 @@ func trie_get_TrieAddressOptionalAccount{
let fp_and_pc = get_fp_and_pc();
local __fp__: felt* = fp_and_pc.fp_val;

with dict_ptr {
let (pointer) = hashdict_read(1, &key.value);
let (pointer) = hashdict_read{dict_ptr=dict_ptr}(1, &key.value);
if (pointer == 0) {
tempvar pointer = cast(trie.value.default.value, felt);
} else {
tempvar pointer = pointer;
}
let new_dict_ptr = cast(dict_ptr, AddressAccountDictAccess*);
let parent_dict = trie.value._data.value.parent_dict;
Expand All @@ -567,15 +570,19 @@ func trie_get_TrieAddressOptionalAccount{
func trie_get_TrieTupleAddressBytes32U256{
poseidon_ptr: PoseidonBuiltin*, trie: TrieTupleAddressBytes32U256
}(address: Address, key: Bytes32) -> U256 {
alloc_locals;
let dict_ptr = cast(trie.value._data.value.dict_ptr, DictAccess*);

let (keys) = alloc();
assert keys[0] = address.value;
assert keys[1] = key.value.low;
assert keys[2] = key.value.high;

with dict_ptr {
let (pointer) = hashdict_read(3, keys);
let (pointer) = hashdict_read{dict_ptr=dict_ptr}(3, keys);
if (pointer == 0) {
tempvar pointer = cast(trie.value.default.value, felt);
} else {
tempvar pointer = pointer;
}
let new_dict_ptr = cast(dict_ptr, TupleAddressBytes32U256DictAccess*);
let parent_dict = trie.value._data.value.parent_dict;
Expand All @@ -594,14 +601,18 @@ func trie_get_TrieTupleAddressBytes32U256{
func trie_get_TrieBytes32U256{poseidon_ptr: PoseidonBuiltin*, trie: TrieBytes32U256}(
key: Bytes32
) -> U256 {
alloc_locals;
let dict_ptr = cast(trie.value._data.value.dict_ptr, DictAccess*);

let (keys) = alloc();
assert keys[0] = key.value.low;
assert keys[1] = key.value.high;

with dict_ptr {
let (pointer) = hashdict_read(2, keys);
let (pointer) = hashdict_read{dict_ptr=dict_ptr}(2, keys);
if (pointer == 0) {
tempvar pointer = cast(trie.value.default.value, felt);
} else {
tempvar pointer = pointer;
}
let new_dict_ptr = cast(dict_ptr, Bytes32U256DictAccess*);
let parent_dict = trie.value._data.value.parent_dict;
Expand All @@ -620,10 +631,14 @@ func trie_get_TrieBytes32U256{poseidon_ptr: PoseidonBuiltin*, trie: TrieBytes32U
func trie_get_TrieBytesOptionalUnionBytesLegacyTransaction{
poseidon_ptr: PoseidonBuiltin*, trie: TrieBytesOptionalUnionBytesLegacyTransaction
}(key: Bytes) -> OptionalUnionBytesLegacyTransaction {
alloc_locals;
let dict_ptr = cast(trie.value._data.value.dict_ptr, DictAccess*);

with dict_ptr {
let (pointer) = hashdict_read(key.value.len, key.value.data);
let (pointer) = hashdict_read{dict_ptr=dict_ptr}(key.value.len, key.value.data);
if (pointer == 0) {
tempvar pointer = cast(trie.value.default.value, felt);
} else {
tempvar pointer = pointer;
}
let new_dict_ptr = cast(dict_ptr, BytesOptionalUnionBytesLegacyTransactionDictAccess*);
let parent_dict = trie.value._data.value.parent_dict;
Expand All @@ -646,9 +661,15 @@ func trie_get_TrieBytesOptionalUnionBytesLegacyTransaction{
func trie_get_TrieBytesOptionalUnionBytesReceipt{
poseidon_ptr: PoseidonBuiltin*, trie: TrieBytesOptionalUnionBytesReceipt
}(key: Bytes) -> OptionalUnionBytesReceipt {
alloc_locals;
let dict_ptr = cast(trie.value._data.value.dict_ptr, DictAccess*);

let (pointer) = hashdict_read{dict_ptr=dict_ptr}(key.value.len, key.value.data);
if (pointer == 0) {
tempvar pointer = cast(trie.value.default.value, felt);
} else {
tempvar pointer = pointer;
}
let new_dict_ptr = cast(dict_ptr, BytesOptionalUnionBytesReceiptDictAccess*);
let parent_dict = trie.value._data.value.parent_dict;
tempvar mapping = MappingBytesOptionalUnionBytesReceipt(
Expand All @@ -668,9 +689,15 @@ func trie_get_TrieBytesOptionalUnionBytesReceipt{
func trie_get_TrieBytesOptionalUnionBytesWithdrawal{
poseidon_ptr: PoseidonBuiltin*, trie: TrieBytesOptionalUnionBytesWithdrawal
}(key: Bytes) -> OptionalUnionBytesWithdrawal {
alloc_locals;
let dict_ptr = cast(trie.value._data.value.dict_ptr, DictAccess*);

let (pointer) = hashdict_read{dict_ptr=dict_ptr}(key.value.len, key.value.data);
if (pointer == 0) {
tempvar pointer = cast(trie.value.default.value, felt);
} else {
tempvar pointer = pointer;
}
let new_dict_ptr = cast(dict_ptr, BytesOptionalUnionBytesWithdrawalDictAccess*);
let parent_dict = trie.value._data.value.parent_dict;
tempvar mapping = MappingBytesOptionalUnionBytesWithdrawal(
Expand Down Expand Up @@ -1147,6 +1174,15 @@ func _prepare_trie_inner_storage{
return mapping_ptr_end;
}

// Skip all None values, which are deleted trie entries
// Note: Considering that the given trie was built from the state._storage_tries of type
// Trie[Tuple[Address, Bytes32], U256], there should not be any None values remaining.
if (dict_ptr.new_value.value == 0) {
return _prepare_trie_inner_storage(
trie, dict_ptr + Bytes32U256DictAccess.SIZE, mapping_ptr_end
);
}

let preimage_b32 = _get_bytes32_preimage_for_key(
dict_ptr.key.value, cast(trie.value._data.value.dict_ptr, DictAccess*)
);
Expand Down
10 changes: 6 additions & 4 deletions cairo/ethereum/cancun/vm/interpreter.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ func process_message_call{
if (has_collision.value + has_storage.value != FALSE) {
// Return early with collision error
tempvar collision_error = new EthereumException(AddressCollision);
let msg = create_empty_message_call_output(collision_error);
let msg = create_empty_message_call_output(Uint(0), collision_error);
return msg;
}

Expand Down Expand Up @@ -488,8 +488,8 @@ func process_message_call{

// Prepare return values based on error state
if (cast(evm.value.error, felt) != 0) {
let msg = create_empty_message_call_output(evm.value.error);
squash_evm{evm=evm}();
let msg = create_empty_message_call_output(evm.value.gas_left, evm.value.error);
return msg;
}

Expand All @@ -512,7 +512,9 @@ func process_message_call{
return msg;
}

func create_empty_message_call_output(error: EthereumException*) -> MessageCallOutput {
func create_empty_message_call_output(
gas_left: Uint, error: EthereumException*
) -> MessageCallOutput {
alloc_locals;
let (empty_logs: Log*) = alloc();
tempvar empty_tuple_log = TupleLog(new TupleLogStruct(data=empty_logs, len=0));
Expand All @@ -539,7 +541,7 @@ func create_empty_message_call_output(error: EthereumException*) -> MessageCallO
tempvar msg = MessageCallOutput(
new MessageCallOutputStruct(
gas_left=Uint(0),
gas_left=gas_left,
refund_counter=U256(new U256Struct(0, 0)),
logs=empty_tuple_log,
accounts_to_delete=empty_set1,
Expand Down
18 changes: 0 additions & 18 deletions cairo/programs/fibonacci.cairo

This file was deleted.

8 changes: 0 additions & 8 deletions cairo/scripts/compile_cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ def compile_cairo(file_name, should_implement_hints=True):
json.dump(program.Schema().dump(program), f, indent=4, sort_keys=True)


def compile_os():
compile_cairo(Path(__file__).parents[1] / "programs" / "os.cairo")


def compile_fibonacci():
compile_cairo(Path(__file__).parents[1] / "programs" / "fibonacci.cairo")


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Compile Cairo program")
parser.add_argument("file_name", help="The Cairo file to compile")
Expand Down
48 changes: 38 additions & 10 deletions cairo/tests/ethereum/cancun/test_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,21 @@
validate_header,
)
from ethereum.cancun.fork_types import Address, VersionedHash
from ethereum.cancun.state import State, set_account
from ethereum.cancun.state import Account, State, set_account
from ethereum.cancun.transactions import (
AccessListTransaction,
BlobTransaction,
FeeMarketTransaction,
LegacyTransaction,
Transaction,
calculate_intrinsic_cost,
signing_hash_155,
signing_hash_1559,
signing_hash_2930,
signing_hash_4844,
signing_hash_pre155,
)
from ethereum.cancun.utils.address import to_address
from ethereum.cancun.vm import Environment
from ethereum.cancun.vm.gas import TARGET_BLOB_GAS_PER_BLOCK
from ethereum.crypto.hash import Hash32, keccak256
Expand All @@ -42,7 +44,14 @@
from cairo_addons.testing.errors import strict_raises
from tests.ethereum.cancun.vm.test_interpreter import unimplemented_precompiles
from tests.utils.constants import OMMER_HASH
from tests.utils.strategies import account_strategy, address, bytes32, small_bytes, uint
from tests.utils.strategies import (
account_strategy,
address,
bounded_u256_strategy,
bytes32,
small_bytes,
uint,
)

MIN_BASE_FEE = 1_000

Expand Down Expand Up @@ -71,6 +80,7 @@ def tx_with_small_data(draw, gas_strategy=uint, gas_price_strategy=uint):
to=to,
gas=gas_strategy,
gas_price=gas_price_strategy,
value=bounded_u256_strategy(max_value=2**128 - 1),
)

access_list_tx = st.builds(
Expand All @@ -80,6 +90,7 @@ def tx_with_small_data(draw, gas_strategy=uint, gas_price_strategy=uint):
to=to,
gas=gas_strategy,
gas_price=gas_price_strategy,
value=bounded_u256_strategy(max_value=2**128 - 1),
)

base_fee_per_gas = draw(gas_price_strategy)
Expand All @@ -94,6 +105,7 @@ def tx_with_small_data(draw, gas_strategy=uint, gas_price_strategy=uint):
gas=gas_strategy,
max_priority_fee_per_gas=st.just(max_priority_fee_per_gas),
max_fee_per_gas=st.just(max_fee_per_gas),
value=bounded_u256_strategy(max_value=2**128 - 1),
)

blob_tx = st.builds(
Expand All @@ -107,6 +119,7 @@ def tx_with_small_data(draw, gas_strategy=uint, gas_price_strategy=uint):
blob_versioned_hashes=st.lists(st.from_type(VersionedHash), max_size=3).map(
tuple
),
value=bounded_u256_strategy(max_value=2**128 - 1),
)

# Choose one transaction type
Expand Down Expand Up @@ -194,31 +207,46 @@ def tx_with_sender_in_state(
env = draw(st.from_type(Environment))
if env.gas_price < env.base_fee_per_gas:
env.gas_price = draw(
st.one_of(
st.integers(
min_value=int(env.base_fee_per_gas), max_value=2**64 - 1
).map(Uint),
st.just(env.base_fee_per_gas),
st.integers(min_value=int(env.base_fee_per_gas), max_value=2**64 - 1).map(
Uint
)
)
# Values too high would cause taylor_exponential to run indefinitely.
env.excess_blob_gas = draw(
st.integers(0, 10 * int(TARGET_BLOB_GAS_PER_BLOCK)).map(U64)
)
state = env.state
tx = draw(tx_strategy)
account = draw(account_strategy)
private_key = draw(st.from_type(PrivateKey))
expected_address = int(private_key.public_key.to_address(), 16)
if draw(integers(0, 99)) < 80:
# to ensure the account has enough balance and tx.gas > intrinsic_cost
# also that the code is empty
env.origin = to_address(Uint(expected_address))
if calculate_intrinsic_cost(tx) > tx.gas:
tx = replace(tx, gas=(calculate_intrinsic_cost(tx) + Uint(10000)))

set_account(
state,
to_address(Uint(expected_address)),
Account(
balance=U256(tx.value) * U256(env.gas_price)
+ U256(env.excess_blob_gas)
+ U256(10000),
nonce=account.nonce,
code=bytes(),
),
)
# 2 * chain_id + 35 + v must be less than 2^64 for the signature of a legacy transaction to be valid
chain_id = draw(st.integers(min_value=1, max_value=(2**64 - 37) // 2).map(U64))
tx = draw(tx_strategy)
nonce = U256(account.nonce)
# To avoid useless failures, set nonce of the transaction to the nonce of the sender account
tx = (
replace(tx, chain_id=chain_id, nonce=nonce)
if not isinstance(tx, LegacyTransaction)
else replace(tx, nonce=nonce)
)
private_key = draw(st.from_type(PrivateKey))
expected_address = int(private_key.public_key.to_address(), 16)

pre_155 = draw(integers(0, 99)) < 50
if isinstance(tx, LegacyTransaction):
Expand Down
Loading

0 comments on commit f537aeb

Please sign in to comment.