[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:
parent
35350dff75
commit
67a6f91df6
@ -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
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user