Skip to content

Commit bf143ab

Browse files
authored
Fix fork error handling (#1571)
Closes #1497 ## Introduced changes <!-- A brief description of the changes --> - Fixes incosistencies in fork state behavior when errors should be raised TODO: - [x] Fix gas calculation (is crashing right now in forking) ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md`
1 parent d5c64c6 commit bf143ab

File tree

5 files changed

+62
-68
lines changed

5 files changed

+62
-68
lines changed

crates/cheatnet/src/forking/state.rs

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use runtime::starknet::context::BlockInfo;
1313
use sierra_casm::compile;
1414
use starknet::core::types::{
1515
BlockId, ContractClass as ContractClassStarknet, FieldElement, MaybePendingBlockWithTxHashes,
16+
StarknetError,
1617
};
1718
use starknet::providers::jsonrpc::HttpTransport;
1819
use starknet::providers::{JsonRpcClient, Provider, ProviderError};
@@ -53,6 +54,19 @@ impl ForkStateReader {
5354
}
5455
}
5556

57+
#[macro_export]
58+
macro_rules! other_provider_error {
59+
( $boxed:expr ) => {{
60+
let err_str = $boxed.deref().to_string();
61+
if err_str.contains("error sending request for url") {
62+
return Err(StateReadError(
63+
"Unable to reach the node. Check your internet connection and node url".to_string(),
64+
));
65+
}
66+
Err(StateReadError(format!("JsonRpc provider error: {err_str}")))
67+
}};
68+
}
69+
5670
impl BlockInfoReader for ForkStateReader {
5771
fn get_block_info(&mut self) -> StateResult<BlockInfo> {
5872
if let Some(cache_hit) = self.cache.get_block_info() {
@@ -77,30 +91,14 @@ impl BlockInfoReader for ForkStateReader {
7791
Ok(MaybePendingBlockWithTxHashes::PendingBlock(_)) => {
7892
unreachable!("Pending block is not be allowed at the configuration level")
7993
}
94+
Err(ProviderError::Other(boxed)) => other_provider_error!(boxed),
8095
Err(err) => Err(StateReadError(format!(
81-
"Unable to get block with tx hashes from fork, err: {err:?}"
96+
"Unable to get block with tx hashes from fork ({err})"
8297
))),
8398
}
8499
}
85100
}
86101

87-
#[macro_export]
88-
macro_rules! other_provider_error {
89-
( $boxed:expr ) => {{
90-
let err_str = $boxed.deref().to_string();
91-
if err_str.contains("error sending request for url") {
92-
return node_connection_error();
93-
}
94-
Err(StateReadError(format!("JsonRpc provider error: {err_str}")))
95-
}};
96-
}
97-
98-
fn node_connection_error<T>() -> StateResult<T> {
99-
Err(StateReadError(
100-
"Unable to reach the node. Check your internet connection and node url".to_string(),
101-
))
102-
}
103-
104102
impl StateReader for ForkStateReader {
105103
fn get_storage_at(
106104
&mut self,
@@ -123,8 +121,9 @@ impl StateReader for ForkStateReader {
123121
Ok(value_sf)
124122
}
125123
Err(ProviderError::Other(boxed)) => other_provider_error!(boxed),
126-
Err(_) => Err(StateReadError(format!(
127-
"Unable to get storage at address: {contract_address:?} and key: {key:?} from fork"
124+
Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => Ok(Default::default()),
125+
Err(x) => Err(StateReadError(format!(
126+
"Unable to get storage at address: {contract_address:?} and key: {key:?} from fork ({x})"
128127
))),
129128
}
130129
}
@@ -144,8 +143,11 @@ impl StateReader for ForkStateReader {
144143
Ok(nonce)
145144
}
146145
Err(ProviderError::Other(boxed)) => other_provider_error!(boxed),
147-
Err(_) => Err(StateReadError(format!(
148-
"Unable to get nonce at {contract_address:?} from fork"
146+
Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => {
147+
Ok(Default::default())
148+
}
149+
Err(x) => Err(StateReadError(format!(
150+
"Unable to get nonce at {contract_address:?} from fork ({x})"
149151
))),
150152
}
151153
}
@@ -165,9 +167,12 @@ impl StateReader for ForkStateReader {
165167
.cache_get_class_hash_at(contract_address, class_hash);
166168
Ok(class_hash)
167169
}
170+
Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => {
171+
Ok(Default::default())
172+
}
168173
Err(ProviderError::Other(boxed)) => other_provider_error!(boxed),
169-
Err(_) => Err(StateReadError(format!(
170-
"Unable to get class hash at {contract_address:?} from fork"
174+
Err(x) => Err(StateReadError(format!(
175+
"Unable to get class hash at {contract_address:?} from fork ({x})"
171176
))),
172177
}
173178
}
@@ -190,8 +195,13 @@ impl StateReader for ForkStateReader {
190195

191196
Ok(contract_class)
192197
}
198+
Err(ProviderError::StarknetError(StarknetError::ClassHashNotFound)) => {
199+
Err(UndeclaredClassHash(*class_hash))
200+
}
193201
Err(ProviderError::Other(boxed)) => other_provider_error!(boxed),
194-
Err(_) => Err(UndeclaredClassHash(*class_hash)),
202+
Err(x) => Err(StateReadError(format!(
203+
"Unable to get compiled class at {class_hash} from fork ({x})"
204+
))),
195205
}
196206
};
197207

crates/cheatnet/src/state.rs

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ use blockifier::execution::entry_point::CallEntryPoint;
88
use blockifier::state::state_api::State;
99
use blockifier::{
1010
execution::contract_class::ContractClass,
11-
state::{
12-
errors::StateError,
13-
state_api::{StateReader, StateResult},
14-
},
11+
state::state_api::{StateReader, StateResult},
1512
};
1613
use cairo_felt::Felt252;
1714
use runtime::starknet::context::BlockInfo;
@@ -20,6 +17,7 @@ use runtime::starknet::state::DictStateReader;
2017
use starknet_api::core::EntryPointSelector;
2118

2219
use crate::constants::build_test_entry_point;
20+
use blockifier::state::errors::StateError::UndeclaredClassHash;
2321
use starknet_api::transaction::ContractAddressSalt;
2422
use starknet_api::{
2523
core::{ClassHash, CompiledClassHash, ContractAddress, Nonce},
@@ -82,8 +80,8 @@ impl StateReader for ExtendedStateReader {
8280
.or_else(|_| {
8381
self.fork_state_reader
8482
.as_mut()
85-
.map_or(Ok(StarkFelt::default()), |reader| {
86-
match_node_response(reader.get_storage_at(contract_address, key))
83+
.map_or(Ok(Default::default()), {
84+
|reader| reader.get_storage_at(contract_address, key)
8785
})
8886
})
8987
}
@@ -94,8 +92,8 @@ impl StateReader for ExtendedStateReader {
9492
.or_else(|_| {
9593
self.fork_state_reader
9694
.as_mut()
97-
.map_or(Ok(Nonce::default()), |reader| {
98-
match_node_response(reader.get_nonce_at(contract_address))
95+
.map_or(Ok(Default::default()), {
96+
|reader| reader.get_nonce_at(contract_address)
9997
})
10098
})
10199
}
@@ -106,8 +104,8 @@ impl StateReader for ExtendedStateReader {
106104
.or_else(|_| {
107105
self.fork_state_reader
108106
.as_mut()
109-
.map_or(Ok(ClassHash::default()), |reader| {
110-
match_node_response(reader.get_class_hash_at(contract_address))
107+
.map_or(Ok(Default::default()), {
108+
|reader| reader.get_class_hash_at(contract_address)
111109
})
112110
})
113111
}
@@ -119,10 +117,11 @@ impl StateReader for ExtendedStateReader {
119117
self.dict_state_reader
120118
.get_compiled_contract_class(class_hash)
121119
.or_else(|_| {
122-
self.fork_state_reader.as_mut().map_or(
123-
Err(StateError::UndeclaredClassHash(*class_hash)),
124-
|reader| reader.get_compiled_contract_class(class_hash),
125-
)
120+
self.fork_state_reader
121+
.as_mut()
122+
.map_or(Err(UndeclaredClassHash(*class_hash)), |reader| {
123+
reader.get_compiled_contract_class(class_hash)
124+
})
126125
})
127126
}
128127

@@ -317,16 +316,6 @@ impl TraceData {
317316
}
318317
}
319318

320-
fn match_node_response<T: Default>(result: StateResult<T>) -> StateResult<T> {
321-
match result {
322-
Ok(class_hash) => Ok(class_hash),
323-
Err(StateError::StateReadError(msg)) if msg.contains("node") => {
324-
Err(StateError::StateReadError(msg))
325-
}
326-
_ => Ok(Default::default()),
327-
}
328-
}
329-
330319
fn get_cheat_for_contract<T: Clone>(
331320
global_cheat: &Option<T>,
332321
contract_cheats: &HashMap<ContractAddress, CheatStatus<T>>,

crates/forge-runner/src/gas.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use blockifier::fee::gas_usage::calculate_tx_gas_usage;
66
use blockifier::fee::os_resources::OS_RESOURCES;
77
use blockifier::fee::os_usage::get_additional_os_resources;
88
use blockifier::state::cached_state::{CachedState, StateChangesCount};
9+
use blockifier::state::errors::StateError;
910
use blockifier::transaction::transaction_types::TransactionType;
1011
use blockifier::{
1112
abi::constants, block_context::BlockContext, execution::entry_point::ExecutionResources,
@@ -15,19 +16,16 @@ use cairo_vm::vm::runners::cairo_runner::ExecutionResources as VmExecutionResour
1516
use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources;
1617
use cheatnet::state::ExtendedStateReader;
1718

18-
#[must_use]
1919
pub fn calculate_used_gas(
2020
block_context: &BlockContext,
2121
state: &mut CachedState<ExtendedStateReader>,
2222
resources: &UsedResources,
23-
) -> u128 {
23+
) -> Result<u128, StateError> {
2424
let total_vm_usage = get_total_vm_usage(&resources.execution_resources);
25-
let mut state_changes = state
26-
.get_actual_state_changes_for_fee_charge(
27-
block_context.fee_token_addresses.eth_fee_token_address,
28-
None,
29-
)
30-
.unwrap();
25+
let mut state_changes = state.get_actual_state_changes_for_fee_charge(
26+
block_context.fee_token_addresses.eth_fee_token_address,
27+
None,
28+
)?;
3129
// compiled_class_hash_updates is used only for keeping track of declares
3230
// which we don't want to include in gas cost
3331
state_changes.compiled_class_hash_updates.clear();
@@ -40,8 +38,8 @@ pub fn calculate_used_gas(
4038

4139
let resource_mapping = used_resources_to_resource_mapping(&total_vm_usage, l1_gas_usage);
4240

43-
calculate_tx_l1_gas_usage(&resource_mapping, block_context)
44-
.expect("Calculating gas failed, some resources were not included.")
41+
Ok(calculate_tx_l1_gas_usage(&resource_mapping, block_context)
42+
.expect("Calculating gas failed, some resources were not included."))
4543
}
4644

4745
#[must_use]

crates/forge-runner/src/running.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ pub fn run_test_case(
268268
let call_trace_ref = get_call_trace_ref(&mut forge_runtime);
269269
let execution_resources = get_all_execution_resources(forge_runtime);
270270

271-
let gas = calculate_used_gas(&block_context, &mut blockifier_state, &execution_resources);
271+
let gas = calculate_used_gas(&block_context, &mut blockifier_state, &execution_resources)?;
272272

273273
Ok(RunResultWithInfo {
274274
run_result,
@@ -305,7 +305,7 @@ fn extract_test_case_summary(
305305
Err(err) => bail!(err),
306306
}
307307
}
308-
// `ForkStateReader.get_block_info`, `get_fork_state_reader` may return an error
308+
// `ForkStateReader.get_block_info`, `get_fork_state_reader, `calculate_used_gas` may return an error
309309
// `available_gas` may be specified with Scarb ~2.4
310310
Err(error) => Ok(TestCaseSummary::Failed {
311311
name: case.name.clone(),

crates/forge/src/block_number_map.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use cairo_felt::Felt252;
44
use conversions::IntoConv;
55
use forge_runner::compiled_runnable::ValidatedForkConfig;
66
use num_bigint::BigInt;
7-
use starknet::core::types::BlockTag::Latest;
87
use starknet::core::types::{BlockId, MaybePendingBlockWithTxHashes};
98
use starknet::providers::jsonrpc::HttpTransport;
109
use starknet::providers::{JsonRpcClient, Provider};
@@ -83,13 +82,11 @@ impl BlockNumberMap {
8382
async fn get_latest_block_number(url: &Url) -> Result<BlockNumber> {
8483
let client = JsonRpcClient::new(HttpTransport::new(url.clone()));
8584

86-
match Handle::current()
87-
.spawn(async move { client.get_block_with_tx_hashes(BlockId::Tag(Latest)).await })
85+
Handle::current()
86+
.spawn(async move { client.block_number().await })
8887
.await?
89-
{
90-
Ok(MaybePendingBlockWithTxHashes::Block(block)) => Ok(BlockNumber(block.block_number)),
91-
_ => Err(anyhow!("Could not get the latest block number".to_string())),
92-
}
88+
.map(BlockNumber)
89+
.map_err(|x| anyhow!(x.to_string()))
9390
}
9491

9592
async fn get_block_number_from_hash(url: &Url, block_hash: &Felt252) -> Result<BlockNumber> {

0 commit comments

Comments
 (0)