feat: add optimism canyon engine tests (#6107)

This commit is contained in:
Dan Cline
2024-01-17 15:54:00 -05:00
committed by GitHub
parent 43167eabda
commit aeee4486eb
4 changed files with 289 additions and 11 deletions

2
Cargo.lock generated
View File

@ -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",

View File

@ -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 }

View File

@ -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");
}
} }

View File

@ -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())
}
} }