Poll the engine_exchangeTransitionConfigurationV1 endpoint (#3047)
				
					
				
			## Issue Addressed There has been an [`engine_exchangetransitionconfigurationv1`](https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_exchangetransitionconfigurationv1) method added to the execution API specs. The `engine_exchangetransitionconfigurationv1` will be polled every 60s as per this PR: https://github.com/ethereum/execution-apis/pull/189. If that PR is merged as-is, then we will be matching the spec. If that PR *is not* merged, we are still fully compatible with the spec, but just doing more than we are required. ## Additional Info - [x] ~~Blocked on #2837~~ - [x] Add method to EE integration tests
This commit is contained in:
		
							parent
							
								
									4186d117af
								
							
						
					
					
						commit
						3b4865c3ae
					
				| @ -723,6 +723,9 @@ where | ||||
|                 execution_layer.spawn_clean_proposer_preparation_routine::<TSlotClock, TEthSpec>( | ||||
|                     beacon_chain.slot_clock.clone(), | ||||
|                 ); | ||||
| 
 | ||||
|                 // Spawns a routine that polls the `exchange_transition_configuration` endpoint.
 | ||||
|                 execution_layer.spawn_transition_configuration_poll(beacon_chain.spec.clone()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; | ||||
| pub const LATEST_TAG: &str = "latest"; | ||||
| 
 | ||||
| use crate::engines::ForkChoiceState; | ||||
| pub use json_structures::TransitionConfigurationV1; | ||||
| pub use types::{Address, EthSpec, ExecutionBlockHash, ExecutionPayload, Hash256, Uint256}; | ||||
| 
 | ||||
| pub mod http; | ||||
| @ -27,6 +28,7 @@ pub enum Error { | ||||
|     ExecutionHeadBlockNotFound, | ||||
|     ParentHashEqualsBlockHash(ExecutionBlockHash), | ||||
|     PayloadIdUnavailable, | ||||
|     TransitionConfigurationMismatch, | ||||
| } | ||||
| 
 | ||||
| impl From<reqwest::Error> for Error { | ||||
| @ -71,6 +73,11 @@ pub trait EngineApi { | ||||
|         forkchoice_state: ForkChoiceState, | ||||
|         payload_attributes: Option<PayloadAttributes>, | ||||
|     ) -> Result<ForkchoiceUpdatedResponse, Error>; | ||||
| 
 | ||||
|     async fn exchange_transition_configuration_v1( | ||||
|         &self, | ||||
|         transition_configuration: TransitionConfigurationV1, | ||||
|     ) -> Result<TransitionConfigurationV1, Error>; | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Copy, Debug, PartialEq)] | ||||
|  | ||||
| @ -36,6 +36,11 @@ pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2); | ||||
| pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1"; | ||||
| pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_millis(500); | ||||
| 
 | ||||
| pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1: &str = | ||||
|     "engine_exchangeTransitionConfigurationV1"; | ||||
| pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1_TIMEOUT: Duration = | ||||
|     Duration::from_millis(500); | ||||
| 
 | ||||
