Skip to content

Commit 1014c9e

Browse files
blockifier_reexecution: move compare_block_hash to utils (#13909)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f887c41 commit 1014c9e

2 files changed

Lines changed: 72 additions & 69 deletions

File tree

crates/blockifier_reexecution/src/rpc_replay.rs

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,18 @@ use blockifier::blockifier::config::{
99
ContractClassManagerConfig,
1010
};
1111
use blockifier::context::ChainInfo;
12-
use blockifier::state::cached_state::CommitmentStateDiff;
1312
use blockifier::state::contract_class_manager::ContractClassManager;
14-
use starknet_api::block::{BlockInfo, BlockNumber, StarknetVersion};
15-
use starknet_api::block_hash::block_hash_calculator::{
16-
calculate_block_commitments,
17-
calculate_block_hash,
18-
PartialBlockHashComponents,
19-
TransactionHashingData,
20-
};
13+
use starknet_api::block::BlockNumber;
14+
#[cfg(feature = "cairo_native")]
15+
use starknet_api::block_hash::block_hash_calculator::TransactionHashingData;
2116
#[cfg(feature = "cairo_native")]
2217
use starknet_api::contract_class::SierraVersion;
2318

2419
use crate::errors::{RPCStateReaderError, ReexecutionError, ReexecutionResult};
2520
use crate::state_reader::config::RpcStateReaderConfig;
2621
use crate::state_reader::reexecution_state_reader::{BlockReexecutor, ReexecuteBlockOutcome};
27-
use crate::state_reader::rpc_objects::BlockHeader;
2822
use crate::state_reader::rpc_state_reader::RpcBlockReexecutor;
29-
use crate::utils::{compare_state_diffs, get_chain_info};
30-
// Block hash comparison is only valid for Starknet v0.14.0 and later.
31-
const MIN_VERSION_FOR_BLOCK_HASH_COMPARISON: &str = "0.14.0";
23+
use crate::utils::{compare_block_hash, compare_state_diffs, get_chain_info};
3224

3325
struct ReplayCounters {
3426
matched: AtomicU64,
@@ -351,62 +343,6 @@ fn compare_tx_hashing_data(
351343
false
352344
}
353345

354-
/// Computes the block hash from the reexecution output and compares it against the expected hash
355-
/// from the chain. Returns `true` if they match, or if the block predates v0.14.0 (skipped).
356-
///
357-
/// Uses the state root from the RPC block header (`new_root`) since the blockifier does not
358-
/// compute state roots. If the state diff already matched, the state root should also match.
359-
///
360-
/// Note: Blocks before v0.14.0 may include deprecated (Cairo 0) declared classes which are not
361-
/// represented in [`CommitmentStateDiff`]; those blocks skip hash comparison below.
362-
async fn compare_block_hash(
363-
txs_hashing_data: Vec<TransactionHashingData>,
364-
actual_state_diff: CommitmentStateDiff,
365-
block_header: &BlockHeader,
366-
block_number: BlockNumber,
367-
) -> ReexecutionResult<bool> {
368-
let starknet_version: StarknetVersion = block_header.starknet_version.clone().try_into()?;
369-
370-
let min_version: StarknetVersion =
371-
MIN_VERSION_FOR_BLOCK_HASH_COMPARISON.try_into().expect("Invalid min version constant.");
372-
if starknet_version < min_version {
373-
tracing::debug!(
374-
"Block {block_number}: skipping block hash comparison (version {} < {}).",
375-
block_header.starknet_version,
376-
MIN_VERSION_FOR_BLOCK_HASH_COMPARISON
377-
);
378-
return Ok(true);
379-
}
380-
381-
let (commitments, _measurements) = calculate_block_commitments(
382-
&txs_hashing_data,
383-
actual_state_diff.into(),
384-
block_header.l1_da_mode,
385-
&starknet_version,
386-
)
387-
.await;
388-
389-
let block_info: BlockInfo = block_header.clone().try_into()?;
390-
let partial_block_hash_components = PartialBlockHashComponents::new(&block_info, commitments);
391-
392-
let computed_hash = calculate_block_hash(
393-
&partial_block_hash_components,
394-
block_header.new_root,
395-
block_header.parent_hash,
396-
)?;
397-
398-
if computed_hash == block_header.block_hash {
399-
Ok(true)
400-
} else {
401-
tracing::warn!(
402-
"Block hash mismatch for block {block_number}.\n expected: {}\n actual: {}",
403-
block_header.block_hash,
404-
computed_hash,
405-
);
406-
Ok(false)
407-
}
408-
}
409-
410346
fn is_block_not_found(err: &ReexecutionError) -> bool {
411347
matches!(err, ReexecutionError::Rpc(RPCStateReaderError::BlockNotFound(_)))
412348
}

crates/blockifier_reexecution/src/utils.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,21 @@ use blockifier::state::global_cache::CompiledClasses;
1010
use blockifier::state::state_api::StateResult;
1111
use indexmap::IndexMap;
1212
use pretty_assertions::assert_eq;
13-
use starknet_api::block::BlockNumber;
13+
use starknet_api::block::{BlockInfo, BlockNumber, StarknetVersion};
14+
use starknet_api::block_hash::block_hash_calculator::{
15+
calculate_block_commitments,
16+
calculate_block_hash,
17+
PartialBlockHashComponents,
18+
TransactionHashingData,
19+
};
1420
use starknet_api::contract_class::ContractClass;
1521
use starknet_api::core::{ChainId, ClassHash, CompiledClassHash, ContractAddress, Nonce};
1622
use starknet_api::state::{SierraContractClass, StorageKey};
1723
use starknet_types_core::felt::Felt;
1824

25+
use crate::errors::ReexecutionResult;
1926
use crate::state_reader::config::RpcStateReaderConfig;
27+
use crate::state_reader::rpc_objects::BlockHeader;
2028

2129
pub static RPC_NODE_URL: LazyLock<String> = LazyLock::new(|| {
2230
env::var("TEST_URL")
@@ -187,6 +195,65 @@ pub fn compare_state_diffs(
187195
is_match
188196
}
189197

198+
// Block hash comparison is only valid for Starknet v0.14.0 and later.
199+
const MIN_VERSION_FOR_BLOCK_HASH_COMPARISON: &str = "0.14.0";
200+
201+
/// Computes the block hash from the reexecution output and compares it against the expected hash
202+
/// from the chain. Returns `true` if they match, or if the block predates v0.14.0 (skipped).
203+
///
204+
/// Uses the state root from the RPC block header (`new_root`) since the blockifier does not
205+
/// compute state roots. If the state diff already matched, the state root should also match.
206+
///
207+
/// Note: Blocks before v0.14.0 may include deprecated (Cairo 0) declared classes which are not
208+
/// represented in [`CommitmentStateDiff`]; those blocks skip hash comparison below.
209+
pub async fn compare_block_hash(
210+
txs_hashing_data: Vec<TransactionHashingData>,
211+
actual_state_diff: CommitmentStateDiff,
212+
block_header: &BlockHeader,
213+
block_number: BlockNumber,
214+
) -> ReexecutionResult<bool> {
215+
let starknet_version: StarknetVersion = block_header.starknet_version.clone().try_into()?;
216+
217+
let min_version: StarknetVersion =
218+
MIN_VERSION_FOR_BLOCK_HASH_COMPARISON.try_into().expect("Invalid min version constant.");
219+
if starknet_version < min_version {
220+
tracing::debug!(
221+
"Block {block_number}: skipping block hash comparison (version {} < {}).",
222+
block_header.starknet_version,
223+
MIN_VERSION_FOR_BLOCK_HASH_COMPARISON
224+
);
225+
return Ok(true);
226+
}
227+
228+
let (commitments, _measurements) = calculate_block_commitments(
229+
&txs_hashing_data,
230+
actual_state_diff.into(),
231+
block_header.l1_da_mode,
232+
&starknet_version,
233+
)
234+
.await;
235+
236+
let block_info: BlockInfo = block_header.clone().try_into()?;
237+
let partial_block_hash_components = PartialBlockHashComponents::new(&block_info, commitments);
238+
239+
let computed_hash = calculate_block_hash(
240+
&partial_block_hash_components,
241+
block_header.new_root,
242+
block_header.parent_hash,
243+
)?;
244+
245+
if computed_hash == block_header.block_hash {
246+
Ok(true)
247+
} else {
248+
tracing::warn!(
249+
"Block hash mismatch for block {block_number}.\n expected: {}\n actual: {}",
250+
block_header.block_hash,
251+
computed_hash,
252+
);
253+
Ok(false)
254+
}
255+
}
256+
190257
/// Asserts equality between two `CommitmentStateDiff` structs, ignoring insertion order.
191258
#[macro_export]
192259
macro_rules! assert_eq_state_diff {

0 commit comments

Comments
 (0)