Skip to content

Commit 79f1c81

Browse files
committed
testgpu
1 parent ddd20dc commit 79f1c81

File tree

3 files changed

+17
-234
lines changed

3 files changed

+17
-234
lines changed

README.md

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -456,30 +456,3 @@ This project builds upon the excellent work of:
456456

457457
- [Reth](https://github.com/paradigmxyz/reth) - The Rust Ethereum client
458458
- [Evolve](https://ev.xyz/) - The modular rollup framework
459-
460-
### Canonical Block Hash Activation
461-
462-
Legacy deployments allowed application-level hashes to flow through the Engine API, which meant
463-
Reth had to ignore block-hash mismatches and upstream tooling flagged every block as a fork. Newer
464-
networks can opt-in to canonical keccak block hashes by setting `hashRewireActivationHeight` inside
465-
the `evolve` section of the chainspec:
466-
467-
Separately, Ethereum clients expect `stateRoot` to be the current block's post-state root (after
468-
executing this block's transactions on top of the parent). If an integration accidentally supplies
469-
the parent (height-1) root instead, execution clients can report persistent "forks" that are really
470-
just state-root mismatches. ev-reth computes and exposes the canonical post-state root so state-root
471-
validation behaves like standard Ethereum.
472-
473-
```json
474-
"config": {
475-
...,
476-
"evolve": {
477-
"hashRewireActivationHeight": 0
478-
}
479-
}
480-
```
481-
482-
Set the activation height to the first block where canonical hashes should be enforced (use `0` for
483-
fresh networks). Before the activation height the node continues to bypass hash mismatches so
484-
existing chains keep working; after activation, the node rejects malformed payloads and the reported
485-
block hash always matches the standard Engine API expectations.

crates/node/src/config.rs

Lines changed: 0 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,6 @@ struct ChainspecEvolveConfig {
2121
/// Block height at which the custom contract size limit activates.
2222
#[serde(default, rename = "contractSizeLimitActivationHeight")]
2323
pub contract_size_limit_activation_height: Option<u64>,
24-
/// Block height at which canonical block hash enforcement activates.
25-
///
26-
/// # Background
27-
///
28-
/// Early versions of ev-node passed block hashes from height H-1 instead of H
29-
/// when communicating with ev-reth via the Engine API. This caused block hashes
30-
/// to not match the canonical Ethereum block hash (keccak256 of RLP-encoded header),
31-
/// resulting in block explorers like Blockscout incorrectly displaying every block
32-
/// as a fork due to parent hash mismatches.
33-
///
34-
/// # Migration Strategy
35-
///
36-
/// For existing networks with historical blocks containing non-canonical hashes:
37-
/// - Set this to a future block height where the fix will activate
38-
/// - Before the activation height: hash mismatches are bypassed (legacy mode)
39-
/// - At and after the activation height: canonical hash validation is enforced
40-
///
41-
/// This allows nodes to sync from genesis on networks with historical hash
42-
/// mismatches while ensuring new blocks use correct canonical hashes.
43-
///
44-
/// # Default Behavior
45-
///
46-
/// If not set, canonical hash validation is enforced from genesis (block 0).
47-
/// This is the correct setting for new networks without legacy data.
48-
#[serde(default, rename = "canonicalHashActivationHeight")]
49-
pub canonical_hash_activation_height: Option<u64>,
5024
}
5125

5226
/// Configuration for the Evolve payload builder
@@ -70,32 +44,6 @@ pub struct EvolvePayloadBuilderConfig {
7044
/// Block height at which the custom contract size limit activates.
7145
#[serde(default)]
7246
pub contract_size_limit_activation_height: Option<u64>,
73-
/// Block height at which canonical block hash enforcement activates.
74-
///
75-
/// # Background
76-
///
77-
/// Early versions of ev-node passed block hashes from height H-1 instead of H
78-
/// when communicating with ev-reth via the Engine API. This caused block hashes
79-
/// to not match the canonical Ethereum block hash (keccak256 of RLP-encoded header),
80-
/// resulting in block explorers like Blockscout incorrectly displaying every block
81-
/// as a fork due to parent hash mismatches.
82-
///
83-
/// # Migration Strategy
84-
///
85-
/// For existing networks with historical blocks containing non-canonical hashes:
86-
/// - Set this to a future block height where the fix will activate
87-
/// - Before the activation height: hash mismatches are bypassed (legacy mode)
88-
/// - At and after the activation height: canonical hash validation is enforced
89-
///
90-
/// This allows nodes to sync from genesis on networks with historical hash
91-
/// mismatches while ensuring new blocks use correct canonical hashes.
92-
///
93-
/// # Default Behavior
94-
///
95-
/// If not set, canonical hash validation is enforced from genesis (block 0).
96-
/// This is the correct setting for new networks without legacy data.
97-
#[serde(default)]
98-
pub canonical_hash_activation_height: Option<u64>,
9947
}
10048

10149
impl EvolvePayloadBuilderConfig {
@@ -108,7 +56,6 @@ impl EvolvePayloadBuilderConfig {
10856
mint_precompile_activation_height: None,
10957
contract_size_limit: None,
11058
contract_size_limit_activation_height: None,
111-
canonical_hash_activation_height: None,
11259
}
11360
}
11461

@@ -143,7 +90,6 @@ impl EvolvePayloadBuilderConfig {
14390
config.contract_size_limit = extras.contract_size_limit;
14491
config.contract_size_limit_activation_height =
14592
extras.contract_size_limit_activation_height;
146-
config.canonical_hash_activation_height = extras.canonical_hash_activation_height;
14793
}
14894
Ok(config)
14995
}
@@ -197,29 +143,6 @@ impl EvolvePayloadBuilderConfig {
197143
self.base_fee_redirect_settings()
198144
.and_then(|(sink, activation)| (block_number >= activation).then_some(sink))
199145
}
200-
201-
/// Returns true if canonical block hash validation should be enforced for the given block.
202-
///
203-
/// This method controls whether the validator should reject blocks with hash mismatches
204-
/// or bypass the check for legacy compatibility.
205-
///
206-
/// # Returns
207-
///
208-
/// - `true`: Enforce canonical hash validation (reject mismatches)
209-
/// - `false`: Bypass hash validation (legacy mode for historical blocks)
210-
///
211-
/// # Logic
212-
///
213-
/// - If `canonical_hash_activation_height` is `None`: Always enforce (new networks)
214-
/// - If `canonical_hash_activation_height` is `Some(N)`:
215-
/// - `block_number < N`: Don't enforce (legacy mode)
216-
/// - `block_number >= N`: Enforce (canonical mode)
217-
pub const fn is_canonical_hash_enforced(&self, block_number: u64) -> bool {
218-
match self.canonical_hash_activation_height {
219-
Some(activation) => block_number >= activation,
220-
None => true, // Default: enforce canonical hashes from genesis
221-
}
222-
}
223146
}
224147

