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

feat: use keccak builtin #1053

Merged
merged 6 commits into from
Apr 2, 2024
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
23 changes: 14 additions & 9 deletions src/kakarot/instructions/environmental_information.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.bool import FALSE
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin
from starkware.cairo.common.cairo_keccak.keccak import cairo_keccak_bigend, finalize_keccak
from starkware.cairo.common.memset import memset
from starkware.cairo.common.math import unsigned_div_rem, split_felt
from starkware.cairo.common.math_cmp import is_not_zero, is_le
from starkware.cairo.common.uint256 import Uint256, uint256_le, uint256_add, uint256_eq

from kakarot.account import Account
from kakarot.interfaces.interfaces import ICairo1Helpers
from kakarot.evm import EVM
from kakarot.errors import Errors
from kakarot.gas import Gas
from kakarot.memory import Memory
from kakarot.model import model
from kakarot.stack import Stack
from kakarot.state import State
from kakarot.storages import Kakarot_precompiles_class_hash
from utils.array import slice
from utils.bytes import bytes_to_bytes8_little_endian
from utils.uint256 import uint256_to_uint160
Expand Down Expand Up @@ -476,16 +477,20 @@ namespace EnvironmentalInformation {
}

let (local dst: felt*) = alloc();
bytes_to_bytes8_little_endian(dst, account.code_len, account.code);
let (dst_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
dst, account.code_len, account.code
);

let (keccak_ptr: felt*) = alloc();
local keccak_ptr_start: felt* = keccak_ptr;
with keccak_ptr {
let (result) = cairo_keccak_bigend(dst, account.code_len);
}
finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr);
let (implementation) = Kakarot_precompiles_class_hash.read();
let (code_hash) = ICairo1Helpers.library_call_keccak(
class_hash=implementation,
words_len=dst_len,
words=dst,
last_input_word=last_word,
last_input_num_bytes=last_word_num_bytes,
);

Stack.push_uint256(result);
Stack.push_uint256(code_hash);

return evm;
}
Expand Down
22 changes: 13 additions & 9 deletions src/kakarot/instructions/sha3.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.bool import FALSE
from starkware.cairo.common.math import split_felt, unsigned_div_rem
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin
from starkware.cairo.common.cairo_keccak.keccak import cairo_keccak_bigend, finalize_keccak
from starkware.cairo.common.uint256 import Uint256
from starkware.cairo.common.math_cmp import is_not_zero

from kakarot.evm import EVM
from kakarot.interfaces.interfaces import ICairo1Helpers
from kakarot.gas import Gas
from kakarot.memory import Memory
from kakarot.model import model
from kakarot.stack import Stack
from kakarot.storages import Kakarot_precompiles_class_hash
from utils.bytes import bytes_to_bytes8_little_endian

