mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(engine): validate execution requests (#13685)
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -8519,6 +8519,7 @@ dependencies = [
|
|||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-rpc-types-engine",
|
"alloy-rpc-types-engine",
|
||||||
|
"assert_matches",
|
||||||
"op-alloy-rpc-types-engine",
|
"op-alloy-rpc-types-engine",
|
||||||
"reth-chain-state",
|
"reth-chain-state",
|
||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
|
|||||||
@ -31,5 +31,8 @@ serde.workspace = true
|
|||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tokio = { workspace = true, default-features = false, features = ["sync"] }
|
tokio = { workspace = true, default-features = false, features = ["sync"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_matches.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
op = ["dep:op-alloy-rpc-types-engine"]
|
op = ["dep:op-alloy-rpc-types-engine"]
|
||||||
@ -8,6 +8,9 @@
|
|||||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||||
|
|
||||||
|
use alloy_primitives::Bytes;
|
||||||
|
use reth_chainspec::EthereumHardforks;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
pub use error::{
|
pub use error::{
|
||||||
EngineObjectValidationError, InvalidPayloadAttributesError, PayloadBuilderError,
|
EngineObjectValidationError, InvalidPayloadAttributesError, PayloadBuilderError,
|
||||||
@ -24,7 +27,6 @@ pub use traits::{
|
|||||||
mod payload;
|
mod payload;
|
||||||
pub use payload::PayloadOrAttributes;
|
pub use payload::PayloadOrAttributes;
|
||||||
|
|
||||||
use reth_chainspec::EthereumHardforks;
|
|
||||||
/// The types that are used by the engine API.
|
/// The types that are used by the engine API.
|
||||||
pub trait PayloadTypes: Send + Sync + Unpin + core::fmt::Debug + Clone + 'static {
|
pub trait PayloadTypes: Send + Sync + Unpin + core::fmt::Debug + Clone + 'static {
|
||||||
/// The built payload type.
|
/// The built payload type.
|
||||||
@ -363,12 +365,85 @@ pub enum PayloadKind {
|
|||||||
WaitForPending,
|
WaitForPending,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates that execution requests are valid according to Engine API specification.
|
||||||
|
///
|
||||||
|
/// `executionRequests`: `Array of DATA` - List of execution layer triggered requests. Each list
|
||||||
|
/// element is a `requests` byte array as defined by [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685).
|
||||||
|
/// The first byte of each element is the `request_type` and the remaining bytes are the
|
||||||
|
/// `request_data`. Elements of the list **MUST** be ordered by `request_type` in ascending order.
|
||||||
|
/// Elements with empty `request_data` **MUST** be excluded from the list. If any element is out of
|
||||||
|
/// order or has a length of 1-byte or shorter, client software **MUST** return `-32602: Invalid
|
||||||
|
/// params` error.
|
||||||
|
pub fn validate_execution_requests(requests: &[Bytes]) -> Result<(), EngineObjectValidationError> {
|
||||||
|
let mut last_request_type = None;
|
||||||
|
for request in requests {
|
||||||
|
if request.len() <= 1 {
|
||||||
|
return Err(EngineObjectValidationError::InvalidParams(
|
||||||
|
"empty execution request".to_string().into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_type = request[0];
|
||||||
|
if Some(request_type) < last_request_type {
|
||||||
|
return Err(EngineObjectValidationError::InvalidParams(
|
||||||
|
"execution requests out of order".to_string().into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
last_request_type = Some(request_type);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn version_ord() {
|
fn version_ord() {
|
||||||
assert!(EngineApiMessageVersion::V4 > EngineApiMessageVersion::V3);
|
assert!(EngineApiMessageVersion::V4 > EngineApiMessageVersion::V3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn execution_requests_validation() {
|
||||||
|
assert_matches!(validate_execution_requests(&[]), Ok(()));
|
||||||
|
|
||||||
|
let valid_requests = [
|
||||||
|
Bytes::from_iter([1, 2]),
|
||||||
|
Bytes::from_iter([2, 3]),
|
||||||
|
Bytes::from_iter([3, 4]),
|
||||||
|
Bytes::from_iter([4, 5]),
|
||||||
|
];
|
||||||
|
assert_matches!(validate_execution_requests(&valid_requests), Ok(()));
|
||||||
|
|
||||||
|
let requests_with_empty = [
|
||||||
|
Bytes::from_iter([1, 2]),
|
||||||
|
Bytes::from_iter([2, 3]),
|
||||||
|
Bytes::new(),
|
||||||
|
Bytes::from_iter([3, 4]),
|
||||||
|
];
|
||||||
|
assert_matches!(
|
||||||
|
validate_execution_requests(&requests_with_empty),
|
||||||
|
Err(EngineObjectValidationError::InvalidParams(_))
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut requests_valid_reversed = valid_requests;
|
||||||
|
requests_valid_reversed.reverse();
|
||||||
|
assert_matches!(
|
||||||
|
validate_execution_requests(&requests_with_empty),
|
||||||
|
Err(EngineObjectValidationError::InvalidParams(_))
|
||||||
|
);
|
||||||
|
|
||||||
|
let requests_out_of_order = [
|
||||||
|
Bytes::from_iter([1, 2]),
|
||||||
|
Bytes::from_iter([2, 3]),
|
||||||
|
Bytes::from_iter([4, 5]),
|
||||||
|
Bytes::from_iter([3, 4]),
|
||||||
|
];
|
||||||
|
assert_matches!(
|
||||||
|
validate_execution_requests(&requests_out_of_order),
|
||||||
|
Err(EngineObjectValidationError::InvalidParams(_))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ use reth_chainspec::{EthereumHardfork, EthereumHardforks};
|
|||||||
use reth_engine_primitives::{BeaconConsensusEngineHandle, EngineTypes, EngineValidator};
|
use reth_engine_primitives::{BeaconConsensusEngineHandle, EngineTypes, EngineValidator};
|
||||||
use reth_payload_builder::PayloadStore;
|
use reth_payload_builder::PayloadStore;
|
||||||
use reth_payload_primitives::{
|
use reth_payload_primitives::{
|
||||||
validate_payload_timestamp, EngineApiMessageVersion, PayloadBuilderAttributes,
|
validate_execution_requests, validate_payload_timestamp, EngineApiMessageVersion,
|
||||||
PayloadOrAttributes,
|
PayloadBuilderAttributes, PayloadOrAttributes,
|
||||||
};
|
};
|
||||||
use reth_rpc_api::EngineApiServer;
|
use reth_rpc_api::EngineApiServer;
|
||||||
use reth_rpc_types_compat::engine::payload::convert_to_payload_body_v1;
|
use reth_rpc_types_compat::engine::payload::convert_to_payload_body_v1;
|
||||||
@ -268,6 +268,8 @@ where
|
|||||||
.validator
|
.validator
|
||||||
.validate_version_specific_fields(EngineApiMessageVersion::V4, payload_or_attrs)?;
|
.validate_version_specific_fields(EngineApiMessageVersion::V4, payload_or_attrs)?;
|
||||||
|
|
||||||
|
validate_execution_requests(&execution_requests)?;
|
||||||
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.inner
|
.inner
|
||||||
.beacon_consensus
|
.beacon_consensus
|
||||||
|
|||||||
Reference in New Issue
Block a user