Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev: catch cairo errors #1222

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
dev: catch cairo errors
  • Loading branch information
enitrat committed Jun 25, 2024
commit 8bdb4631e273b5d003f17d8bc2031c70a0c17a80
16 changes: 12 additions & 4 deletions src/kakarot/accounts/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ namespace AccountContract {
}(kakarot_address, evm_address, implementation_class) {
alloc_locals;
let (is_initialized) = Account_is_initialized.read();
assert is_initialized = 0;
with_attr error_message("Account already initialized") {
assert is_initialized = 0;
}
Account_is_initialized.write(1);
Ownable.initializer(kakarot_address);
Account_evm_address.write(evm_address);
Expand Down Expand Up @@ -754,7 +756,9 @@ namespace Internals {
) {
alloc_locals;
let tx = EthTransaction.decode(tx_data_len, tx_data);
assert tx.signer_nonce = account_nonce;
with_attr error_message("Invalid nonce") {
assert tx.signer_nonce = account_nonce;
}

// Note: here, the validate process assumes an ECDSA signature, and r, s, v field
// Technically, the transaction type can determine the signature scheme.
Expand All @@ -766,12 +770,16 @@ namespace Internals {
assert y_parity = v - 27;
} else {
assert y_parity = (v - 2 * chain_id - 35);
assert tx.chain_id = chain_id;
with_attr error_message("Invalid chain id") {
assert tx.chain_id = chain_id;
}
}
tempvar range_check_ptr = range_check_ptr;
} else {
assert y_parity = v;
assert tx.chain_id = chain_id;
with_attr error_message("Invalid chain id") {
assert tx.chain_id = chain_id;
}
tempvar range_check_ptr = range_check_ptr;
}

Expand Down
6 changes: 5 additions & 1 deletion tests/fixtures/starknet.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ def _factory(entrypoint, **kwargs) -> list:
vm_class=VmWithCoverage,
)
run_resources = RunResources(n_steps=4_000_000)
runner.run_until_pc(end, run_resources)
try:
runner.run_until_pc(end, run_resources)
except Exception as e:
raise Exception(e.__str__()) from e

