Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into feat/memory
Browse files Browse the repository at this point in the history
  • Loading branch information
enitrat committed Dec 30, 2024
2 parents 87155a2 + 7fa1396 commit 283a532
Show file tree
Hide file tree
Showing 25 changed files with 460 additions and 268 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"exitstatus",
"fibonacci",
"fixturenames",
"frozendict",
"hookwrapper",
"intdigest",
"isort",
Expand Down Expand Up @@ -42,6 +43,7 @@
"sessionstart",
"slackapi",
"testrunfinished",
"usort",
"workerid",
"workerinput"
],
Expand Down
70 changes: 47 additions & 23 deletions cairo/ethereum/cancun/trie.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,31 @@ struct ExtensionNode {
value: ExtensionNodeStruct*,
}

struct SubnodesStruct {
branch_0: Extended,
branch_1: Extended,
branch_2: Extended,
branch_3: Extended,
branch_4: Extended,
branch_5: Extended,
branch_6: Extended,
branch_7: Extended,
branch_8: Extended,
branch_9: Extended,
branch_10: Extended,
branch_11: Extended,
branch_12: Extended,
branch_13: Extended,
branch_14: Extended,
branch_15: Extended,
}

struct Subnodes {
value: SubnodesStruct*,
}

struct BranchNodeStruct {
subnodes: SequenceExtended,
subnodes: Subnodes,
value: Extended,
}

