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

Commit

Permalink
feat: chain_id storage var (#1571)
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
- [ ] 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 #1530

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by
this PR. -->

Chain id is set in constructor and stored in storage

<!-- 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/1571)
<!-- Reviewable:end -->
  • Loading branch information
obatirou authored Nov 6, 2024
1 parent 4fd4a0a commit d4ae7b7
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 25 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,13 @@ on an underlying StarknetOS network.
It is **not** a description on how to deploy a solidity contract on the Kakarot
EVM.

Note that the chosen `chain_id` when deploying is important:

- To keep compatibility with metamask the max chain id is 4503599627370476 see
https://gist.github.com/rekmarks/a47bd5f2525936c4b8eee31a16345553
- To be compatible with ledger the chain id needs to be inferior to 4 bytes see
https://github.com/kkrt-labs/kakarot/issues/1530

The [deploy script](./kakarot_scripts/deploy_kakarot.py) relies on some env
variables defined in a `.env` file located at the root of the project and loaded
in the [constant file](./kakarot_scripts/constants.py). To get started, just
Expand Down
3 changes: 0 additions & 3 deletions cairo_zero/kakarot/constants.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ namespace Constants {
const EMPTY_CODE_HASH_HIGH = 0xc5d2460186f7233c927e7db2dcc703c0;
const BURN_ADDRESS = 0xdead;

// See https://gist.github.com/rekmarks/a47bd5f2525936c4b8eee31a16345553
const MAX_SAFE_CHAIN_ID = 4503599627370476;

// PRECOMPILES

// Rollup precompiles
Expand Down
12 changes: 12 additions & 0 deletions cairo_zero/kakarot/kakarot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}()
return ();
}

// @notice chain_id initializer
@external
func initialize_chain_id{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
chain_id: felt
) {
Ownable.assert_only_owner();
Kakarot.initialize_chain_id(chain_id);
return ();
}

// Constructor
@constructor
func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
Expand All @@ -79,6 +89,7 @@ func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr
uninitialized_account_class_hash: felt,
cairo1_helpers_class_hash: felt,
block_gas_limit: felt,
chain_id: felt,
) {
return Kakarot.constructor(
owner,
Expand All @@ -87,6 +98,7 @@ func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr
uninitialized_account_class_hash,
cairo1_helpers_class_hash,
block_gas_limit,
chain_id,
);
}

