Skip to content

Commit

Permalink
feat: support catching EntryPointNotFound
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyalesokhin-starkware committed Oct 14, 2024
1 parent 014fea0 commit 19c936e
Show file tree
Hide file tree
Showing 9 changed files with 873 additions and 712 deletions.
1,497 changes: 806 additions & 691 deletions crates/blockifier/feature_contracts/cairo1/compiled/test_contract.casm.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ mod TestContract {
syscalls::replace_class_syscall(class_hash).unwrap_syscall();
syscalls::send_message_to_l1_syscall(17.try_into().unwrap(), dummy_span).unwrap_syscall();
self.my_storage_var.write(17);
panic!("test_revert_helper");
panic(array!['test_revert_helper']);
}

#[external(v0)]
Expand Down Expand Up @@ -626,6 +626,12 @@ mod TestContract {
*error_span.pop_back().unwrap() == 'ENTRYPOINT_FAILED',
'Unexpected error',
);
let inner_error = *error_span.pop_back().unwrap();
if entry_point_selector == selector!("bad_selector") {
assert(inner_error == 'ENTRYPOINT_NOT_FOUND', 'Unexpected error');
} else {
assert(inner_error == 'test_revert_helper', 'Unexpected error');
}
},
};
// TODO(Yoni, 1/12/2024): test replace class once get_class_hash_at syscall is supported.
Expand Down
15 changes: 5 additions & 10 deletions crates/blockifier/src/execution/entry_point_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use cairo_vm::types::builtin_name::BuiltinName;
use num_bigint::BigInt;
use pretty_assertions::assert_eq;
use starknet_api::core::{EntryPointSelector, PatriciaKey};
use starknet_api::execution_utils::format_panic_data;
use starknet_api::transaction::{Calldata, Fee};
use starknet_api::{calldata, felt, storage_key};

Expand Down Expand Up @@ -164,10 +165,11 @@ fn test_entry_point_not_found_in_contract() {
let entry_point_selector = EntryPointSelector(felt!(2_u8));
let entry_point_call =
CallEntryPoint { entry_point_selector, ..trivial_external_entry_point_new(test_contract) };
let error = entry_point_call.execute_directly(&mut state).unwrap_err();
let call_info = entry_point_call.execute_directly(&mut state).unwrap();
assert!(call_info.execution.failed);
assert_eq!(
format!("Entry point {entry_point_selector:?} not found in contract."),
format!("{error}")
format_panic_data(&call_info.execution.retdata.0),
"0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND')"
);
}

