[Merge] Optimistic EL verification (#2683)

* Ignore payload errors

* Only return payload handle on valid response

* Push some engine logs down to debug

* Push ee fork choice log to debug

* Push engine call failure to debug

* Push some more errors to debug

* Fix panic at startup
This commit is contained in:
Paul Hauner 2021-10-07 00:34:17 +11:00
parent 35350dff75
commit 67a6f91df6
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
6 changed files with 80 additions and 62 deletions

View File

@ -3378,7 +3378,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
) )
.await .await
{ {
error!( debug!(
log, log,
"Failed to update execution head"; "Failed to update execution head";
"error" => ?e "error" => ?e

View File

@ -55,7 +55,7 @@ use fork_choice::{ForkChoice, ForkChoiceStore};
use parking_lot::RwLockReadGuard; use parking_lot::RwLockReadGuard;
use proto_array::Block as ProtoBlock; use proto_array::Block as ProtoBlock;
use safe_arith::ArithError; use safe_arith::ArithError;
use slog::{debug, error, Logger}; use slog::{debug, error, info, Logger};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use ssz::Encode; use ssz::Encode;
use state_processing::per_block_processing::{is_execution_enabled, is_merge_block}; use state_processing::per_block_processing::{is_execution_enabled, is_merge_block};
@ -1127,7 +1127,15 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
match is_valid_terminal_pow_block { match is_valid_terminal_pow_block {
Some(true) => Ok(()), Some(true) => Ok(()),
Some(false) => Err(ExecutionPayloadError::InvalidTerminalPoWBlock), Some(false) => Err(ExecutionPayloadError::InvalidTerminalPoWBlock),
None => Err(ExecutionPayloadError::TerminalPoWBlockNotFound), None => {
info!(
chain.log,
"Optimistically accepting terminal block";
"block_hash" => ?execution_payload.parent_hash,
"msg" => "the terminal block/parent was unavailable"
);
Ok(())
}
}?; }?;
} }
@ -1147,21 +1155,34 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
object_fork: block.message().body().fork_name(), object_fork: block.message().body().fork_name(),
})?; })?;
let (execute_payload_status, execute_payload_handle) = execution_layer let execute_payload_response = execution_layer
.block_on(|execution_layer| execution_layer.execute_payload(execution_payload)) .block_on(|execution_layer| execution_layer.execute_payload(execution_payload));
.map_err(ExecutionPayloadError::from)?;
match execute_payload_status { match execute_payload_response {
ExecutePayloadResponse::Valid => Ok(()), Ok((status, handle)) => match status {
ExecutePayloadResponse::Valid => handle,
ExecutePayloadResponse::Invalid => { ExecutePayloadResponse::Invalid => {
Err(ExecutionPayloadError::RejectedByExecutionEngine) return Err(ExecutionPayloadError::RejectedByExecutionEngine.into());
} }
ExecutePayloadResponse::Syncing => { ExecutePayloadResponse::Syncing => {
Err(ExecutionPayloadError::ExecutionEngineIsSyncing) debug!(
chain.log,
"Optimistically accepting payload";
"msg" => "execution engine is syncing"
);
handle
}
},
Err(e) => {
error!(
chain.log,
"Optimistically accepting payload";
"error" => ?e,
"msg" => "execution engine returned an error"
);
None
}
} }
}?;
Some(execute_payload_handle)
} else { } else {
None None
}; };

View File