Expand Down
20 changes: 18 additions & 2 deletions cairo_zero/kakarot/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ from kakarot.storages import (
Kakarot_coinbase,
Kakarot_prev_randao,
Kakarot_block_gas_limit,
Kakarot_chain_id,
Kakarot_evm_to_starknet_address,
Kakarot_authorized_cairo_precompiles_callers,
Kakarot_l1_messaging_contract_address,
Expand All @@ -45,20 +46,23 @@ namespace Kakarot {
// @param uninitialized_account_class_hash The class hash of the uninitialized account used for deterministic address calculation.
// @param cairo1_helpers_class_hash The precompiles class hash for precompiles not implemented in Kakarot.
// @param block_gas_limit The block gas limit.
// @param chain_id The chain ID.
func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
owner: felt,
native_token_address,
account_contract_class_hash,
uninitialized_account_class_hash,
cairo1_helpers_class_hash,
block_gas_limit,
chain_id: felt,
) {
Ownable.initializer(owner);
Kakarot_native_token_address.write(native_token_address);
Kakarot_account_contract_class_hash.write(account_contract_class_hash);
Kakarot_uninitialized_account_class_hash.write(uninitialized_account_class_hash);
Kakarot_cairo1_helpers_class_hash.write(cairo1_helpers_class_hash);
Kakarot_block_gas_limit.write(block_gas_limit);
Kakarot_chain_id.write(chain_id);
return ();
}

Expand Down Expand Up @@ -125,8 +129,7 @@ namespace Kakarot {
func eth_chain_id{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (
chain_id: felt
) {
let (tx_info) = get_tx_info();
let (_, chain_id) = unsigned_div_rem(tx_info.chain_id, Constants.MAX_SAFE_CHAIN_ID);
let (chain_id) = Kakarot_chain_id.read();
return (chain_id=chain_id);
}

Expand Down Expand Up @@ -410,4 +413,17 @@ namespace Kakarot {
0, l1_sender, to, 2100000000, 1, value_u256, data_len, data, 0, access_list
);
}

// @notice Initialize the chain ID
// @param chain_id The chain ID
func initialize_chain_id{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
chain_id: felt
) {
with_attr error_message("Kakarot: chain_id already initialized") {
let (current_chain_id) = Kakarot_chain_id.read();
assert current_chain_id = 0;
}
Kakarot_chain_id.write(chain_id);
return ();
}
}
4 changes: 4 additions & 0 deletions cairo_zero/kakarot/storages.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func Kakarot_prev_randao() -> (res: Uint256) {
func Kakarot_block_gas_limit() -> (res: felt) {
}

@storage_var
func Kakarot_chain_id() -> (res: felt) {
}

@storage_var
func Kakarot_authorized_cairo_precompiles_callers(address: felt) -> (res: felt) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class TestBlockInformation:
)
@SyscallHandler.patch("Kakarot_coinbase", COINBASE)
@SyscallHandler.patch("Kakarot_block_gas_limit", BLOCK_GAS_LIMIT)
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@patch.object(SyscallHandler, "block_timestamp", 0x1234)
def test__exec_block_information(self, cairo_run, opcode, expected_result):
output = cairo_run("test__exec_block_information", opcode=opcode)
Expand Down
8 changes: 8 additions & 0 deletions cairo_zero/tests/src/kakarot/test_kakarot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ from kakarot.kakarot import (
handle_l1_message,
pause,
unpause,
initialize_chain_id,
)
from kakarot.model import model
from kakarot.account import Account
Expand Down Expand Up @@ -280,3 +281,10 @@ func test__unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_p
unpause();
return ();
}

func test__initialize_chain_id{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {
tempvar chain_id;
%{ ids.chain_id = program_input["chain_id"] %}
initialize_chain_id(chain_id);
return ();
}
55 changes: 41 additions & 14 deletions cairo_zero/tests/src/kakarot/test_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@
from web3.exceptions import NoABIFunctionsFound

from kakarot_scripts.ef_tests.fetch import EF_TESTS_PARSED_DIR
from tests.utils.constants import (
CHAIN_ID,
MAX_SAFE_CHAIN_ID,
TRANSACTION_GAS_LIMIT,
TRANSACTIONS,
)
from tests.utils.constants import CHAIN_ID, TRANSACTION_GAS_LIMIT, TRANSACTIONS
from tests.utils.errors import cairo_error
from tests.utils.helpers import felt_to_signed_int, rlp_encode_signed_data
from tests.utils.syscall_handler import SyscallHandler, parse_state
Expand Down Expand Up @@ -97,7 +92,6 @@ def test_should_pause(self, cairo_run):
)

class TestUnpause:

@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error(message="Ownable: caller is not the owner"):
Expand Down Expand Up @@ -185,6 +179,31 @@ def test_should_set_prev_randao(self, cairo_run):
value=prev_randao,
)

class TestInitializeChainId:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__initialize_chain_id", chain_id=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
def test_should_initialize_chain_id(self, cairo_run):
chain_id = 0x123

cairo_run("test__initialize_chain_id", chain_id=chain_id)
SyscallHandler.mock_storage.assert_any_call(
address=get_storage_var_address("Kakarot_chain_id"),
value=chain_id,
)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
def test_should_fail_initialize_chain_id_twice(self, cairo_run):
chain_id = 0x123
with (
cairo_error(message="Kakarot: chain_id already initialized"),
SyscallHandler.patch("Kakarot_chain_id", chain_id),
):
cairo_run("test__initialize_chain_id", chain_id=chain_id)

class TestBlockGasLimit:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
Expand Down Expand Up @@ -521,12 +540,13 @@ def test_create_tx_returndata_should_be_20_bytes_evm_address(self, cairo_run):

