Skip to content
This repository has been archived by the owner on Jan 9, 2025. It is now read-only.

fix: fix early return for create #810

Merged
merged 8 commits into from
Nov 23, 2023
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
50 changes: 34 additions & 16 deletions src/kakarot/instructions/system_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from starkware.cairo.common.math import split_felt, unsigned_div_rem
from starkware.cairo.common.math_cmp import is_le, is_not_zero, is_nn
from starkware.cairo.common.memcpy import memcpy
from starkware.cairo.common.uint256 import Uint256, uint256_eq
from starkware.cairo.common.uint256 import Uint256, uint256_lt
from starkware.cairo.common.registers import get_fp_and_pc

// Internal dependencies
Expand Down Expand Up @@ -229,10 +229,6 @@

let (value_high, value_low) = split_felt(sub_ctx.call_context.value);
tempvar value = Uint256(value_low, value_high);
let (is_zero) = uint256_eq(value, Uint256(0, 0));
if (is_zero != 0) {
return sub_ctx;
}

let calling_context = sub_ctx.call_context.calling_context;
let transfer = model.Transfer(
Expand Down Expand Up @@ -708,6 +704,7 @@
return (create2_address,);
}

// @notice Pre-compute the evm address of a contract account before deploying it.
func get_evm_address{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
Expand All @@ -724,8 +721,6 @@
alloc_locals;
let (state, account) = State.get_account(state, address);
let nonce = account.nonce;
let account = Account.set_nonce(account, nonce + 1);
let state = State.set_account(state, address, account);

// create2 context pops 4 off the stack, create pops 3
// so we use popped_len to derive the way we should handle
Expand Down Expand Up @@ -756,7 +751,6 @@
}(ctx: model.ExecutionContext*, popped_len: felt, popped: Uint256*) -> model.ExecutionContext* {
alloc_locals;

// Load CallContext bytecode code from memory
let value = popped[0];
let offset = popped[1];
let size = popped[2];
Expand All @@ -765,6 +759,7 @@
let (memory, gas_cost) = Memory.load_n(
self=ctx.memory, element_len=size.low, element=bytecode, offset=offset.low
);
let ctx = ExecutionContext.update_memory(ctx, memory);

// Get target account
let (state, evm_contract_address) = get_evm_address(
Expand All @@ -773,24 +768,35 @@
let (starknet_contract_address) = Account.compute_starknet_address(evm_contract_address);
tempvar address = new model.Address(starknet_contract_address, evm_contract_address);

// Create Account with empty bytecode
let (state, account) = State.get_account(state, address);
let is_collision = Account.has_code_or_nonce(account);
let (state, sender) = State.get_account(state, ctx.call_context.address);
let ctx = ExecutionContext.update_state(ctx, state);
let balance = [sender.balance];
let (insufficient_balance) = uint256_lt(balance, value);
if (insufficient_balance != 0) {
let stack = Stack.push_uint128(ctx.stack, 0);
let ctx = ExecutionContext.update_stack(ctx, stack);
return ctx;

Check warning on line 778 in src/kakarot/instructions/system_operations.cairo

View check run for this annotation

Codecov / codecov/patch

src/kakarot/instructions/system_operations.cairo#L776-L778

Added lines #L776 - L778 were not covered by tests
}

// Update calling context before creating sub context
let ctx = ExecutionContext.update_memory(ctx, memory);
let sender = Account.set_nonce(sender, sender.nonce + 1);
let state = State.set_account(state, ctx.call_context.address, sender);
let ctx = ExecutionContext.update_state(ctx, state);

let (state, account) = State.get_account(state, address);
let is_collision = Account.has_code_or_nonce(account);
if (is_collision != 0) {
let (revert_reason_len, revert_reason) = Errors.addressCollision();
tempvar ctx = ExecutionContext.stop(ctx, revert_reason_len, revert_reason, TRUE);
let stack = Stack.push_uint128(ctx.stack, 0);
let ctx = ExecutionContext.update_stack(ctx, stack);
let ctx = ExecutionContext.update_state(ctx, state);

Check warning on line 790 in src/kakarot/instructions/system_operations.cairo

View check run for this annotation

Codecov / codecov/patch

src/kakarot/instructions/system_operations.cairo#L788-L790

Added lines #L788 - L790 were not covered by tests
return ctx;
}
let account = Account.set_nonce(account, 1);
let state = State.set_account(state, address, account);
let ctx = ExecutionContext.update_state(ctx, state);

// Create sub context with copied state
let state = State.copy(ctx.state);
let (state, account) = State.get_account(state, address);
let account = Account.set_nonce(account, 1);
let state = State.set_account(state, address, account);
let (calldata: felt*) = alloc();
tempvar call_context: model.CallContext* = new model.CallContext(
Expand All @@ -808,6 +814,18 @@
is_create=TRUE,
);
let sub_ctx = ExecutionContext.init(call_context);

let transfer = model.Transfer(
sender=ctx.call_context.address, recipient=address, amount=value
);
let (state, success) = State.add_transfer(state, transfer);

if (success == 0) {
let (revert_reason_len, revert_reason) = Errors.balanceError();
let sub_ctx = ExecutionContext.stop(sub_ctx, revert_reason_len, revert_reason, TRUE);
let sub_ctx = ExecutionContext.update_state(sub_ctx, state);
return sub_ctx;

Check warning on line 827 in src/kakarot/instructions/system_operations.cairo

View check run for this annotation

Codecov / codecov/patch

src/kakarot/instructions/system_operations.cairo#L824-L827

Added lines #L824 - L827 were not covered by tests
}
let sub_ctx = ExecutionContext.update_state(sub_ctx, state);

return sub_ctx;
Expand Down
11 changes: 10 additions & 1 deletion src/kakarot/state.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from starkware.cairo.common.dict_access import DictAccess
from starkware.cairo.common.memcpy import memcpy
from starkware.cairo.common.registers import get_fp_and_pc
from starkware.cairo.common.uint256 import Uint256, uint256_add, uint256_sub, uint256_le
from starkware.cairo.common.uint256 import Uint256, uint256_add, uint256_sub, uint256_le, uint256_eq

from kakarot.account import Account
from kakarot.model import model
Expand Down Expand Up @@ -213,6 +213,15 @@
let fp_and_pc = get_fp_and_pc();
local __fp__: felt* = fp_and_pc.fp_val;

if (transfer.sender == transfer.recipient) {
return (self, 1);

Check warning on line 217 in src/kakarot/state.cairo

View check run for this annotation

Codecov / codecov/patch

src/kakarot/state.cairo#L217

Added line #L217 was not covered by tests
}

let (null_transfer) = uint256_eq(transfer.amount, Uint256(0, 0));
if (null_transfer != 0) {
return (self, 1);

Check warning on line 222 in src/kakarot/state.cairo

View check run for this annotation

Codecov / codecov/patch

src/kakarot/state.cairo#L222

Added line #L222 was not covered by tests
}

let (self, sender) = get_account(self, transfer.sender);
let (success) = uint256_le(transfer.amount, [sender.balance]);

Expand Down
16 changes: 13 additions & 3 deletions tests/src/kakarot/instructions/test_system_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ from starkware.cairo.common.bool import TRUE, FALSE
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin
from starkware.cairo.common.math import split_felt, assert_not_zero
from starkware.cairo.common.uint256 import Uint256, uint256_sub
from starkware.starknet.common.syscalls import get_contract_address

// Local dependencies
from data_availability.starknet import Starknet
Expand Down Expand Up @@ -632,7 +631,7 @@ func test__exec_create__should_return_a_new_context_with_bytecode_from_memory_at
let bytecode_len = 0;
let (bytecode: felt*) = alloc();
// As this test contract is mocking the contract account we have to set this contract address as the starknet_contract_address.
let (contract_address: felt) = get_contract_address();
let (contract_address: felt) = Account.compute_starknet_address(evm_caller_address);
let ctx = TestHelpers.init_context_at_address_with_stack(
contract_address, evm_caller_address, bytecode_len, bytecode, stack
);
Expand Down Expand Up @@ -680,6 +679,11 @@ func test__exec_create__should_return_a_new_context_with_bytecode_from_memory_at
account.code_len, account.code, return_data_len, sub_ctx.return_data
);

tempvar sender_address = new model.Address(contract_address, evm_caller_address);
let (state, sender) = State.get_account(state, sender_address);
assert [sender.balance] = Uint256(0, 0);
assert [account.balance] = Uint256(1, 0);

return ();
}

