Don't panic in forkchoiceUpdated handler (#3165)

## Issue Addressed

Fix a panic due to misuse of the Tokio executor when processing a forkchoiceUpdated response. We were previously calling `process_invalid_execution_payload` from the async function `update_execution_engine_forkchoice_async`, which resulted in a panic because `process_invalid_execution_payload` contains a call to fork choice, which ultimately calls `block_on`.

An example backtrace can be found here: https://gist.github.com/michaelsproul/ac5da03e203d6ffac672423eaf52fb20

## Proposed Changes

Wrap the call to `process_invalid_execution_payload` in a `spawn_blocking` so that `block_on` is no longer called from an async context.

## Additional Info

- I've been thinking about how to catch bugs like this with static analysis (a new Clippy lint).
- The payload validation tests have been re-worked to support distinct responses from the mock EE for newPayload and forkchoiceUpdated. Three new tests have been added covering the `Invalid`, `InvalidBlockHash` and `InvalidTerminalBlock` cases.
- I think we need a bunch more tests of different legal and illegal variations
This commit is contained in:
Michael Sproul
2022-05-04 23:30:34 +00:00
parent 10795f1c86
commit ae47a93c42
9 changed files with 365 additions and 132 deletions

View File

@@ -72,6 +72,7 @@ use state_processing::{
use std::borrow::Cow;
use std::fs;
use std::io::Write;
use std::sync::Arc;
use std::time::Duration;
use store::{Error as DBError, HotColdDB, HotStateSummary, KeyValueStore, StoreOp};
use tree_hash::TreeHash;
@@ -577,7 +578,7 @@ pub struct FullyVerifiedBlock<'a, T: BeaconChainTypes> {
pub trait IntoFullyVerifiedBlock<T: BeaconChainTypes>: Sized {
fn into_fully_verified_block(
self,
chain: &BeaconChain<T>,
chain: &Arc<BeaconChain<T>>,
) -> Result<FullyVerifiedBlock<T>, BlockError<T::EthSpec>> {
self.into_fully_verified_block_slashable(chain)
.map(|fully_verified| {
@@ -593,7 +594,7 @@ pub trait IntoFullyVerifiedBlock<T: BeaconChainTypes>: Sized {
/// Convert the block to fully-verified form while producing data to aid checking slashability.
fn into_fully_verified_block_slashable(
self,
chain: &BeaconChain<T>,
chain: &Arc<BeaconChain<T>>,
) -> Result<FullyVerifiedBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>>;
fn block(&self) -> &SignedBeaconBlock<T::EthSpec>;
@@ -828,7 +829,7 @@ impl<T: BeaconChainTypes> IntoFullyVerifiedBlock<T> for GossipVerifiedBlock<T> {
/// Completes verification of the wrapped `block`.
fn into_fully_verified_block_slashable(
self,
chain: &BeaconChain<T>,
chain: &Arc<BeaconChain<T>>,
) -> Result<FullyVerifiedBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>> {
let fully_verified =
SignatureVerifiedBlock::from_gossip_verified_block_check_slashable(self, chain)?;
@@ -948,7 +949,7 @@ impl<T: BeaconChainTypes> IntoFullyVerifiedBlock<T> for SignatureVerifiedBlock<T
/// Completes verification of the wrapped `block`.
fn into_fully_verified_block_slashable(
self,
chain: &BeaconChain<T>,
chain: &Arc<BeaconChain<T>>,
) -> Result<FullyVerifiedBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>> {
let header = self.block.signed_block_header();
let (parent, block) = if let Some(parent) = self.parent {
@@ -977,7 +978,7 @@ impl<T: BeaconChainTypes> IntoFullyVerifiedBlock<T> for SignedBeaconBlock<T::Eth
/// and then using that implementation of `IntoFullyVerifiedBlock` to complete verification.
fn into_fully_verified_block_slashable(
self,
chain: &BeaconChain<T>,
chain: &Arc<BeaconChain<T>>,
) -> Result<FullyVerifiedBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>> {
// Perform an early check to prevent wasting time on irrelevant blocks.
let block_root = check_block_relevancy(&self, None, chain)
@@ -1004,7 +1005,7 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
block: SignedBeaconBlock<T::EthSpec>,
block_root: Hash256,
parent: PreProcessingSnapshot<T::EthSpec>,
chain: &BeaconChain<T>,
chain: &Arc<BeaconChain<T>>,
) -> Result<Self, BlockError<T::EthSpec>> {
if let Some(parent) = chain.fork_choice.read().get_block(&block.parent_root()) {
// Reject any block where the parent has an invalid payload. It's impossible for a valid