225148
/// Errors that can occur during configuration validation
@@ -409,7 +332,6 @@ mod tests {
409332
mint_precompile_activation_height: Some(0),
410333
contract_size_limit: None,
411334
contract_size_limit_activation_height: None,
412-
canonical_hash_activation_height: None,
413335
};
414336
assert!(config_with_sink.validate().is_ok());
415337
}
@@ -424,7 +346,6 @@ mod tests {
424346
mint_precompile_activation_height: None,
425347
contract_size_limit: None,
426348
contract_size_limit_activation_height: None,
427-
canonical_hash_activation_height: None,
428349
};
429350

430351
assert_eq!(config.base_fee_sink_for_block(4), None);
@@ -555,29 +476,4 @@ mod tests {
555476
);
556477
}
557478

558-
#[test]
559-
fn test_canonical_hash_activation_from_chainspec() {
560-
let extras = json!({
561-
"canonicalHashActivationHeight": 500
562-
});
563-
564-
let chainspec = create_test_chainspec_with_extras(Some(extras));
565-
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();
566-
567-
assert_eq!(config.canonical_hash_activation_height, Some(500));
568-
// Before activation: legacy mode (not enforced)
569-
assert!(!config.is_canonical_hash_enforced(499));
570-
// At and after activation: canonical mode (enforced)
571-
assert!(config.is_canonical_hash_enforced(500));
572-
assert!(config.is_canonical_hash_enforced(600));
573-
}
574-
575-
#[test]
576-
fn test_canonical_hash_default_enforces_from_genesis() {
577-
// When not configured, canonical hashes should be enforced from genesis
578-
let config = EvolvePayloadBuilderConfig::new();
579-
assert_eq!(config.canonical_hash_activation_height, None);
580-
assert!(config.is_canonical_hash_enforced(0));
581-
assert!(config.is_canonical_hash_enforced(1000));
582-
}
583479
}