class TestEthChainIdEntrypoint:
@given(chain_id=integers(min_value=0, max_value=2**64 - 1))
def test_should_return_chain_id_modulo_max_safe_chain_id(
self, cairo_run, chain_id
):
with patch.dict(SyscallHandler.tx_info, {"chain_id": chain_id}):
def test_should_return_chain_id(self, cairo_run, chain_id):
with (
patch.dict(SyscallHandler.tx_info, {"chain_id": chain_id}),
SyscallHandler.patch("Kakarot_chain_id", chain_id),
):
res = cairo_run("test__eth_chain_id")
assert res == chain_id % MAX_SAFE_CHAIN_ID
assert res == chain_id

class TestEthSendRawTransactionEntrypoint:
@SyscallHandler.patch("Pausable_paused", 1)
Expand Down Expand Up @@ -559,6 +579,7 @@ def test_should_raise_invalid_chain_id_tx_type_different_from_0(
)

@SyscallHandler.patch("IAccount.get_nonce", lambda addr, data: [1])
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@pytest.mark.parametrize("tx", TRANSACTIONS)
def test_should_raise_invalid_nonce(self, cairo_run, tx):
# explicitly set the nonce in transaction to be different from the patch
Expand All @@ -571,8 +592,9 @@ def test_should_raise_invalid_nonce(self, cairo_run, tx):
tx_data=tx_data,
)

@given(gas_limit=integers(min_value=2**64, max_value=2**248 - 1))
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@SyscallHandler.patch("IAccount.get_nonce", lambda _, __: [34])
@given(gas_limit=integers(min_value=2**64, max_value=2**248 - 1))
def test_raise_gas_limit_too_high(self, cairo_run, gas_limit):
tx = {
"type": 2,
Expand All @@ -595,8 +617,9 @@ def test_raise_gas_limit_too_high(self, cairo_run, gas_limit):
tx_data=tx_data,
)

@given(maxFeePerGas=integers(min_value=2**128, max_value=2**248 - 1))
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@SyscallHandler.patch("IAccount.get_nonce", lambda _, __: [34])
@given(maxFeePerGas=integers(min_value=2**128, max_value=2**248 - 1))
def test_raise_max_fee_per_gas_too_high(self, cairo_run, maxFeePerGas):
tx = {
"type": 2,
Expand All @@ -619,6 +642,7 @@ def test_raise_max_fee_per_gas_too_high(self, cairo_run, maxFeePerGas):
tx_data=tx_data,
)

@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@pytest.mark.parametrize("tx", TRANSACTIONS)
def test_raise_transaction_gas_limit_too_high(self, cairo_run, tx):
tx_data = list(rlp_encode_signed_data(tx))
Expand All @@ -635,6 +659,7 @@ def test_raise_transaction_gas_limit_too_high(self, cairo_run, tx):

@SyscallHandler.patch("Kakarot_block_gas_limit", TRANSACTION_GAS_LIMIT)
@SyscallHandler.patch("Kakarot_base_fee", TRANSACTION_GAS_LIMIT * 10**10)
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@pytest.mark.parametrize("tx", TRANSACTIONS)
def test_raise_max_fee_per_gas_too_low(self, cairo_run, tx):
tx_data = list(rlp_encode_signed_data(tx))
Expand All @@ -659,6 +684,7 @@ def max_priority_fee_too_high(draw):

