Skip to content

Commit

Permalink
feat: add code hash to storage account (#1309)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

<!-- Give an estimate of the time you spent on this PR in terms of work
days.
Did you spend 0.5 days on this PR or rather 2 days?  -->

Time spent on this PR:

## Pull request type

<!-- Please try to limit your pull request to one type,
submit multiple pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying,
or link to a relevant issue. -->

Resolves #1267

## What is the new behavior?

- code hash is added to model.Account
- it is computed only once when creating a contract account

<!-- Reviewable:start -->
- - -
This change is [<img src="https://reviewable.io/review_button.svg"
height="34" align="absmiddle"
alt="Reviewable"/>](https://reviewable.io/reviews/kkrt-labs/kakarot/1309)
<!-- Reviewable:end -->

---------

Co-authored-by: Mathieu <60658558+enitrat@users.noreply.github.com>
  • Loading branch information
obatirou and enitrat committed Aug 1, 2024
1 parent 36998e3 commit ce568f2
Show file tree
Hide file tree
Showing 19 changed files with 292 additions and 45 deletions.
1 change: 1 addition & 0 deletions docs/general/accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Account contracts store the following information:
- `is_initialized`: A boolean indicating whether the account has been
initialized, used to prevent reinitializing an already initialized account.
- `evm_address`: The Ethereum address associated with this Starknet account.
- `code_hash`: The hash of the EVM contract account bytecode.

## Account entrypoints

Expand Down
1 change: 1 addition & 0 deletions src/backend/starknet.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ namespace Internals {
Internals._save_valid_jumpdests(
starknet_address, self.valid_jumpdests_start, self.valid_jumpdests
);
IAccount.set_code_hash(starknet_address, [self.code_hash]);
return ();
}

Expand Down
84 changes: 77 additions & 7 deletions src/kakarot/account.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,20 @@ from starkware.cairo.common.hash_state import (
)
from starkware.starknet.common.storage import normalize_address
from starkware.starknet.common.syscalls import get_contract_address

from starkware.cairo.lang.compiler.lib.registers import get_ap
from kakarot.constants import Constants
from kakarot.storages import (
Kakarot_uninitialized_account_class_hash,
Kakarot_native_token_address,
Kakarot_account_contract_class_hash,
Kakarot_cairo1_helpers_class_hash,
)
from kakarot.interfaces.interfaces import IAccount, IERC20
from kakarot.interfaces.interfaces import IAccount, IERC20, ICairo1Helpers
from kakarot.model import model
from kakarot.storages import Kakarot_evm_to_starknet_address
from utils.dict import default_dict_copy
from utils.utils import Helpers
from utils.bytes import bytes_to_bytes8_little_endian

namespace Account {
// @notice Create a new account
Expand All @@ -44,7 +46,12 @@ namespace Account {
// @return The updated state
// @return The account
func init(
address: model.Address*, code_len: felt, code: felt*, nonce: felt, balance: Uint256*
address: model.Address*,
code_len: felt,
code: felt*,
code_hash: Uint256*,
nonce: felt,
balance: Uint256*,
) -> model.Account* {
let (storage_start) = default_dict_new(0);
let (transient_storage_start) = default_dict_new(0);
Expand All @@ -53,6 +60,7 @@ namespace Account {
address=address,
code_len=code_len,
code=code,
code_hash=code_hash,
storage_start=storage_start,
storage=storage_start,
transient_storage_start=transient_storage_start,
Expand Down Expand Up @@ -82,6 +90,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=storage_start,
storage=storage,
transient_storage_start=transient_storage_start,
Expand Down Expand Up @@ -114,8 +123,17 @@ namespace Account {
tempvar address = new model.Address(starknet=starknet_address, evm=evm_address);
let balance = fetch_balance(address);
assert balance_ptr = new Uint256(balance.low, balance.high);
// empty code hash see https://eips.ethereum.org/EIPS/eip-1052
tempvar code_hash_ptr = new Uint256(
304396909071904405792975023732328604784, 262949717399590921288928019264691438528
);
let account = Account.init(
address=address, code_len=0, code=bytecode, nonce=0, balance=balance_ptr
address=address,
code_len=0,
code=bytecode,
code_hash=code_hash_ptr,
nonce=0,
balance=balance_ptr,
);
return account;
}
Expand All @@ -126,14 +144,21 @@ namespace Account {

let (bytecode_len, bytecode) = IAccount.bytecode(contract_address=starknet_address);
let (nonce) = IAccount.get_nonce(contract_address=starknet_address);
IAccount.get_code_hash(contract_address=starknet_address);
let (ap_val) = get_ap();
let code_hash = cast(ap_val - 2, Uint256*);

// CAs are instantiated with their actual nonce - EOAs are instantiated with the nonce=1
// that is set when they're deployed.

// If an account was created-selfdestructed in the same tx, its nonce is 0, thus
// it is considered as a new account as per the `has_code_or_nonce` rule.
let account = Account.init(
address=address, code_len=bytecode_len, code=bytecode, nonce=nonce, balance=balance_ptr
address=address,
code_len=bytecode_len,
code=bytecode,
code_hash=code_hash,
nonce=nonce,
balance=balance_ptr,
);
return account;
}
Expand Down Expand Up @@ -161,6 +186,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -200,6 +226,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -229,6 +256,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -258,6 +286,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -296,6 +325,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand All @@ -319,14 +349,19 @@ namespace Account {
// @param code_len The len of the code
// @param code The code array
// @return The updated Account with the code and valid jumpdests set
func set_code{range_check_ptr}(
func set_code{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
self: model.Account*, code_len: felt, code: felt*
) -> model.Account* {
alloc_locals;
compute_code_hash(code_len, code);
let (ap_val) = get_ap();
let code_hash = cast(ap_val - 2, Uint256*);
let (valid_jumpdests_start, valid_jumpdests) = Helpers.initialize_jumpdests(code_len, code);
return new model.Account(
address=self.address,
code_len=code_len,
code=code,
code_hash=code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand All @@ -348,6 +383,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand All @@ -367,6 +403,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -421,6 +458,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand All @@ -442,6 +480,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -578,6 +617,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=self.storage,
transient_storage_start=self.transient_storage_start,
Expand All @@ -603,6 +643,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=storage,
transient_storage_start=self.transient_storage_start,
Expand Down Expand Up @@ -637,6 +678,7 @@ namespace Account {
address=self.address,
code_len=self.code_len,
code=self.code,
code_hash=self.code_hash,
storage_start=self.storage_start,
storage=storage_ptr,
transient_storage_start=self.transient_storage_start,
Expand All @@ -650,6 +692,34 @@ namespace Account {
);
return self;
}

func compute_code_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
code_len: felt, code: felt*
) -> Uint256 {
alloc_locals;
if (code_len == 0) {
// see https://eips.ethereum.org/EIPS/eip-1052
let empty_code_hash = Uint256(
304396909071904405792975023732328604784, 262949717399590921288928019264691438528
);
return empty_code_hash;
}

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

let (implementation) = Kakarot_cairo1_helpers_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,
);
return code_hash;
}
}

namespace Internals {
Expand Down
17 changes: 17 additions & 0 deletions src/kakarot/accounts/account_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,23 @@ func is_valid_jumpdest{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_che
return (is_valid=is_valid);
}

@view
func get_code_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (
code_hash: Uint256
) {
let code_hash = AccountContract.get_code_hash();
return (code_hash,);
}

@external
func set_code_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
code_hash: Uint256
) {
Ownable.assert_only_owner();
AccountContract.set_code_hash(code_hash);
return ();
}

// @notice Authorizes a pre-eip155 transaction by message hash.
// @param message_hash The hash of the message.
@external
Expand Down
17 changes: 17 additions & 0 deletions src/kakarot/accounts/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ func Account_jumpdests_initialized() -> (initialized: felt) {
func Account_authorized_message_hashes(hash: Uint256) -> (res: felt) {
}

@storage_var
func Account_code_hash() -> (code_hash: Uint256) {
}

@event
func transaction_executed(response_len: felt, response: felt*, success: felt, gas_used: felt) {
}
Expand Down Expand Up @@ -436,6 +440,19 @@ namespace AccountContract {
);
return is_valid_jumpdest(index=index);
}

func get_code_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
) -> Uint256 {
let (code_hash) = Account_code_hash.read();
return code_hash;
}

func set_code_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
code_hash: Uint256
) {
Account_code_hash.write(code_hash);
return ();
}
}

namespace Internals {
Expand Down
19 changes: 2 additions & 17 deletions src/kakarot/instructions/environmental_information.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ from starkware.cairo.common.math_cmp import is_not_zero, is_nn
from starkware.cairo.common.uint256 import Uint256, uint256_le

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_cairo1_helpers_class_hash
from utils.array import slice
from utils.bytes import bytes_to_bytes8_little_endian
from utils.uint256 import uint256_to_uint160, uint256_add, uint256_eq
Expand Down Expand Up @@ -480,21 +479,7 @@ namespace EnvironmentalInformation {
return evm;
}

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

let (implementation) = Kakarot_cairo1_helpers_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(code_hash);
Stack.push_uint256([account.code_hash]);

return evm;
}
Expand Down
3 changes: 1 addition & 2 deletions src/kakarot/instructions/system_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ 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, unsigned_div_rem
from starkware.cairo.common.math_cmp import is_nn, is_not_zero
from starkware.cairo.common.registers import get_fp_and_pc
from starkware.cairo.common.uint256 import Uint256, uint256_lt, uint256_le
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, ICairo1Helpers
from kakarot.interfaces.interfaces import ICairo1Helpers
from kakarot.constants import Constants
from kakarot.errors import Errors
from kakarot.evm import EVM
Expand Down
6 changes: 6 additions & 0 deletions src/kakarot/interfaces/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ namespace IAccount {
to: felt, function_selector: felt, calldata_len: felt, calldata: felt*
) -> (retdata_len: felt, retdata: felt*, success: felt) {
}

func get_code_hash() -> (code_hash: Uint256) {
}

func set_code_hash(code_hash: Uint256) {
}
}

@contract_interface
Expand Down
2 changes: 2 additions & 0 deletions src/kakarot/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ namespace Kakarot {
alloc_locals;
let starknet_address = Account.get_starknet_address(evm_address);
IAccount.write_bytecode(starknet_address, bytecode_len, bytecode);
let code_hash = Account.compute_code_hash(bytecode_len, bytecode);
IAccount.set_code_hash(starknet_address, code_hash);
return ();
}

Expand Down
Loading

0 comments on commit ce568f2

Please sign in to comment.