crates/node/src/validator.rs

Lines changed: 17 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,24 @@ use reth_ethereum::{
1616
},
1717
};
1818
use reth_ethereum_payload_builder::EthereumExecutionPayloadValidator;
19-
use reth_primitives_traits::{Block as _, RecoveredBlock};
19+
use reth_primitives_traits::RecoveredBlock;
2020
use tracing::info;
2121

22-
use crate::{
23-
attributes::EvolveEnginePayloadAttributes, config::EvolvePayloadBuilderConfig,
24-
node::EvolveEngineTypes,
25-
};
22+
use crate::{attributes::EvolveEnginePayloadAttributes, node::EvolveEngineTypes};
2623

2724
/// Evolve engine validator that handles custom payload validation.
2825
///
29-
/// This validator extends the standard Ethereum payload validation with support for
30-
/// legacy block hash compatibility. See [`EvolvePayloadBuilderConfig::canonical_hash_activation_height`]
31-
/// for details on the migration strategy.
32-
///
33-
/// # Block Hash Validation
34-
///
35-
/// Early versions of ev-node passed block hashes from height H-1 instead of H,
36-
/// causing block explorers (e.g., Blockscout) to show all blocks as forks. This
37-
/// validator can bypass hash mismatch errors for historical blocks while enforcing
38-
/// canonical validation for new blocks, controlled by `canonical_hash_activation_height`.
26+
/// This validator delegates to the standard Ethereum payload validation.
3927
#[derive(Debug, Clone)]
4028
pub struct EvolveEngineValidator {
4129
inner: EthereumExecutionPayloadValidator<ChainSpec>,
42-
config: EvolvePayloadBuilderConfig,
4330
}
4431

