mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat: add optimism canyon engine tests (#6107)
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5684,6 +5684,7 @@ dependencies = [
|
|||||||
"alloy-chains",
|
"alloy-chains",
|
||||||
"alloy-rlp",
|
"alloy-rlp",
|
||||||
"aquamarine",
|
"aquamarine",
|
||||||
|
"assert_matches",
|
||||||
"backon",
|
"backon",
|
||||||
"boyer-moore-magiclen",
|
"boyer-moore-magiclen",
|
||||||
"clap",
|
"clap",
|
||||||
@ -5702,6 +5703,7 @@ dependencies = [
|
|||||||
"itertools 0.12.0",
|
"itertools 0.12.0",
|
||||||
"jemalloc-ctl",
|
"jemalloc-ctl",
|
||||||
"jemallocator",
|
"jemallocator",
|
||||||
|
"jsonrpsee",
|
||||||
"metrics",
|
"metrics",
|
||||||
"metrics-exporter-prometheus",
|
"metrics-exporter-prometheus",
|
||||||
"metrics-process",
|
"metrics-process",
|
||||||
|
|||||||
@ -116,6 +116,10 @@ itertools.workspace = true
|
|||||||
rayon.workspace = true
|
rayon.workspace = true
|
||||||
futures-util.workspace = true
|
futures-util.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
jsonrpsee.workspace = true
|
||||||
|
assert_matches = "1.5.0"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
jemallocator = { version = "0.5.0", optional = true }
|
jemallocator = { version = "0.5.0", optional = true }
|
||||||
jemalloc-ctl = { version = "0.5.0", optional = true }
|
jemalloc-ctl = { version = "0.5.0", optional = true }
|
||||||
|
|||||||
@ -278,9 +278,9 @@ impl NodeConfig {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the chain for the node
|
/// Set the [ChainSpec] for the node
|
||||||
pub fn with_chain(mut self, chain: Arc<ChainSpec>) -> Self {
|
pub fn with_chain(mut self, chain: impl Into<Arc<ChainSpec>>) -> Self {
|
||||||
self.chain = chain;
|
self.chain = chain.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,12 +296,6 @@ impl NodeConfig {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the [ChainSpec] for the node
|
|
||||||
pub fn with_chain_spec(mut self, chain: Arc<ChainSpec>) -> Self {
|
|
||||||
self.chain = chain;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the trusted setup file for the node
|
/// Set the trusted setup file for the node
|
||||||
pub fn with_trusted_setup_file(mut self, trusted_setup_file: impl Into<PathBuf>) -> Self {
|
pub fn with_trusted_setup_file(mut self, trusted_setup_file: impl Into<PathBuf>) -> Self {
|
||||||
self.trusted_setup_file = Some(trusted_setup_file.into());
|
self.trusted_setup_file = Some(trusted_setup_file.into());
|
||||||
@ -1371,7 +1365,6 @@ impl NodeHandle {
|
|||||||
/// Waits for the node to exit, if it was configured to exit.
|
/// Waits for the node to exit, if it was configured to exit.
|
||||||
pub async fn wait_for_node_exit(self) -> eyre::Result<()> {
|
pub async fn wait_for_node_exit(self) -> eyre::Result<()> {
|
||||||
self.consensus_engine_rx.await??;
|
self.consensus_engine_rx.await??;
|
||||||
info!(target: "reth::cli", "Consensus engine has exited.");
|
|
||||||
|
|
||||||
if self.terminate {
|
if self.terminate {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1459,4 +1452,276 @@ mod tests {
|
|||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn optimism_pre_canyon_no_withdrawals_valid() {
|
||||||
|
reth_tracing::init_test_tracing();
|
||||||
|
use alloy_chains::Chain;
|
||||||
|
use jsonrpsee::http_client::HttpClient;
|
||||||
|
use reth_primitives::Genesis;
|
||||||
|
use reth_rpc_api::EngineApiClient;
|
||||||
|
use reth_rpc_types::engine::{
|
||||||
|
ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
// this launches a test node with http
|
||||||
|
let rpc_args = RpcServerArgs::default().with_http();
|
||||||
|
|
||||||
|
// create optimism genesis with canyon at block 2
|
||||||
|
let spec = ChainSpec::builder()
|
||||||
|
.chain(Chain::optimism_mainnet())
|
||||||
|
.genesis(Genesis::default())
|
||||||
|
.regolith_activated()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let genesis_hash = spec.genesis_hash();
|
||||||
|
|
||||||
|
// create node config
|
||||||
|
let node_config = NodeConfig::test().with_rpc(rpc_args).with_instance(7).with_chain(spec);
|
||||||
|
|
||||||
|
let (handle, _manager) = spawn_node(node_config).await.unwrap();
|
||||||
|
|
||||||
|
// call a function on the node
|
||||||
|
let client = handle.rpc_server_handles().auth.http_client();
|
||||||
|
let block_number = client.block_number().await.unwrap();
|
||||||
|
|
||||||
|
// it should be zero, since this is an ephemeral test node
|
||||||
|
assert_eq!(block_number, U256::ZERO);
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let forkchoice_state = ForkchoiceState {
|
||||||
|
head_block_hash: genesis_hash,
|
||||||
|
safe_block_hash: genesis_hash,
|
||||||
|
finalized_block_hash: genesis_hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload_attributes = OptimismPayloadAttributes {
|
||||||
|
payload_attributes: PayloadAttributes {
|
||||||
|
timestamp: 1,
|
||||||
|
prev_randao: Default::default(),
|
||||||
|
suggested_fee_recipient: Default::default(),
|
||||||
|
// canyon is _not_ in the chain spec, so this should cause the engine call to fail
|
||||||
|
withdrawals: None,
|
||||||
|
parent_beacon_block_root: None,
|
||||||
|
},
|
||||||
|
no_tx_pool: None,
|
||||||
|
gas_limit: Some(1),
|
||||||
|
transactions: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let res = <HttpClient as EngineApiClient<OptimismEngineTypes>>::fork_choice_updated_v2(
|
||||||
|
&client,
|
||||||
|
forkchoice_state,
|
||||||
|
Some(payload_attributes),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
res.expect("pre-canyon engine call without withdrawals should succeed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn optimism_pre_canyon_withdrawals_invalid() {
|
||||||
|
reth_tracing::init_test_tracing();
|
||||||
|
use alloy_chains::Chain;
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use jsonrpsee::{core::Error, http_client::HttpClient, types::error::INVALID_PARAMS_CODE};
|
||||||
|
use reth_primitives::Genesis;
|
||||||
|
use reth_rpc_api::EngineApiClient;
|
||||||
|
use reth_rpc_types::engine::{
|
||||||
|
ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
// this launches a test node with http
|
||||||
|
let rpc_args = RpcServerArgs::default().with_http();
|
||||||
|
|
||||||
|
// create optimism genesis with canyon at block 2
|
||||||
|
let spec = ChainSpec::builder()
|
||||||
|
.chain(Chain::optimism_mainnet())
|
||||||
|
.genesis(Genesis::default())
|
||||||
|
.regolith_activated()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let genesis_hash = spec.genesis_hash();
|
||||||
|
|
||||||
|
// create node config
|
||||||
|
let node_config = NodeConfig::test().with_rpc(rpc_args).with_instance(8).with_chain(spec);
|
||||||
|
|
||||||
|
let (handle, _manager) = spawn_node(node_config).await.unwrap();
|
||||||
|
|
||||||
|
// call a function on the node
|
||||||
|
let client = handle.rpc_server_handles().auth.http_client();
|
||||||
|
let block_number = client.block_number().await.unwrap();
|
||||||
|
|
||||||
|
// it should be zero, since this is an ephemeral test node
|
||||||
|
assert_eq!(block_number, U256::ZERO);
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let forkchoice_state = ForkchoiceState {
|
||||||
|
head_block_hash: genesis_hash,
|
||||||
|
safe_block_hash: genesis_hash,
|
||||||
|
finalized_block_hash: genesis_hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload_attributes = OptimismPayloadAttributes {
|
||||||
|
payload_attributes: PayloadAttributes {
|
||||||
|
timestamp: 1,
|
||||||
|
prev_randao: Default::default(),
|
||||||
|
suggested_fee_recipient: Default::default(),
|
||||||
|
// canyon is _not_ in the chain spec, so this should cause the engine call to fail
|
||||||
|
withdrawals: Some(vec![]),
|
||||||
|
parent_beacon_block_root: None,
|
||||||
|
},
|
||||||
|
no_tx_pool: None,
|
||||||
|
gas_limit: Some(1),
|
||||||
|
transactions: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let res = <HttpClient as EngineApiClient<OptimismEngineTypes>>::fork_choice_updated_v2(
|
||||||
|
&client,
|
||||||
|
forkchoice_state,
|
||||||
|
Some(payload_attributes),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let err = res.expect_err("pre-canyon engine call with withdrawals should fail");
|
||||||
|
assert_matches!(err, Error::Call(ref object) if object.code() == INVALID_PARAMS_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn optimism_post_canyon_no_withdrawals_invalid() {
|
||||||
|
reth_tracing::init_test_tracing();
|
||||||
|
use alloy_chains::Chain;
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use jsonrpsee::{core::Error, http_client::HttpClient, types::error::INVALID_PARAMS_CODE};
|
||||||
|
use reth_primitives::Genesis;
|
||||||
|
use reth_rpc_api::EngineApiClient;
|
||||||
|
use reth_rpc_types::engine::{
|
||||||
|
ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
// this launches a test node with http
|
||||||
|
let rpc_args = RpcServerArgs::default().with_http();
|
||||||
|
|
||||||
|
// create optimism genesis with canyon at block 2
|
||||||
|
let spec = ChainSpec::builder()
|
||||||
|
.chain(Chain::optimism_mainnet())
|
||||||
|
.genesis(Genesis::default())
|
||||||
|
.canyon_activated()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let genesis_hash = spec.genesis_hash();
|
||||||
|
|
||||||
|
// create node config
|
||||||
|
let node_config = NodeConfig::test().with_rpc(rpc_args).with_instance(9).with_chain(spec);
|
||||||
|
|
||||||
|
let (handle, _manager) = spawn_node(node_config).await.unwrap();
|
||||||
|
|
||||||
|
// call a function on the node
|
||||||
|
let client = handle.rpc_server_handles().auth.http_client();
|
||||||
|
let block_number = client.block_number().await.unwrap();
|
||||||
|
|
||||||
|
// it should be zero, since this is an ephemeral test node
|
||||||
|
assert_eq!(block_number, U256::ZERO);
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let forkchoice_state = ForkchoiceState {
|
||||||
|
head_block_hash: genesis_hash,
|
||||||
|
safe_block_hash: genesis_hash,
|
||||||
|
finalized_block_hash: genesis_hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload_attributes = OptimismPayloadAttributes {
|
||||||
|
payload_attributes: PayloadAttributes {
|
||||||
|
timestamp: 1,
|
||||||
|
prev_randao: Default::default(),
|
||||||
|
suggested_fee_recipient: Default::default(),
|
||||||
|
// canyon is _not_ in the chain spec, so this should cause the engine call to fail
|
||||||
|
withdrawals: None,
|
||||||
|
parent_beacon_block_root: None,
|
||||||
|
},
|
||||||
|
no_tx_pool: None,
|
||||||
|
gas_limit: Some(1),
|
||||||
|
transactions: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let res = <HttpClient as EngineApiClient<OptimismEngineTypes>>::fork_choice_updated_v2(
|
||||||
|
&client,
|
||||||
|
forkchoice_state,
|
||||||
|
Some(payload_attributes),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let err = res.expect_err("post-canyon engine call with no withdrawals should fail");
|
||||||
|
assert_matches!(err, Error::Call(ref object) if object.code() == INVALID_PARAMS_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn optimism_post_canyon_withdrawals_valid() {
|
||||||
|
reth_tracing::init_test_tracing();
|
||||||
|
use alloy_chains::Chain;
|
||||||
|
use jsonrpsee::http_client::HttpClient;
|
||||||
|
use reth_primitives::Genesis;
|
||||||
|
use reth_rpc_api::EngineApiClient;
|
||||||
|
use reth_rpc_types::engine::{
|
||||||
|
ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
// this launches a test node with http
|
||||||
|
let rpc_args = RpcServerArgs::default().with_http();
|
||||||
|
|
||||||
|
// create optimism genesis with canyon at block 2
|
||||||
|
let spec = ChainSpec::builder()
|
||||||
|
.chain(Chain::optimism_mainnet())
|
||||||
|
.genesis(Genesis::default())
|
||||||
|
.canyon_activated()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let genesis_hash = spec.genesis_hash();
|
||||||
|
|
||||||
|
// create node config
|
||||||
|
let node_config = NodeConfig::test().with_rpc(rpc_args).with_instance(10).with_chain(spec);
|
||||||
|
|
||||||
|
let (handle, _manager) = spawn_node(node_config).await.unwrap();
|
||||||
|
|
||||||
|
// call a function on the node
|
||||||
|
let client = handle.rpc_server_handles().auth.http_client();
|
||||||
|
let block_number = client.block_number().await.unwrap();
|
||||||
|
|
||||||
|
// it should be zero, since this is an ephemeral test node
|
||||||
|
assert_eq!(block_number, U256::ZERO);
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let forkchoice_state = ForkchoiceState {
|
||||||
|
head_block_hash: genesis_hash,
|
||||||
|
safe_block_hash: genesis_hash,
|
||||||
|
finalized_block_hash: genesis_hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload_attributes = OptimismPayloadAttributes {
|
||||||
|
payload_attributes: PayloadAttributes {
|
||||||
|
timestamp: 1,
|
||||||
|
prev_randao: Default::default(),
|
||||||
|
suggested_fee_recipient: Default::default(),
|
||||||
|
// canyon is _not_ in the chain spec, so this should cause the engine call to fail
|
||||||
|
withdrawals: Some(vec![]),
|
||||||
|
parent_beacon_block_root: None,
|
||||||
|
},
|
||||||
|
no_tx_pool: None,
|
||||||
|
gas_limit: Some(1),
|
||||||
|
transactions: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// call the engine_forkchoiceUpdated function with payload attributes
|
||||||
|
let res = <HttpClient as EngineApiClient<OptimismEngineTypes>>::fork_choice_updated_v2(
|
||||||
|
&client,
|
||||||
|
forkchoice_state,
|
||||||
|
Some(payload_attributes),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
res.expect("post-canyon engine call with withdrawals should succeed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ pub struct OptimismPayloadAttributes {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::engine::ExecutionPayloadInputV2;
|
use crate::engine::{ExecutionPayloadInputV2, OptimismPayloadAttributes};
|
||||||
|
|
||||||
// <https://github.com/paradigmxyz/reth/issues/6036>
|
// <https://github.com/paradigmxyz/reth/issues/6036>
|
||||||
#[test]
|
#[test]
|
||||||
@ -34,4 +34,11 @@ mod tests {
|
|||||||
let payload = r#"{"parentHash":"0x24e8df372a61cdcdb1a163b52aaa1785e0c869d28c3b742ac09e826bbb524723","feeRecipient":"0x4200000000000000000000000000000000000011","stateRoot":"0x9a5db45897f1ff1e620a6c14b0a6f1b3bcdbed59f2adc516a34c9a9d6baafa71","receiptsRoot":"0x8af6f74835d47835deb5628ca941d00e0c9fd75585f26dabdcb280ec7122e6af","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0xf37b24eeff594848072a05f74c8600001706c83e489a9132e55bf43a236e42ec","blockNumber":"0xe3d5d8","gasLimit":"0x17d7840","gasUsed":"0xb705","timestamp":"0x65a118c0","extraData":"0x","baseFeePerGas":"0x7a0ff32","blockHash":"0xf5c147b2d60a519b72434f0a8e082e18599021294dd9085d7597b0ffa638f1c0","withdrawals":[],"transactions":["0x7ef90159a05ba0034ffdcb246703298224564720b66964a6a69d0d7e9ffd970c546f7c048094deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b90104015d8eb900000000000000000000000000000000000000000000000000000000009e1c4a0000000000000000000000000000000000000000000000000000000065a11748000000000000000000000000000000000000000000000000000000000000000a4b479e5fa8d52dd20a8a66e468b56e993bdbffcccf729223aabff06299ab36db000000000000000000000000000000000000000000000000000000000000000400000000000000000000000073b4168cc87f35cc239200a20eb841cded23493b000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"]}"#;
|
let payload = r#"{"parentHash":"0x24e8df372a61cdcdb1a163b52aaa1785e0c869d28c3b742ac09e826bbb524723","feeRecipient":"0x4200000000000000000000000000000000000011","stateRoot":"0x9a5db45897f1ff1e620a6c14b0a6f1b3bcdbed59f2adc516a34c9a9d6baafa71","receiptsRoot":"0x8af6f74835d47835deb5628ca941d00e0c9fd75585f26dabdcb280ec7122e6af","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0xf37b24eeff594848072a05f74c8600001706c83e489a9132e55bf43a236e42ec","blockNumber":"0xe3d5d8","gasLimit":"0x17d7840","gasUsed":"0xb705","timestamp":"0x65a118c0","extraData":"0x","baseFeePerGas":"0x7a0ff32","blockHash":"0xf5c147b2d60a519b72434f0a8e082e18599021294dd9085d7597b0ffa638f1c0","withdrawals":[],"transactions":["0x7ef90159a05ba0034ffdcb246703298224564720b66964a6a69d0d7e9ffd970c546f7c048094deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b90104015d8eb900000000000000000000000000000000000000000000000000000000009e1c4a0000000000000000000000000000000000000000000000000000000065a11748000000000000000000000000000000000000000000000000000000000000000a4b479e5fa8d52dd20a8a66e468b56e993bdbffcccf729223aabff06299ab36db000000000000000000000000000000000000000000000000000000000000000400000000000000000000000073b4168cc87f35cc239200a20eb841cded23493b000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"]}"#;
|
||||||
let _payload = serde_json::from_str::<ExecutionPayloadInputV2>(payload).unwrap();
|
let _payload = serde_json::from_str::<ExecutionPayloadInputV2>(payload).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_op_payload_attributes() {
|
||||||
|
let payload = r#"{"prevRandao":"0x24e8df372a61cdcdb1a163b52aaa1785e0c869d28c3b742ac09e826bbb524723","suggestedFeeRecipient":"0x4200000000000000000000000000000000000011","timestamp":"1","gasLimit":"0x17d7840","transactions":[],"no_tx_pool":"true","withdrawals":[]}"#;
|
||||||
|
let _payload = serde_json::from_str::<OptimismPayloadAttributes>(payload).unwrap();
|
||||||
|
assert!(_payload.payload_attributes.withdrawals.is_some())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user