Expand Down Expand Up @@ -187,9 +210,9 @@ func encode_internal_node{

branch_node:
let (value: Extended*) = alloc();
let len = node.value.branch_node.value.subnodes.value.len;
let len = 16;
// TOD0: check if we really need to copy of if we can just use the pointer
memcpy(value, node.value.branch_node.value.subnodes.value.data, len);
memcpy(value, node.value.branch_node.value.subnodes.value, len);
assert [value + len] = node.value.branch_node.value.value;
tempvar sequence = SequenceExtended(new SequenceExtendedStruct(value, len + 1));
let unencoded_ = ExtendedImpl.sequence(sequence);
Expand Down Expand Up @@ -858,27 +881,28 @@ func patricialize{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: Kec
let patricialized_15 = patricialize(branches.value.data[15], next_level);
let encoded_15 = encode_internal_node(patricialized_15);

let (sequence: Extended*) = alloc();
assert sequence[0] = encoded_0;
assert sequence[1] = encoded_1;
assert sequence[2] = encoded_2;
assert sequence[3] = encoded_3;
assert sequence[4] = encoded_4;
assert sequence[5] = encoded_5;
assert sequence[6] = encoded_6;
assert sequence[7] = encoded_7;
assert sequence[8] = encoded_8;
assert sequence[9] = encoded_9;
assert sequence[10] = encoded_10;
assert sequence[11] = encoded_11;
assert sequence[12] = encoded_12;
assert sequence[13] = encoded_13;
assert sequence[14] = encoded_14;
assert sequence[15] = encoded_15;

tempvar sequence_extended = SequenceExtended(new SequenceExtendedStruct(sequence, 16));
tempvar subnodes = Subnodes(
new SubnodesStruct(
encoded_0,
encoded_1,
encoded_2,
encoded_3,
encoded_4,
encoded_5,
encoded_6,
encoded_7,
encoded_8,
encoded_9,
encoded_10,
encoded_11,
encoded_12,
encoded_13,
encoded_14,
encoded_15,
),
);
let value_extended = ExtendedImpl.bytes(value);
tempvar branch_node = BranchNode(new BranchNodeStruct(sequence_extended, value_extended));
tempvar branch_node = BranchNode(new BranchNodeStruct(subnodes, value_extended));
let internal_node = InternalNodeImpl.branch_node(branch_node);

return internal_node;
Expand Down
6 changes: 3 additions & 3 deletions cairo/ethereum/exceptions.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Error types common across all Ethereum forks.
//
// When raising an exception, the exception is a valid pointer. Otherwise, the pointer is `0`. When
// checking for an exceptino, a simple cast(maybe_exception, felt) != 0 is enough to check if the
// checking for an exception, a simple cast(maybe_exception, felt) != 0 is enough to check if the
// function raised.
//
// Example:
Expand All @@ -16,10 +16,10 @@
// tempvar no_error = EthereumException(cast(0, BytesStruct*));
// ```

from ethereum_types.bytes import BytesStruct
from ethereum_types.bytes import Bytes

// @notice Base type for all exceptions _expected_ to be thrown during normal
// operation.
struct EthereumException {
value: BytesStruct*,
value: Bytes,
}
20 changes: 13 additions & 7 deletions cairo/ethereum/utils/numeric.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ from starkware.cairo.common.math_cmp import is_le, is_not_zero
from ethereum_types.numeric import Uint

func min{range_check_ptr}(a: felt, b: felt) -> felt {
if (a == b) {
return a;
}
alloc_locals;

let res = is_le(a, b);
if (res == 1) {
return a;
}
tempvar is_min_b;
%{ memory[ap - 1] = 1 if ids.b <= ids.a else 0 %}
jmp min_is_b if is_min_b != 0;

min_is_a:
assert [range_check_ptr] = b - a;
let range_check_ptr = range_check_ptr + 1;
return a;

min_is_b:
assert [range_check_ptr] = a - b;
let range_check_ptr = range_check_ptr + 1;
return b;
}

Expand Down
2 changes: 1 addition & 1 deletion cairo/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ profile = "black"
src_paths = ["src", "tests"]

[tool.uv.sources]
ethereum = { git = "https://github.com/kkrt-labs/execution-specs.git", branch = "dev/change-type-branch-nodes" }
ethereum = { git = "https://github.com/kkrt-labs/execution-specs.git", rev = "b255036441d64437bd4fc9f9068bc64c45470e93" }

[build-system]
requires = ["hatchling"]
Expand Down
9 changes: 6 additions & 3 deletions cairo/src/evm.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ from starkware.cairo.common.default_dict import default_dict_finalize

from src.errors import Errors
from src.model import model
from src.account import Account
from src.stack import Stack
from src.state import State
from src.utils.dict import dict_squash
from src.utils.utils import Helpers

// @title EVM related functions.
// @notice This file contains functions related to the execution context.
Expand All @@ -38,9 +39,11 @@ namespace EVM {
}

func finalize{range_check_ptr, evm: model.EVM*}() {
let (squashed_start, squashed_end) = default_dict_finalize(
evm.message.valid_jumpdests_start, evm.message.valid_jumpdests, 0
alloc_locals;
let (local squashed_start, local squashed_end) = dict_squash(
evm.message.valid_jumpdests_start, evm.message.valid_jumpdests
);
Helpers.finalize_jumpdests(0, squashed_start, squashed_end, evm.message.bytecode);
tempvar message = new model.Message(
bytecode=evm.message.bytecode,
bytecode_len=evm.message.bytecode_len,
Expand Down
5 changes: 4 additions & 1 deletion cairo/src/instructions/memory_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,10 @@ namespace MemoryOperations {
let current_value = State.read_storage(evm.message.address, key);

let initial_state = evm.message.initial_state;
let original_value = State.read_storage{state=initial_state}(evm.message.address, key);
// TODO: This raises with wrong dict pointer as the State.copy() only copies visited accounts
// so unvisited accounts are actually the same in the initial state and the current state.
// let original_value = State.read_storage{state=initial_state}(evm.message.address, key);
tempvar original_value = new Uint256(0, 0);

tempvar message = new model.Message(
bytecode=evm.message.bytecode,
Expand Down
147 changes: 63 additions & 84 deletions cairo/src/interpreter.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,49 @@ namespace Interpreter {
let valid_jumpdests_start = cast([ap - 2], DictAccess*);
let valid_jumpdests = cast([ap - 1], DictAccess*);

let initial_state = State.copy();
let initial_state = state;
let state = State.copy();
let stack = Stack.init();
let memory = Memory.init();

// Cache the coinbase, precompiles, caller, and target, making them warm
State.get_account(env.coinbase);
State.cache_precompiles();
State.get_account(address);
let access_list_cost = State.cache_access_list(access_list_len, access_list);

let intrinsic_gas = intrinsic_gas + access_list_cost;
assert [range_check_ptr] = gas_limit - intrinsic_gas;
let range_check_ptr = range_check_ptr + 1;
assert [range_check_ptr] = (2 * Constants.MAX_CODE_SIZE + 1 - bytecode_len) * is_deploy_tx;
let range_check_ptr = range_check_ptr + 1;

// Charge the gas fee to the user without setting up a transfer.
// Transfers with the exact amounts will be performed post-execution.
// Note: balance > effective_fee was verified in eth_send_raw_unsigned_tx()
let max_fee = gas_limit * env.gas_price;
let (fee_high, fee_low) = split_felt(max_fee);
let max_fee_u256 = Uint256(low=fee_low, high=fee_high);

let sender = State.get_account(env.origin);
let (local new_balance) = uint256_sub([sender.balance], max_fee_u256);
let sender = Account.set_balance(sender, &new_balance);
let sender = Account.set_nonce(sender, sender.nonce + 1);
State.update_account(env.origin, sender);

let transfer = model.Transfer(env.origin, address, [value]);
let success = State.add_transfer(transfer);

// Check collision
let account = State.get_account(address);
let code_or_nonce = Account.has_code_or_nonce(account);
let is_collision = code_or_nonce * is_deploy_tx;
// Nonce is set to 1 in case of deploy_tx and account is marked as created
let nonce = account.nonce * (1 - is_deploy_tx) + is_deploy_tx;
let account = Account.set_nonce(account, nonce);
let account = Account.set_created(account, is_deploy_tx);
State.update_account(address, account);

tempvar message = new model.Message(
bytecode=bytecode,
bytecode_len=bytecode_len,
Expand All @@ -885,65 +927,7 @@ namespace Interpreter {
env=env,
initial_state=initial_state,
);

let stack = Stack.init();
let memory = Memory.init();

// Cache the coinbase, precompiles, caller, and target, making them warm
with state {
let coinbase = State.get_account(env.coinbase);
State.cache_precompiles();
State.get_account(address);
let access_list_cost = State.cache_access_list(access_list_len, access_list);
}

let intrinsic_gas = intrinsic_gas + access_list_cost;
let evm = EVM.init(message, gas_limit - intrinsic_gas);

let is_gas_limit_enough = is_le_felt(intrinsic_gas, gas_limit);
if (is_gas_limit_enough == FALSE) {
let evm = EVM.halt_validation_failed(evm);
State.finalize{state=state}();
return ();
}

tempvar is_initcode_invalid = is_deploy_tx * is_nn(
bytecode_len - (2 * Constants.MAX_CODE_SIZE + 1)
);
if (is_initcode_invalid != FALSE) {
let evm = EVM.halt_validation_failed(evm);
State.finalize{state=state}();
return ();
}

// Charge the gas fee to the user without setting up a transfer.
// Transfers with the exact amounts will be performed post-execution.
// Note: balance > effective_fee was verified in eth_send_raw_unsigned_tx()
let max_fee = gas_limit * env.gas_price;
let (fee_high, fee_low) = split_felt(max_fee);
let max_fee_u256 = Uint256(low=fee_low, high=fee_high);

with state {
let sender = State.get_account(env.origin);
let (local new_balance) = uint256_sub([sender.balance], max_fee_u256);
let sender = Account.set_balance(sender, &new_balance);
let sender = Account.set_nonce(sender, sender.nonce + 1);
State.update_account(env.origin, sender);
let transfer = model.Transfer(env.origin, address, [value]);
let success = State.add_transfer(transfer);
// Check collision
let account = State.get_account(address);
let code_or_nonce = Account.has_code_or_nonce(account);
let is_collision = code_or_nonce * is_deploy_tx;
// Nonce is set to 1 in case of deploy_tx and account is marked as created
let nonce = account.nonce * (1 - is_deploy_tx) + is_deploy_tx;
let account = Account.set_nonce(account, nonce);
let account = Account.set_created(account, is_deploy_tx);
State.update_account(address, account);
}

if (is_collision != 0) {
let (revert_reason_len, revert_reason) = Errors.addressCollision();
tempvar evm = EVM.stop(evm, revert_reason_len, revert_reason, Errors.EXCEPTIONAL_HALT);
Expand All @@ -958,25 +942,15 @@ namespace Interpreter {
tempvar evm = evm;
}

with stack, memory, state {
with stack, memory {
let evm = run(evm);
}

let required_gas = gas_limit - evm.gas_left;
let (max_refund, _) = unsigned_div_rem(required_gas, 5);
let is_max_refund_le_gas_refund = is_nn(evm.gas_refund - max_refund);
tempvar gas_refund = is_max_refund_le_gas_refund * max_refund + (
1 - is_max_refund_le_gas_refund
) * evm.gas_refund;

let total_gas_used = required_gas - gas_refund;
let initial_state = evm.message.initial_state;
State.finalize{state=initial_state}();

// Reset the state if the execution has failed.
// Only the gas fee paid will be committed.
State.finalize{state=state}();
tempvar initial_state = evm.message.initial_state;
State.finalize{state=initial_state}();

if (evm.reverted != 0) {
tempvar state = initial_state;
} else {
Expand All @@ -986,25 +960,30 @@ namespace Interpreter {
let success = 1 - is_reverted;
let paid_fee_u256 = Uint256(max_fee_u256.low * success, max_fee_u256.high * success);

with state {
let sender = State.get_account(env.origin);
uint256_add([sender.balance], paid_fee_u256);
let (ap_val) = get_ap();
let sender = Account.set_balance(sender, cast(ap_val - 3, Uint256*));
let sender = Account.set_nonce(sender, sender.nonce + is_reverted);
State.update_account(env.origin, sender);
}
let sender = State.get_account(env.origin);
uint256_add([sender.balance], paid_fee_u256);
let (ap_val) = get_ap();
let sender = Account.set_balance(sender, cast(ap_val - 3, Uint256*));
let sender = Account.set_nonce(sender, sender.nonce + is_reverted);
State.update_account(env.origin, sender);

// So as to not burn the base_fee_per gas, we send it to the coinbase.
let required_gas = gas_limit - evm.gas_left;
let (max_refund, _) = unsigned_div_rem(required_gas, 5);
let is_max_refund_le_gas_refund = is_nn(evm.gas_refund - max_refund);
tempvar gas_refund = is_max_refund_le_gas_refund * max_refund + (
1 - is_max_refund_le_gas_refund
) * evm.gas_refund;

let total_gas_used = required_gas - gas_refund;
let actual_fee = total_gas_used * env.gas_price;
let (fee_high, fee_low) = split_felt(actual_fee);
let actual_fee_u256 = Uint256(low=fee_low, high=fee_high);
let transfer = model.Transfer(env.origin, env.coinbase, actual_fee_u256);

with state {
State.add_transfer(transfer);
State.finalize();
}
// TODO: This should be burnt
State.add_transfer(transfer);
State.finalize();

return ();
}
Expand Down
Loading

0 comments on commit 283a532

Please sign in to comment.