| pub struct HttpJsonRpc { | ||||
|     pub client: Client, | ||||
|     pub url: SensitiveUrl, | ||||
| @ -179,6 +184,23 @@ impl EngineApi for HttpJsonRpc { | ||||
| 
 | ||||
|         Ok(response.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn exchange_transition_configuration_v1( | ||||
|         &self, | ||||
|         transition_configuration: TransitionConfigurationV1, | ||||
|     ) -> Result<TransitionConfigurationV1, Error> { | ||||
|         let params = json!([transition_configuration]); | ||||
| 
 | ||||
|         let response = self | ||||
|             .rpc_request( | ||||
|                 ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1, | ||||
|                 params, | ||||
|                 ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1_TIMEOUT, | ||||
|             ) | ||||
|             .await?; | ||||
| 
 | ||||
|         Ok(response) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  | ||||
| @ -363,6 +363,15 @@ impl From<ForkchoiceUpdatedResponse> for JsonForkchoiceUpdatedV1Response { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct TransitionConfigurationV1 { | ||||
|     pub terminal_total_difficulty: Uint256, | ||||
|     pub terminal_block_hash: ExecutionBlockHash, | ||||
|     #[serde(with = "eth2_serde_utils::u64_hex_be")] | ||||
|     pub terminal_block_number: u64, | ||||
| } | ||||
| 
 | ||||
| /// Serializes the `logs_bloom` field of an `ExecutionPayload`.
 | ||||
| pub mod serde_logs_bloom { | ||||
|     use super::*; | ||||
|  | ||||
| @ -44,6 +44,8 @@ const EXECUTION_BLOCKS_LRU_CACHE_SIZE: usize = 128; | ||||
| const DEFAULT_SUGGESTED_FEE_RECIPIENT: [u8; 20] = | ||||
|     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; | ||||
| 
 | ||||
| const CONFIG_POLL_INTERVAL: Duration = Duration::from_secs(60); | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum Error { | ||||
|     NoEngines, | ||||
| @ -303,6 +305,24 @@ impl ExecutionLayer { | ||||
|         self.spawn(preparation_cleaner, "exec_preparation_cleanup"); | ||||
|     } | ||||
| 
 | ||||
|     /// Spawns a routine that polls the `exchange_transition_configuration` endpoint.
 | ||||
|     pub fn spawn_transition_configuration_poll(&self, spec: ChainSpec) { | ||||
|         let routine = |el: ExecutionLayer| async move { | ||||
|             loop { | ||||
|                 if let Err(e) = el.exchange_transition_configuration(&spec).await { | ||||
|                     error!( | ||||
|                         el.log(), | ||||
|                         "Failed to check transition config"; | ||||
|                         "error" => ?e | ||||
|                     ); | ||||
|                 } | ||||
|                 sleep(CONFIG_POLL_INTERVAL).await; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         self.spawn(routine, "exec_config_poll"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns `true` if there is at least one synced and reachable engine.
 | ||||
|     pub async fn is_synced(&self) -> bool { | ||||
|         self.engines().any_synced().await | ||||
| @ -551,6 +571,65 @@ impl ExecutionLayer { | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn exchange_transition_configuration(&self, spec: &ChainSpec) -> Result<(), Error> { | ||||
|         let local = TransitionConfigurationV1 { | ||||
|             terminal_total_difficulty: spec.terminal_total_difficulty, | ||||
|             terminal_block_hash: spec.terminal_block_hash, | ||||
|             terminal_block_number: 0, | ||||
|         }; | ||||
| 
 | ||||
|         let broadcast_results = self | ||||
|             .engines() | ||||
|             .broadcast(|engine| engine.api.exchange_transition_configuration_v1(local)) | ||||
|             .await; | ||||
| 
 | ||||
|         let mut errors = vec![]; | ||||
|         for (i, result) in broadcast_results.into_iter().enumerate() { | ||||
|             match result { | ||||
|                 Ok(remote) => { | ||||
|                     if local.terminal_total_difficulty != remote.terminal_total_difficulty | ||||
|                         || local.terminal_block_hash != remote.terminal_block_hash | ||||
|                     { | ||||
|                         error!( | ||||
|                             self.log(), | ||||
|                             "Execution client config mismatch"; | ||||
|                             "msg" => "ensure lighthouse and the execution client are up-to-date and \ | ||||
|                                       configured consistently",
 | ||||
|                             "execution_endpoint" => i, | ||||
|                             "remote" => ?remote, | ||||
|                             "local" => ?local, | ||||
|                         ); | ||||
|                         errors.push(EngineError::Api { | ||||
|                             id: i.to_string(), | ||||
|                             error: ApiError::TransitionConfigurationMismatch, | ||||
|                         }); | ||||
|                     } else { | ||||
|                         debug!( | ||||
|                             self.log(), | ||||
|                             "Execution client config is OK"; | ||||
|                             "execution_endpoint" => i | ||||
|                         ); | ||||
|                     } | ||||
|                 } | ||||
|                 Err(e) => { | ||||
|                     error!( | ||||
|                         self.log(), | ||||
|                         "Unable to get transition config"; | ||||
|                         "error" => ?e, | ||||
|                         "execution_endpoint" => i, | ||||
|                     ); | ||||
|                     errors.push(e); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if errors.is_empty() { | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(Error::EngineErrors(errors)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Used during block production to determine if the merge has been triggered.
 | ||||
|     ///
 | ||||
|     /// ## Specification
 | ||||
|  | ||||
| @ -109,7 +109,7 @@ impl GenericExecutionEngine for Geth { | ||||
|             .arg("engine,eth") | ||||
|             .arg("--http.port") | ||||
|             .arg(http_port.to_string()) | ||||
|             .arg("--http.authport") | ||||
|             .arg("--authrpc.port") | ||||
|             .arg(http_auth_port.to_string()) | ||||
|             .arg("--port") | ||||
|             .arg(network_port.to_string()) | ||||
|  | ||||
| @ -108,6 +108,16 @@ impl<E: GenericExecutionEngine> TestRig<E> { | ||||
|     pub async fn perform_tests(&self) { | ||||
|         self.wait_until_synced().await; | ||||
| 
 | ||||
|         /* | ||||
|          * Check the transition config endpoint. | ||||
|          */ | ||||
|         for ee in [&self.ee_a, &self.ee_b] { | ||||
|             ee.execution_layer | ||||
|                 .exchange_transition_configuration(&self.spec) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|         } | ||||
| 
 | ||||
|         /* | ||||
|          * Read the terminal block hash from both pairs, check it's equal. | ||||
|          */ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user