@SyscallHandler.patch("Kakarot_block_gas_limit", TRANSACTION_GAS_LIMIT)
@SyscallHandler.patch("IAccount.get_nonce", lambda _, __: [34])
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@given(max_priority_fee_too_high())
def test_raise_max_priority_fee_too_high(
self, cairo_run, max_priority_fee_too_high
Expand Down Expand Up @@ -687,6 +713,7 @@ def test_raise_max_priority_fee_too_high(
@SyscallHandler.patch("IERC20.balanceOf", lambda _, __: [0, 0])
@SyscallHandler.patch("Kakarot_block_gas_limit", TRANSACTION_GAS_LIMIT)
@SyscallHandler.patch("IAccount.get_evm_address", lambda _, __: [0xABDE1])
@SyscallHandler.patch("Kakarot_chain_id", CHAIN_ID)
@pytest.mark.parametrize("tx", TRANSACTIONS)
def test_raise_not_enough_ETH_balance(self, cairo_run, tx):
tx_data = list(rlp_encode_signed_data(tx))
Expand Down
12 changes: 8 additions & 4 deletions kakarot_scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
BLOCK_GAS_LIMIT = 7_000_000
DEFAULT_GAS_PRICE = 1
BEACON_ROOT_ADDRESS = "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
# see https://gist.github.com/rekmarks/a47bd5f2525936c4b8eee31a16345553
MAX_SAFE_CHAIN_ID = 4503599627370476

# See https://github.com/kkrt-labs/kakarot/issues/1530
MAX_LEDGER_CHAIN_ID = 2**32 - 1


class NetworkType(Enum):
Expand Down Expand Up @@ -206,7 +207,8 @@ class NetworkType(Enum):
if WEB3.is_connected():
chain_id = WEB3.eth.chain_id
else:
chain_id = starknet_chain_id % MAX_SAFE_CHAIN_ID
# Before making any changes to chain_id see https://github.com/kkrt-labs/kakarot/issues/1530
chain_id = starknet_chain_id % MAX_LEDGER_CHAIN_ID
except (
requests.exceptions.ConnectionError,
requests.exceptions.MissingSchema,
Expand All @@ -216,14 +218,16 @@ class NetworkType(Enum):
f"⚠️ Could not get chain Id from {NETWORK['rpc_url']}: {e}, defaulting to KKRT"
)
starknet_chain_id = int.from_bytes(b"KKRT", "big")
chain_id = starknet_chain_id % MAX_SAFE_CHAIN_ID
# Before making any changes to chain_id see https://github.com/kkrt-labs/kakarot/issues/1530
chain_id = starknet_chain_id % MAX_LEDGER_CHAIN_ID


class ChainId(IntEnum):
chain_id = chain_id
starknet_chain_id = starknet_chain_id


# Before making any changes to chain_id see https://github.com/kkrt-labs/kakarot/issues/1530
NETWORK["chain_id"] = ChainId.chain_id

ETH_TOKEN_ADDRESS = 0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7
Expand Down
1 change: 1 addition & 0 deletions kakarot_scripts/deployment/kakarot_deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ async def deploy_or_upgrade_kakarot(owner):
class_hash["uninitialized_account"], # uninitialized_account_class_hash_
class_hash["Cairo1Helpers"],
BLOCK_GAS_LIMIT,
NETWORK["chain_id"],
)
await invoke(
"kakarot",
Expand Down
1 change: 1 addition & 0 deletions kakarot_scripts/deployment/starknet_deployments.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ async def deploy_starknet_contracts(account):
class_hash["uninitialized_account"],
class_hash["Cairo1Helpers"],
BLOCK_GAS_LIMIT,
NETWORK["chain_id"],
)
try:
coinbase = (
Expand Down
3 changes: 1 addition & 2 deletions tests/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

import pytest

from kakarot_scripts.constants import BLOCK_GAS_LIMIT, MAX_SAFE_CHAIN_ID
from kakarot_scripts.constants import BLOCK_GAS_LIMIT

BLOCK_GAS_LIMIT = BLOCK_GAS_LIMIT
MIN_BASE_FEE_PER_BLOB_GAS = 1

CHAIN_ID = int.from_bytes(b"KKRT", "big") # KKRT (0x4b4b5254) in ASCII
BIG_CHAIN_ID = int.from_bytes(b"SN_SEPOLIA", "big")
MAX_SAFE_CHAIN_ID = MAX_SAFE_CHAIN_ID

# Class hash of the cairo1 helpers
CAIRO1_HELPERS_CLASS_HASH = 0xDEADBEEFABDE1E11A5
Expand Down

0 comments on commit d4ae7b7

Please sign in to comment.