runner.original_steps = runner.vm.current_step
runner.end_run(disable_trace_padding=False)
if request.config.getoption("proof_mode"):
Expand Down
16 changes: 8 additions & 8 deletions tests/src/kakarot/accounts/test_contract_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_should_set_storage_variables(self, cairo_run):
@SyscallHandler.patch("IKakarot.register_account", lambda addr, data: [])
@SyscallHandler.patch("Account_is_initialized", 1)
def test_should_run_only_once(self, cairo_run):
with cairo_error():
with cairo_error(message="Account already initialized"):
cairo_run(
"test__initialize",
kakarot_address=0x1234,
Expand All @@ -100,7 +100,7 @@ def test_should_return_stored_address(self, cairo_run):
class TestWriteBytecode:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__write_bytecode", bytecode=[])

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand Down Expand Up @@ -144,7 +144,7 @@ def test_should_read_bytecode(self, cairo_run, bytecode, storage):
class TestNonce:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_nonce", new_nonce=[])

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -158,7 +158,7 @@ def test_should_set_nonce(self, cairo_run):
class TestImplementation:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_implementation", new_implementation=[])

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -172,7 +172,7 @@ class TestJumpdests:
class TestWriteJumpdests:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__write_jumpdests", bytecode=[])

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand Down Expand Up @@ -322,7 +322,7 @@ async def test_should_raise_with_wrong_signed_chain_id(

encoded_unsigned_tx = rlp_encode_signed_data(transaction)

with cairo_error():
with cairo_error(message="Invalid chain id"):
cairo_run(
"test__validate",
address=int(address, 16),
Expand All @@ -343,7 +343,7 @@ async def test_should_raise_with_wrong_address(self, cairo_run, transaction):
encoded_unsigned_tx = rlp_encode_signed_data(transaction)

assert address != int(private_key.public_key.to_address(), 16)
with cairo_error():
with cairo_error("Invalid signature."):
cairo_run(
"test__validate",
address=int(address, 16),
Expand All @@ -364,7 +364,7 @@ async def test_should_raise_with_wrong_nonce(self, cairo_run, transaction):
encoded_unsigned_tx = rlp_encode_signed_data(transaction)

assert address != int(private_key.public_key.to_address(), 16)
with cairo_error():
with cairo_error("Invalid nonce"):
cairo_run(
"test__validate",
address=int(address, 16),
Expand Down
29 changes: 16 additions & 13 deletions tests/src/kakarot/test_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from kakarot_scripts.ef_tests.fetch import EF_TESTS_PARSED_DIR
from tests.utils.constants import TRANSACTION_GAS_LIMIT
from tests.utils.errors import cairo_error
from tests.utils.helpers import felt_to_signed_int
from tests.utils.syscall_handler import SyscallHandler, parse_state

CONTRACT_ADDRESS = 1234
Expand Down Expand Up @@ -74,7 +75,7 @@ class TestKakarot:
class TestNativeToken:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_native_token", address=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -89,7 +90,7 @@ def test_should_set_native_token(self, cairo_run):
class TestTransferOwnership:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__transfer_ownership", new_owner=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -103,7 +104,7 @@ def test_should_transfer_ownership(self, cairo_run):
class TestBaseFee:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_base_fee", base_fee=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -117,7 +118,7 @@ def test_should_set_base_fee(self, cairo_run):
class TestCoinbase:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_coinbase", coinbase=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -131,7 +132,7 @@ def test_should_set_coinbase(self, cairo_run):
class TestPrevRandao:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_prev_randao", prev_randao=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -146,7 +147,7 @@ def test_should_set_prev_randao(self, cairo_run):
class TestBlockGasLimit:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_block_gas_limit", block_gas_limit=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -161,7 +162,7 @@ def test_should_set_block_gas_limit(self, cairo_run):
class TestAccountContractClassHash:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_account_contract_class_hash", class_hash=0xABC)

@SyscallHandler.patch("Ownable_owner", SyscallHandler.caller_address)
Expand All @@ -176,7 +177,7 @@ def test_should_set_account_contract_class_hash(self, cairo_run):
class TestAuthorizedCairoPrecompileCaller:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run(
"test__set_authorized_cairo_precompile_caller",
caller_address=0xABC,
Expand All @@ -203,7 +204,7 @@ def test_should_set_authorized_cairo_precompile_caller(self, cairo_run):
class Cairo1HelpersClass:
@SyscallHandler.patch("Ownable_owner", 0xDEAD)
def test_should_assert_only_owner(self, cairo_run):
with cairo_error():
with cairo_error(message="Ownable: caller is not the owner"):
cairo_run("test__set_cairo1_helpers_class_hash", class_hash=0xABC)

class TestRegisterAccount:
Expand Down Expand Up @@ -242,7 +243,7 @@ def test_register_account_should_fail_existing_entry(
)
mock_caller_address.return_value = starknet_address

with cairo_error():
with cairo_error(message="Kakarot: account already registered"):
cairo_run("test__register_account", evm_address=EVM_ADDRESS)

@SyscallHandler.patch("Kakarot_evm_to_starknet_address", EVM_ADDRESS, 0)
Expand All @@ -253,12 +254,14 @@ def test_register_account_should_fail_existing_entry(
def test_register_account_should_fail_caller_not_resolved_address(
self, mock_caller_address, cairo_run
):
starknet_address = cairo_run(
expected_starknet_address = cairo_run(
"compute_starknet_address", evm_address=EVM_ADDRESS
)
mock_caller_address.return_value = starknet_address // 2
mock_caller_address.return_value = expected_starknet_address // 2

with cairo_error():
with cairo_error(
message=f"Kakarot: Caller should be {felt_to_signed_int(expected_starknet_address)}, got {expected_starknet_address // 2}"
):
cairo_run("test__register_account", evm_address=EVM_ADDRESS)

class TestEthCall:
Expand Down
2 changes: 1 addition & 1 deletion tests/src/utils/test_eth_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async def test_should_panic_on_unsupported_tx_types(
self, cairo_run, transaction
):
encoded_unsigned_tx = rlp_encode_signed_data(transaction)
with cairo_error():
with cairo_error("Kakarot: transaction type not supported"):
cairo_run(
"test__decode",
data=list(encoded_unsigned_tx),
Expand Down
2 changes: 1 addition & 1 deletion tests/utils/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def cairo_error(message=None):
yield e
if message is None:
return
error = re.search(r"Error message: (.*)", e.value.message)[1] # type: ignore
error = re.search(r"Error message: (.*)", e.value.__str__())[1] # type: ignore
assert message == error, f"Expected {message}, got {error}"
finally:
pass
10 changes: 10 additions & 0 deletions tests/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from eth_account.typed_transactions import TypedTransaction
from eth_keys import keys
from eth_utils import decode_hex, keccak, to_checksum_address
from starkware.cairo.lang.cairo_constants import DEFAULT_PRIME
from starkware.starknet.public.abi import get_storage_var_address

from kakarot_scripts.constants import NETWORK
Expand Down Expand Up @@ -253,3 +254,12 @@ def pack_calldata(data: bytes) -> List[int]:
"""

return [len(data), *[int(chunk, 16) for chunk in wrap(data.hex(), 2 * 31)]]


def felt_to_signed_int(value: int) -> int:
"""
Convert a felt value to a signed integer.
"""
if value >= DEFAULT_PRIME // 2:
return value - DEFAULT_PRIME
return value
Loading