Expand Down Expand Up @@ -722,7 +726,8 @@ func test__exec_create2__should_return_a_new_context_with_bytecode_from_memory_a
let stack = Stack.push(stack, memory_offset);
let bytecode_len = 0;
let (bytecode: felt*) = alloc();
let (contract_address: felt) = get_contract_address();
let (contract_address: felt) = Account.compute_starknet_address(evm_caller_address);
tempvar sender_address = new model.Address(contract_address, evm_caller_address);
let ctx = TestHelpers.init_context_at_address_with_stack(
contract_address, evm_caller_address, bytecode_len, bytecode, stack
);
Expand Down Expand Up @@ -769,5 +774,10 @@ func test__exec_create2__should_return_a_new_context_with_bytecode_from_memory_a
account.code_len, account.code, return_data_len, sub_ctx.return_data
);

let (state, sender) = State.get_account(state, sender_address);

assert [sender.balance] = Uint256(0, 0);
assert [account.balance] = Uint256(1, 0);

return ();
}
26 changes: 20 additions & 6 deletions tests/src/kakarot/instructions/test_system_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,21 @@ async def test_should_return_a_new_context_based_on_calling_ctx_stack(
await mint(ZERO_ACCOUNT, 2)
await system_operations.test__exec_delegatecall__should_return_a_new_context_based_on_calling_ctx_stack().call()

async def test_create(self, system_operations):
async def test_create(self, system_operations, eth):
salt = 0
# given we start with the first anvil test account
evm_caller_address = to_checksum_address(
0xF39FD6E51AAD88F6F4CE6AB8827279CFFFB92266
)
expected_create_addr = get_create_address(evm_caller_address, salt)

starknet_contract_address = (
await system_operations.compute_starknet_address(
int(evm_caller_address, 16)
).call()
).result.contract_address
await eth.mint(starknet_contract_address, int_to_uint256(1)).execute()

await system_operations.test__exec_create__should_return_a_new_context_with_bytecode_from_memory_at_expected_address(
int(evm_caller_address, 16),
salt,
Expand All @@ -133,7 +140,7 @@ async def test_create_has_deterministic_address(self, system_operations, nonce):
int(expected_create_addr, 16),
).call()

async def test_create2(self, system_operations):
async def test_create2(self, system_operations, eth):
# we store a memory word in memory
# and have our bytecode as the memory read from an offset and size
# we take that word at an offset and size and use it as the bytecode to determine the expected create2 evm contract address
Expand All @@ -143,17 +150,24 @@ async def test_create2(self, system_operations):
size = 4
salt = 5
padded_salt = salt.to_bytes(32, byteorder="big")
evm_caller_address_int = 15
evm_caller_address_bytes = evm_caller_address_int.to_bytes(20, byteorder="big")
evm_caller_address = to_checksum_address(evm_caller_address_bytes)
evm_caller_address = to_checksum_address(
0xF39FD6E51AAD88F6F4CE6AB8827279CFFFB92266
)
bytecode = to_bytes(memory_word)[offset : offset + size]

starknet_contract_address = (
await system_operations.compute_starknet_address(
int(evm_caller_address, 16)
).call()
).result.contract_address
await eth.mint(starknet_contract_address, int_to_uint256(1)).execute()

expected_create2_addr = get_create2_address(
evm_caller_address, padded_salt, bytecode
)

await system_operations.test__exec_create2__should_return_a_new_context_with_bytecode_from_memory_at_expected_address(
evm_caller_address_int,
int(evm_caller_address, 16),
offset,
size,
salt,
Expand Down
Loading