Expand Down Expand Up @@ -396,13 +398,6 @@ fn test_syscall_execution_security_failures() {
"test_bad_syscall_request_arg_type",
calldata![],
);
run_security_test(
state,
security_contract,
"Entry point EntryPointSelector(0x19) not found in contract",
"test_bad_call_selector",
calldata![],
);
run_security_test(
state,
security_contract,
Expand Down
31 changes: 27 additions & 4 deletions crates/blockifier/src/execution/execution_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ use starknet_api::deprecated_contract_class::Program as DeprecatedProgram;
use starknet_api::transaction::Calldata;
use starknet_types_core::felt::Felt;

use super::call_info::CallExecution;
use super::entry_point::ConstructorEntryPointExecutionResult;
use super::errors::ConstructorEntryPointExecutionError;
use super::errors::{
ConstructorEntryPointExecutionError,
EntryPointExecutionError,
PreExecutionError,
};
use super::syscalls::hint_processor::ENTRYPOINT_NOT_FOUND_ERROR;
use crate::execution::call_info::{CallInfo, Retdata};
use crate::execution::contract_class::{ContractClass, TrackedResource};
use crate::execution::entry_point::{
Expand Down Expand Up @@ -62,9 +68,26 @@ pub fn execute_entry_point_call_wrapper(
context.tracked_resource_stack.push(tracked_resource);
}

let res = execute_entry_point_call(call, contract_class, state, resources, context);
context.tracked_resource_stack.pop();
res
let orig_call = call.clone();
match execute_entry_point_call(call, contract_class, state, resources, context) {
Err(EntryPointExecutionError::PreExecutionError(
PreExecutionError::EntryPointNotFound(_),
)) if context.versioned_constants().enable_reverts => Ok(CallInfo {
call: orig_call,
execution: CallExecution {
retdata: Retdata(vec![Felt::from_hex(ENTRYPOINT_NOT_FOUND_ERROR).unwrap()]),
failed: true,
gas_consumed: 0,
..CallExecution::default()
},
tracked_resource: context.tracked_resource_stack.pop().unwrap(),
..CallInfo::default()
}),
res => {
context.tracked_resource_stack.pop();
res
}
}
}

/// Executes a specific call to a contract entry point and returns its output.
Expand Down
3 changes: 3 additions & 0 deletions crates/blockifier/src/execution/syscalls/hint_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ pub const OUT_OF_GAS_ERROR: &str =
// "Block number out of range";
pub const BLOCK_NUMBER_OUT_OF_RANGE_ERROR: &str =
"0x00000000000000426c6f636b206e756d626572206f7574206f662072616e6765";
// "ENTRYPOINT_NOT_FOUND";
pub const ENTRYPOINT_NOT_FOUND_ERROR: &str =
"0x000000000000000000000000454e545259504f494e545f4e4f545f464f554e44";
// "ENTRYPOINT_FAILED";
pub const ENTRYPOINT_FAILED_ERROR: &str =
"0x000000000000000000000000000000454e545259504f494e545f4641494c4544";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ fn test_call_contract_that_panics() {
};
// The inner call should have failed.
assert!(inner_call.execution.failed);
assert_eq!(format_panic_data(&inner_call.execution.retdata.0), "\"test_revert_helper\"");
assert_eq!(
format_panic_data(&inner_call.execution.retdata.0),
"0x746573745f7265766572745f68656c706572 ('test_revert_helper')"
);
assert!(inner_call.execution.events.is_empty());
assert!(inner_call.execution.l2_to_l1_messages.is_empty());
assert_eq!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1549,12 +1549,11 @@ fn test_revert_in_execute(
}

#[rstest]
#[case(true)]
#[case(false)]
fn test_call_contract_that_panics(
mut block_context: BlockContext,
max_l1_resource_bounds: ValidResourceBounds,
#[case] enable_reverts: bool,
#[values(true, false)] enable_reverts: bool,
#[values("test_revert_helper", "bad_selector")] inner_selector: &str,
) {
// Override enable reverts.
block_context.versioned_constants.enable_reverts = enable_reverts;
Expand All @@ -1570,7 +1569,7 @@ fn test_call_contract_that_panics(

let calldata = [
*FeatureContract::TestContract(CairoVersion::Cairo1).get_instance_address(0).0.key(),
selector_from_name("test_revert_helper").0,
selector_from_name(inner_selector).0,
felt!(1_u8),
new_class_hash.0,
];
Expand Down
7 changes: 7 additions & 0 deletions crates/papyrus_rpc/src/v0_8/api/api_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use papyrus_storage::state::StateStorageReader;
use papyrus_storage::{StorageError, StorageReader, StorageTxn};
use starknet_api::block::{BlockHash, BlockHeaderWithoutHash, BlockNumber, BlockStatus};
use starknet_api::core::{ChainId, ClassHash, ContractAddress, GlobalRoot, Nonce};
use starknet_api::execution_utils::format_panic_data;
use starknet_api::hash::StarkHash;
use starknet_api::state::{StateNumber, StorageKey};
use starknet_api::transaction::{
Expand Down Expand Up @@ -901,6 +902,12 @@ impl JsonRpcServer for JsonRpcServerImpl {
.map_err(internal_server_error)?
.map_err(execution_error_to_error_object_owned)?;

if res.failed {
let contract_err = ContractError { revert_error: format_panic_data(&res.retdata.0) };
let rpc_err: JsonRpcError<ContractError> = contract_err.into();
return Err(rpc_err.into());
}

block_not_reverted_validator.validate(&self.storage_reader)?;

Ok(res.retdata.0)
Expand Down
12 changes: 11 additions & 1 deletion crates/papyrus_rpc/src/v0_8/execution_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,17 @@ async fn execution_call() {
.unwrap_err();

const CONTRACT_ERROR_CODE: i32 = 40;
assert_matches!(err, Error::Call(err) if err.code() == CONTRACT_ERROR_CODE);

match err {
Error::Call(err) => {
assert_eq!(err.code(), CONTRACT_ERROR_CODE);
assert_eq!(
err.data().unwrap().get(),
r##"{"revert_error":"0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND')"}"##
);
}
_ => panic!("Expected Error::Call"),
}

// Test that the block context is passed correctly to blockifier.
let mut calldata = get_calldata_for_test_execution_info(
Expand Down

0 comments on commit 19c936e

Please sign in to comment.