4532
impl EvolveEngineValidator {
4633
/// Instantiates a new validator.
47-
pub const fn new(chain_spec: Arc<ChainSpec>, config: EvolvePayloadBuilderConfig) -> Self {
34+
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
4835
Self {
4936
inner: EthereumExecutionPayloadValidator::new(chain_spec),
50-
config,
5137
}
5238
}
5339

@@ -67,56 +53,13 @@ impl PayloadValidator<EvolveEngineTypes> for EvolveEngineValidator {
6753
) -> Result<RecoveredBlock<Self::Block>, NewPayloadError> {
6854
info!("Evolve engine validator: validating payload");
6955

70-
match self.inner.ensure_well_formed_payload(payload.clone()) {
71-
Ok(sealed_block) => {
72-
info!("Evolve engine validator: payload validation succeeded");
73-
sealed_block
74-
.try_recover()
75-
.map_err(|e| NewPayloadError::Other(e.into()))
76-
}
77-
Err(err) => {
78-
tracing::debug!("Evolve payload validation error: {:?}", err);
79-
80-
// Handle block hash mismatch errors specially for legacy compatibility.
81-
//
82-
// Background: Early versions of ev-node passed block hashes from height H-1
83-
// instead of H when communicating with ev-reth via the Engine API. This caused
84-
// block hashes to not match the canonical Ethereum block hash (keccak256 of
85-
// RLP-encoded header), resulting in block explorers like Blockscout incorrectly
86-
// displaying every block as a fork due to parent hash mismatches.
87-
//
88-
// For existing networks with historical blocks containing these non-canonical
89-
// hashes, we need to bypass this validation to allow nodes to sync from genesis.
90-
// The `canonical_hash_activation_height` config controls when to start enforcing
91-
// canonical hashes for new blocks.
92-
if matches!(err, alloy_rpc_types::engine::PayloadError::BlockHash { .. }) {
93-
let block_number = payload.payload.block_number();
94-
95-
// If canonical hash enforcement is active for this block, reject the mismatch
96-
if self.config.is_canonical_hash_enforced(block_number) {
97-
tracing::warn!(
98-
block_number,
99-
"canonical hash enforcement active; rejecting mismatched block hash"
100-
);
101-
return Err(NewPayloadError::Eth(err));
102-
}
103-
104-
// Legacy mode: bypass hash mismatch to allow syncing historical blocks.
105-
// Re-seal the block with the correct canonical hash (keccak256 of header).
106-
info!(
107-
block_number,
108-
"bypassing block hash mismatch (legacy mode before activation height)"
109-
);
110-
let ExecutionData { payload, sidecar } = payload;
111-
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
112-
sealed_block
113-
.try_recover()
114-
.map_err(|e| NewPayloadError::Other(e.into()))
115-
} else {
116-
Err(NewPayloadError::Eth(err))
117-
}
118-
}
119-
}
56+
// Directly delegate to the inner Ethereum validator without any bypass logic.
57+
// This will fail if block hashes don't match, allowing us to see if the error actually occurs.
58+
let sealed_block = self.inner.ensure_well_formed_payload(payload)?;
59+
info!("Evolve engine validator: payload validation succeeded");
60+
sealed_block
61+
.try_recover()
62+
.map_err(|e| NewPayloadError::Other(e.into()))
12063
}
12164

12265
fn validate_payload_attributes_against_header(
@@ -181,8 +124,7 @@ where
181124
type Validator = EvolveEngineValidator;
182125

183126
async fn build(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
184-
let config = EvolvePayloadBuilderConfig::from_chain_spec(ctx.config.chain.as_ref())?;
185-
Ok(EvolveEngineValidator::new(ctx.config.chain.clone(), config))
127+
Ok(EvolveEngineValidator::new(ctx.config.chain.clone()))
186128
}
187129
}
188130

@@ -193,11 +135,9 @@ mod tests {
193135
use reth_chainspec::ChainSpecBuilder;
194136
use reth_primitives::{Block, SealedBlock};
195137

196-
fn validator_with_activation(activation_height: Option<u64>) -> EvolveEngineValidator {
138+
fn create_validator() -> EvolveEngineValidator {
197139
let chain_spec = Arc::new(ChainSpecBuilder::mainnet().build());
198-
let mut config = EvolvePayloadBuilderConfig::new();
199-
config.canonical_hash_activation_height = activation_height;
200-
EvolveEngineValidator::new(chain_spec, config)
140+
EvolveEngineValidator::new(chain_spec)
201141
}
202142

203143
fn mismatched_payload() -> ExecutionData {
@@ -210,35 +150,9 @@ mod tests {
210150
}
211151

212152
#[test]
213-
fn test_legacy_mode_bypasses_hash_mismatch() {
214-
// When activation height is set in the future, legacy mode should bypass hash mismatches
215-
let validator = validator_with_activation(Some(1000));
216-
let payload = mismatched_payload();
217-
218-
validator
219-
.ensure_well_formed_payload(payload)
220-
.expect("hash mismatch should be bypassed in legacy mode");
221-
}
222-
223-
#[test]
224-
fn test_canonical_mode_rejects_hash_mismatch() {
225-
// When activation height is 0 (or in the past), canonical mode should reject mismatches
226-
let validator = validator_with_activation(Some(0));
227-
let payload = mismatched_payload();
228-
229-
let result = validator.ensure_well_formed_payload(payload);
230-
assert!(matches!(
231-
result,
232-
Err(NewPayloadError::Eth(
233-
alloy_rpc_types::engine::PayloadError::BlockHash { .. }
234-
))
235-
));
236-
}
237-
238-
#[test]
239-
fn test_default_enforces_canonical_hash() {
240-
// When no activation height is set, canonical validation should be enforced (default)
241-
let validator = validator_with_activation(None);
153+
fn test_hash_mismatch_is_rejected() {
154+
// Hash mismatches should be rejected
155+
let validator = create_validator();
242156
let payload = mismatched_payload();
243157

244158
let result = validator.ensure_well_formed_payload(payload);

0 commit comments

Comments
 (0)