@ -665,13 +665,9 @@ where
// Issue the head to the execution engine on startup. This ensures it can start // Issue the head to the execution engine on startup. This ensures it can start
// syncing. // syncing.
if head.is_merge_complete { if head.is_merge_complete {
let result = runtime_context runtime_context.executor.spawn(
.executor async move {
.runtime() let result = BeaconChain::<
.upgrade()
.ok_or_else(|| "Cannot update engine head, shutting down".to_string())?
.block_on(async move {
BeaconChain::<
Witness<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>, Witness<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>,
>::update_execution_engine_forkchoice( >::update_execution_engine_forkchoice(
inner_execution_layer, inner_execution_layer,
@ -679,8 +675,7 @@ where
head.finalized_checkpoint.root, head.finalized_checkpoint.root,
head.block_root, head.block_root,
) )
.await .await;
});
// No need to exit early if setting the head fails. It will be set again if/when the // No need to exit early if setting the head fails. It will be set again if/when the
// node comes online. // node comes online.
@ -691,6 +686,9 @@ where
"error" => ?e "error" => ?e
); );
} }
},
"el_fork_choice_update",
);
} }
// Spawn a routine that tracks the status of the execution engines. // Spawn a routine that tracks the status of the execution engines.

View File

@ -2,7 +2,7 @@
use crate::engine_api::{EngineApi, Error as EngineApiError}; use crate::engine_api::{EngineApi, Error as EngineApiError};
use futures::future::join_all; use futures::future::join_all;
use slog::{crit, debug, error, info, warn, Logger}; use slog::{crit, debug, info, warn, Logger};
use std::future::Future; use std::future::Future;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use types::Hash256; use types::Hash256;
@ -89,7 +89,7 @@ impl<T: EngineApi> Engines<T> {
.forkchoice_updated(head.head_block_hash, head.finalized_block_hash) .forkchoice_updated(head.head_block_hash, head.finalized_block_hash)
.await .await
{ {
error!( debug!(
self.log, self.log,
"Failed to issue latest head to engine"; "Failed to issue latest head to engine";
"error" => ?e, "error" => ?e,
@ -225,7 +225,7 @@ impl<T: EngineApi> Engines<T> {
match func(engine).await { match func(engine).await {
Ok(result) => return Ok(result), Ok(result) => return Ok(result),
Err(error) => { Err(error) => {
error!( debug!(
self.log, self.log,
"Execution engine call failed"; "Execution engine call failed";
"error" => ?error, "error" => ?error,
@ -291,7 +291,7 @@ impl<T: EngineApi> Engines<T> {
let is_offline = *engine.state.read().await == EngineState::Offline; let is_offline = *engine.state.read().await == EngineState::Offline;
if !is_offline { if !is_offline {
func(engine).await.map_err(|error| { func(engine).await.map_err(|error| {
error!( debug!(
self.log, self.log,
"Execution engine call failed"; "Execution engine call failed";
"error" => ?error, "error" => ?error,

View File

@ -8,7 +8,7 @@ use engine_api::{Error as ApiError, *};
use engines::{Engine, EngineError, Engines, ForkChoiceHead, Logging}; use engines::{Engine, EngineError, Engines, ForkChoiceHead, Logging};
use lru::LruCache; use lru::LruCache;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use slog::{crit, error, info, Logger}; use slog::{crit, debug, error, info, Logger};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use std::future::Future; use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
@ -249,7 +249,7 @@ impl ExecutionLayer {
random: Hash256, random: Hash256,
) -> Result<PayloadId, Error> { ) -> Result<PayloadId, Error> {
let fee_recipient = self.fee_recipient()?; let fee_recipient = self.fee_recipient()?;
info!( debug!(
self.log(), self.log(),
"Issuing engine_preparePayload"; "Issuing engine_preparePayload";
"fee_recipient" => ?fee_recipient, "fee_recipient" => ?fee_recipient,
@ -285,7 +285,7 @@ impl ExecutionLayer {
random: Hash256, random: Hash256,
) -> Result<ExecutionPayload<T>, Error> { ) -> Result<ExecutionPayload<T>, Error> {
let fee_recipient = self.fee_recipient()?; let fee_recipient = self.fee_recipient()?;
info!( debug!(
self.log(), self.log(),
"Issuing engine_getPayload"; "Issuing engine_getPayload";
"fee_recipient" => ?fee_recipient, "fee_recipient" => ?fee_recipient,
@ -323,8 +323,8 @@ impl ExecutionLayer {
pub async fn execute_payload<T: EthSpec>( pub async fn execute_payload<T: EthSpec>(
&self, &self,
execution_payload: &ExecutionPayload<T>, execution_payload: &ExecutionPayload<T>,
) -> Result<(ExecutePayloadResponse, ExecutePayloadHandle), Error> { ) -> Result<(ExecutePayloadResponse, Option<ExecutePayloadHandle>), Error> {
info!( debug!(
self.log(), self.log(),
"Issuing engine_executePayload"; "Issuing engine_executePayload";
"parent_hash" => ?execution_payload.parent_hash, "parent_hash" => ?execution_payload.parent_hash,
@ -358,23 +358,20 @@ impl ExecutionLayer {
); );
} }
let execute_payload_response = if valid > 0 { if valid > 0 {
ExecutePayloadResponse::Valid let handle = ExecutePayloadHandle {
} else if invalid > 0 {
ExecutePayloadResponse::Invalid
} else if syncing > 0 {
ExecutePayloadResponse::Syncing
} else {
return Err(Error::EngineErrors(errors));
};
let execute_payload_handle = ExecutePayloadHandle {
block_hash: execution_payload.block_hash, block_hash: execution_payload.block_hash,
execution_layer: Some(self.clone()), execution_layer: Some(self.clone()),
log: self.log().clone(), log: self.log().clone(),
}; };
Ok((ExecutePayloadResponse::Valid, Some(handle)))
Ok((execute_payload_response, execute_payload_handle)) } else if invalid > 0 {
Ok((ExecutePayloadResponse::Invalid, None))
} else if syncing > 0 {
Ok((ExecutePayloadResponse::Syncing, None))
} else {
Err(Error::EngineErrors(errors))
}
} }
/// Maps to the `engine_consensusValidated` JSON-RPC call. /// Maps to the `engine_consensusValidated` JSON-RPC call.
@ -392,7 +389,7 @@ impl ExecutionLayer {
block_hash: Hash256, block_hash: Hash256,
status: ConsensusStatus, status: ConsensusStatus,
) -> Result<(), Error> { ) -> Result<(), Error> {
info!( debug!(
self.log(), self.log(),
"Issuing engine_consensusValidated"; "Issuing engine_consensusValidated";
"status" => ?status, "status" => ?status,
@ -430,7 +427,7 @@ impl ExecutionLayer {
head_block_hash: Hash256, head_block_hash: Hash256,
finalized_block_hash: Hash256, finalized_block_hash: Hash256,
) -> Result<(), Error> { ) -> Result<(), Error> {
info!( debug!(
self.log(), self.log(),
"Issuing engine_forkchoiceUpdated"; "Issuing engine_forkchoiceUpdated";
"finalized_block_hash" => ?finalized_block_hash, "finalized_block_hash" => ?finalized_block_hash,

View File

@ -123,11 +123,13 @@ impl<T: EthSpec> MockExecutionLayer<T> {
assert_eq!(payload.timestamp, timestamp); assert_eq!(payload.timestamp, timestamp);
assert_eq!(payload.random, random); assert_eq!(payload.random, random);
let (payload_response, mut payload_handle) = let (payload_response, payload_handle) = self.el.execute_payload(&payload).await.unwrap();
self.el.execute_payload(&payload).await.unwrap();
assert_eq!(payload_response, ExecutePayloadResponse::Valid); assert_eq!(payload_response, ExecutePayloadResponse::Valid);
payload_handle.publish_async(ConsensusStatus::Valid).await; payload_handle
.unwrap()
.publish_async(ConsensusStatus::Valid)
.await;
self.el self.el
.forkchoice_updated(block_hash, Hash256::zero()) .forkchoice_updated(block_hash, Hash256::zero())