namespace Sha3 {
Expand Down Expand Up @@ -56,15 +57,18 @@ namespace Sha3 {
Memory.load_n(size.low, bigendian_data, offset.low);

let (local dst: felt*) = alloc();
bytes_to_bytes8_little_endian(dst, size.low, bigendian_data);

let (keccak_ptr: felt*) = alloc();
local keccak_ptr_start: felt* = keccak_ptr;
let (dst_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
dst, size.low, bigendian_data
);

with keccak_ptr {
let (result) = cairo_keccak_bigend(dst, size.low);
}
finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr);
let (implementation) = Kakarot_precompiles_class_hash.read();
let (result) = ICairo1Helpers.library_call_keccak(
class_hash=implementation,
words_len=dst_len,
words=dst,
last_input_word=last_word,
last_input_num_bytes=last_word_num_bytes,
);

Stack.push_uint256(result);

Expand Down
67 changes: 39 additions & 28 deletions src/kakarot/instructions/system_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.bool import TRUE, FALSE
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin
from starkware.cairo.common.cairo_keccak.keccak import cairo_keccak_bigend, finalize_keccak
from starkware.cairo.common.math import split_felt, unsigned_div_rem
from starkware.cairo.common.math_cmp import is_le, is_nn, is_not_zero
from starkware.cairo.common.registers import get_fp_and_pc
Expand All @@ -14,14 +13,15 @@ from starkware.cairo.common.default_dict import default_dict_new
from starkware.cairo.common.dict_access import DictAccess

from kakarot.account import Account
from kakarot.interfaces.interfaces import IAccount
from kakarot.interfaces.interfaces import IAccount, ICairo1Helpers
from kakarot.constants import Constants
from kakarot.errors import Errors
from kakarot.evm import EVM
from kakarot.gas import Gas
from kakarot.memory import Memory
from kakarot.model import model
from kakarot.stack import Stack
from kakarot.storages import Kakarot_precompiles_class_hash
from kakarot.state import State
from utils.utils import Helpers
from utils.array import slice
Expand Down Expand Up @@ -1007,15 +1007,18 @@ namespace CreateHelper {
assert message[0] = message_len + 0xc0 - 1;

let (message_bytes8: felt*) = alloc();
bytes_to_bytes8_little_endian(message_bytes8, message_len, message);

let (keccak_ptr: felt*) = alloc();
local keccak_ptr_start: felt* = keccak_ptr;
with keccak_ptr {
let (message_hash) = cairo_keccak_bigend(message_bytes8, message_len);
}
let (message_bytes8_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
message_bytes8, message_len, message
);

finalize_keccak(keccak_ptr_start, keccak_ptr);
let (implementation) = Kakarot_precompiles_class_hash.read();
let (message_hash) = ICairo1Helpers.library_call_keccak(
class_hash=implementation,
words_len=message_bytes8_len,
words=message_bytes8,
last_input_word=last_word,
last_input_num_bytes=last_word_num_bytes,
);

let address = uint256_to_uint160(message_hash);
return (address,);
Expand All @@ -1036,18 +1039,22 @@ namespace CreateHelper {
pedersen_ptr: HashBuiltin*,
range_check_ptr,
bitwise_ptr: BitwiseBuiltin*,
}(sender_address: felt, bytecode_len: felt, bytecode: felt*, salt: Uint256) -> (
evm_contract_address: felt
) {
}(sender_address: felt, bytecode_len: felt, bytecode: felt*, salt: Uint256) -> felt {
alloc_locals;
let (keccak_ptr: felt*) = alloc();
local keccak_ptr_start: felt* = keccak_ptr;

let (local bytecode_bytes8: felt*) = alloc();
bytes_to_bytes8_little_endian(bytecode_bytes8, bytecode_len, bytecode);
with keccak_ptr {
let (bytecode_hash) = cairo_keccak_bigend(bytecode_bytes8, bytecode_len);
}
let (bytecode_bytes8_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
bytecode_bytes8, bytecode_len, bytecode
);

let (implementation) = Kakarot_precompiles_class_hash.read();
let (bytecode_hash) = ICairo1Helpers.library_call_keccak(
class_hash=implementation,
words_len=bytecode_bytes8_len,
words=bytecode_bytes8,
last_input_word=last_word,
last_input_num_bytes=last_word_num_bytes,
);

// get keccak hash of
// marker + caller_address + salt + bytecode_hash
Expand All @@ -1061,16 +1068,20 @@ namespace CreateHelper {
let packed_bytes_len = 1 + 20 + 32 + 32;

let (local packed_bytes8: felt*) = alloc();
bytes_to_bytes8_little_endian(packed_bytes8, packed_bytes_len, packed_bytes);

with keccak_ptr {
let (create2_hash) = cairo_keccak_bigend(packed_bytes8, packed_bytes_len);
}

finalize_keccak(keccak_ptr_start, keccak_ptr);
let (packed_bytes8_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
packed_bytes8, packed_bytes_len, packed_bytes
);

let (create2_hash) = ICairo1Helpers.library_call_keccak(
class_hash=implementation,
words_len=packed_bytes8_len,
words=packed_bytes8,
last_input_word=last_word,
last_input_num_bytes=last_word_num_bytes,
);
let create2_address = uint256_to_uint160(create2_hash);
return (create2_address,);

return create2_address;
}

// @notice Pre-compute the evm address of a contract account before deploying it.
Expand All @@ -1095,7 +1106,7 @@ namespace CreateHelper {
return evm_contract_address;
} else {
let salt = popped[3];
let (evm_contract_address) = CreateHelper.get_create2_address(
let evm_contract_address = CreateHelper.get_create2_address(
sender_address=evm_address, bytecode_len=bytecode_len, bytecode=bytecode, salt=salt
);
return evm_contract_address;
Expand Down
30 changes: 27 additions & 3 deletions src/utils/bytes.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,28 @@ func uint256_to_bytes32{range_check_ptr}(dst: felt*, n: Uint256) {
return ();
}

func bytes_to_bytes8_little_endian(dst: felt*, bytes_len: felt, bytes: felt*) -> felt {
// @notice Converts an array of bytes to an array of bytes8, little endian
// @dev The individual bytes are packed into 8-byte words, little endian.
// The last word is returned separately, along with the number of used bytes
// as it may be incomplete.
// @param dst The destination array.
// @param bytes_len The number of bytes in the input array.
// @param bytes The input array.
// @return The number of bytes written to the destination array.
// @return The last word.
// @return The number of bytes used in the last word
func bytes_to_bytes8_little_endian{range_check_ptr}(dst: felt*, bytes_len: felt, bytes: felt*) -> (
felt, felt, felt
) {
alloc_locals;

if (bytes_len == 0) {
return (0);
return (0, 0, 0);
}

let (local pow256) = get_label_location(pow256_table);
let (full_u64_word_count, local last_input_num_bytes) = unsigned_div_rem(bytes_len, 8);
local range_check_ptr = range_check_ptr;

tempvar dst_index = 0;
tempvar bytes_index = bytes_len - 1;
Expand All @@ -161,9 +175,12 @@ func bytes_to_bytes8_little_endian(dst: felt*, bytes_len: felt, bytes: felt*) ->
tempvar bytes8 = bytes8 + current_byte * current_pow;

jmp next if bytes_index != 0;
jmp end_word_not_full if bytes8_index != 0;

let last_input_num_bytes = [fp + 1];
assert [dst + dst_index] = bytes8;
return (dst_index + 1);
let range_check_ptr = [fp + 2];
return (dst_index + 1, 0, 0);

next:
jmp regular if bytes8_index != 0;
Expand Down Expand Up @@ -191,6 +208,13 @@ func bytes_to_bytes8_little_endian(dst: felt*, bytes_len: felt, bytes: felt*) ->
static_assert bytes8_index == [ap - 1];
jmp body;

end_word_not_full:
tempvar dst_index = dst_index;
tempvar bytes8 = bytes8;

let range_check_ptr = [fp + 2];
return (dst_index, bytes8, last_input_num_bytes);

pow256_table:
dw 256 ** 7;
dw 256 ** 6;
Expand Down
6 changes: 5 additions & 1 deletion src/utils/eth_transaction.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,11 @@ namespace EthTransaction {
}

let (local words: felt*) = alloc();
bytes_to_bytes8_little_endian(words, tx_data_len, tx_data);
let (words_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
words, tx_data_len, tx_data
);
assert [words + words_len] = last_word;
let words_len = words_len + 1;

let (keccak_ptr: felt*) = alloc();
let keccak_ptr_start = keccak_ptr;
Expand Down
50 changes: 47 additions & 3 deletions tests/src/kakarot/instructions/test_environmental_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import pytest
from Crypto.Hash import keccak
from starkware.starknet.public.abi import get_selector_from_name

from tests.utils.syscall_handler import SyscallHandler
from tests.utils.uint256 import int_to_uint256

EXISTING_ACCOUNT = 0xABDE1
EXISTING_ACCOUNT_SN_ADDR = 0x1234
NON_EXISTING_ACCOUNT = 0xDEAD
CAIRO1_HELPERS_CLASS_HASH = 0xDEADBEEFABDE1


@pytest.fixture(scope="module", params=[0, 32], ids=["no bytecode", "32 bytes"])
Expand Down Expand Up @@ -113,6 +116,22 @@ def test_gasprice(self, cairo_run):
cairo_run("test__exec_gasprice")

class TestExtCodeHash:
def _pack_into_u64_words(self, bytecode):
bytes8_little_endian = [
int.from_bytes(bytes(bytecode[i : i + 8]), byteorder="little")
for i in range(0, len(bytecode), 8)
]

last_word_bytes_used = len(bytecode) % 8
if last_word_bytes_used == 0:
last_word = 0
full_words = bytes8_little_endian
else:
last_word = bytes8_little_endian[-1]
full_words = bytes8_little_endian[:-1]

return len(full_words), full_words, last_word, last_word_bytes_used

@SyscallHandler.patch(
"IERC20.balanceOf",
lambda sn_addr, data: (
Expand All @@ -128,13 +147,38 @@ class TestExtCodeHash:
EXISTING_ACCOUNT,
EXISTING_ACCOUNT_SN_ADDR,
)
@SyscallHandler.patch(
"Kakarot_precompiles_class_hash",
CAIRO1_HELPERS_CLASS_HASH,
)
def test_extcodehash__should_push_hash(
self, cairo_run, bytecode, bytecode_hash, address
):
with SyscallHandler.patch(
"IAccount.bytecode", lambda sn_addr, data: [len(bytecode), *bytecode]
), SyscallHandler.patch(
"ICairo1Helpers.library_call_keccak",
lambda class_hash, data: int_to_uint256(bytecode_hash),
):
output = cairo_run("test__exec_extcodehash", address=address)
assert output == (
hex(bytecode_hash) if address == EXISTING_ACCOUNT else "0x0"
)

if address == EXISTING_ACCOUNT:
(
len_full_words,
full_words,
last_expected_word,
last_expected_word_bytes_used,
) = self._pack_into_u64_words(bytecode)
SyscallHandler.mock_library_call.assert_any_call(
class_hash=CAIRO1_HELPERS_CLASS_HASH,
function_selector=get_selector_from_name("keccak"),
calldata=[
len_full_words,
*full_words,
last_expected_word,
last_expected_word_bytes_used,
],
)
assert output == hex(bytecode_hash)
else:
assert output == "0x0"
9 changes: 7 additions & 2 deletions tests/src/utils/test_bytes.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func test__uint256_to_bytes32{range_check_ptr}(output_ptr: felt*) {
return ();
}

func test__bytes_to_bytes8_little_endian(output_ptr: felt*) {
func test__bytes_to_bytes8_little_endian{range_check_ptr}(output_ptr: felt*) {
alloc_locals;
tempvar bytes_len: felt;
let (bytes) = alloc();
Expand All @@ -95,7 +95,12 @@ func test__bytes_to_bytes8_little_endian(output_ptr: felt*) {
segments.write_arg(ids.bytes, program_input["bytes"])
%}

bytes_to_bytes8_little_endian(output_ptr, bytes_len, bytes);
let (full_words_len, last_word, last_word_num_bytes) = bytes_to_bytes8_little_endian(
output_ptr, bytes_len, bytes
);

assert output_ptr[full_words_len] = last_word;
assert output_ptr[full_words_len + 1] = last_word_num_bytes;

return ();
}
Loading
Loading