diff --git a/application/src/actor.rs b/application/src/actor.rs index 4a02915..d548451 100644 --- a/application/src/actor.rs +++ b/application/src/actor.rs @@ -389,7 +389,7 @@ impl< .await .expect("Finalizer dropped"); - let Some(mut aux_data) = maybe_aux_data else { + let Some(aux_data) = maybe_aux_data else { debug!( "Aborting block proposal for epoch {} and height {} because of an outdated aux data request", round.epoch().get(), @@ -440,7 +440,7 @@ impl< #[cfg(feature = "prom")] let start_building_start = std::time::Instant::now(); - aux_data.forkchoice.head_block_hash = parent_block.eth_block_hash().into(); + // aux_data.forkchoice.head_block_hash = parent_block.eth_block_hash().into(); // Add pending withdrawals to the block let withdrawals = pending_withdrawals.into_iter().map(|w| w.inner).collect(); diff --git a/node/Cargo.toml b/node/Cargo.toml index 313cb6b..06bb464 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -143,3 +143,4 @@ tokio-console = ["console-subscriber"] jemalloc = ["dep:tikv-jemalloc-ctl"] bench = ["summit-application/bench", "summit-types/bench"] e2e = ["summit-types/e2e"] +bad-blocks = ["summit-types/bad-blocks"] \ No newline at end of file diff --git a/node/src/args.rs b/node/src/args.rs index 7450c16..5c813c8 100644 --- a/node/src/args.rs +++ b/node/src/args.rs @@ -30,6 +30,9 @@ use std::{ #[cfg(feature = "bench")] use summit_types::engine_client::benchmarking::EthereumHistoricalEngineClient; +#[cfg(feature = "bad-blocks")] +use summit_types::engine_client::BadBlockEngineClient; + use crate::config::MAILBOX_SIZE; use summit_types::FinalizedHeader; #[cfg(not(feature = "bench"))] @@ -521,7 +524,11 @@ pub fn run_node_local( .await }; - #[cfg(not(feature = "bench"))] + #[cfg(feature = "bad-blocks")] + let engine_client = + BadBlockEngineClient::new(engine_ipc_path.to_string_lossy().to_string(), 4).await; // make every 4th block a bad eth payload + + #[cfg(all(not(feature = "bench"), not(feature = "bad-blocks")))] let engine_client = RethEngineClient::new(engine_ipc_path.to_string_lossy().to_string()).await; diff --git a/types/Cargo.toml b/types/Cargo.toml index 91d4734..548f75a 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -42,4 +42,5 @@ libc = { version = "0.2.174", optional = true} [features] bench = ["serde_json"] -e2e = ["libc", "alloy-genesis", "url"] \ No newline at end of file +e2e = ["libc", "alloy-genesis", "url"] +bad-blocks = [] \ No newline at end of file diff --git a/types/src/engine_client.rs b/types/src/engine_client.rs index e4d0d53..5b11c01 100644 --- a/types/src/engine_client.rs +++ b/types/src/engine_client.rs @@ -194,6 +194,159 @@ impl EngineClient for RethEngineClient { } } +#[cfg(feature = "bad-blocks")] +#[derive(Clone)] +pub struct BadBlockEngineClient { + engine_ipc_path: String, + provider: RootProvider, + /// How often a bad block should happen + bad_block_timing: u64, +} + +#[cfg(feature = "bad-blocks")] +impl BadBlockEngineClient { + pub async fn new(engine_ipc_path: String, bad_block_timing: u64) -> Self { + let ipc = IpcConnect::new(engine_ipc_path.clone()); + let provider = ProviderBuilder::default().connect_ipc(ipc).await.unwrap(); + Self { + provider, + engine_ipc_path, + bad_block_timing, + } + } + + pub async fn wait_until_reconnect_available(&mut self) { + loop { + let ipc = IpcConnect::new(self.engine_ipc_path.clone()); + + match ProviderBuilder::default().connect_ipc(ipc).await { + Ok(provider) => { + self.provider = provider; + break; + } + Err(e) => { + error!("Failed to connect to IPC, retrying: {}", e); + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + } + } + } + } +} + +#[cfg(feature = "bad-blocks")] +impl EngineClient for BadBlockEngineClient { + async fn start_building_block( + &mut self, + fork_choice_state: ForkchoiceState, + timestamp: u64, + withdrawals: Vec, + suggested_fee_recipient: Address, + parent_beacon_block_root: Option>, + #[cfg(feature = "bench")] _height: u64, + ) -> Option { + let payload_attributes = PayloadAttributes { + timestamp, + prev_randao: [0; 32].into(), + // todo(dalton): this should be the validators public key + suggested_fee_recipient, + withdrawals: Some(withdrawals), + // todo(dalton): we should make this something that we can associate with the simplex height + parent_beacon_block_root, + }; + + let res = match self + .provider + .fork_choice_updated_v3(fork_choice_state, Some(payload_attributes.clone())) + .await + { + Ok(res) => res, + Err(e) if e.is_transport_error() => { + self.wait_until_reconnect_available().await; + self.provider + .fork_choice_updated_v3(fork_choice_state, Some(payload_attributes)) + .await + .expect("Failed to update fork choice after reconnect") + } + Err(_) => panic!("Unable to get a response"), + }; + + if res.is_invalid() { + error!("invalid returned for forkchoice state {fork_choice_state:?}: {res:?}"); + } + if res.is_syncing() { + warn!("syncing returned for forkchoice state {fork_choice_state:?}: {res:?}"); + } + + res.payload_id + } + + async fn get_payload(&mut self, payload_id: PayloadId) -> ExecutionPayloadEnvelopeV4 { + match self.provider.get_payload_v4(payload_id).await { + Ok(res) => res, + Err(e) if e.is_transport_error() => { + self.wait_until_reconnect_available().await; + self.provider + .get_payload_v4(payload_id) + .await + .expect("Failed to get payload after reconnect") + } + Err(_) => panic!("Unable to get a response"), + } + } + + async fn check_payload(&mut self, block: &Block) -> PayloadStatus { + let parent_beacon_block_root = if block.height().is_multiple_of(self.bad_block_timing) { + [1; 32].into() + } else { + block.parent().0.into() + }; + + match self + .provider + .new_payload_v4( + block.payload.clone(), + Vec::new(), + parent_beacon_block_root, + block.execution_requests.clone(), + ) + .await + { + Ok(res) => res, + Err(e) if e.is_transport_error() => { + self.wait_until_reconnect_available().await; + self.provider + .new_payload_v4( + block.payload.clone(), + Vec::new(), + [1; 32].into(), + block.execution_requests.clone(), + ) + .await + .expect("Failed to check payload after reconnect") + } + Err(_) => panic!("Unable to get a response"), + } + } + + async fn commit_hash(&mut self, fork_choice_state: ForkchoiceState) { + let _ = match self + .provider + .fork_choice_updated_v3(fork_choice_state, None) + .await + { + Ok(res) => res, + Err(e) if e.is_transport_error() => { + self.wait_until_reconnect_available().await; + self.provider + .fork_choice_updated_v3(fork_choice_state, None) + .await + .expect("Failed to get payload after reconnect") + } + Err(_) => panic!("Unable to get a response"), + }; + } +} + #[cfg(feature = "bench")] pub mod benchmarking { use crate::engine_client::EngineClient;