Skip to content

Commit

Permalink
Add finalized to HTTP API responses (#3753)
Browse files Browse the repository at this point in the history
## Issue Addressed

#3708 

## Proposed Changes
- Add `is_finalized_block` method to `BeaconChain` in `beacon_node/beacon_chain/src/beacon_chain.rs`.
- Add `is_finalized_state` method to `BeaconChain` in `beacon_node/beacon_chain/src/beacon_chain.rs`.
- Add `fork_and_execution_optimistic_and_finalized` in `beacon_node/http_api/src/state_id.rs`.
- Add `ExecutionOptimisticFinalizedForkVersionedResponse` type in `consensus/types/src/fork_versioned_response.rs`.
- Add `execution_optimistic_finalized_fork_versioned_response`function in  `beacon_node/http_api/src/version.rs`.
- Add `ExecutionOptimisticFinalizedResponse` type in `common/eth2/src/types.rs`.
- Add `add_execution_optimistic_finalized` method in  `common/eth2/src/types.rs`.
- Update API response methods to include finalized.
- Remove `execution_optimistic_fork_versioned_response`

Co-authored-by: Michael Sproul <michael@sigmaprime.io>
  • Loading branch information
danielrachi1 and michaelsproul committed Mar 30, 2023
1 parent 12205a8 commit 036b797
Show file tree
Hide file tree
Showing 16 changed files with 1,118 additions and 571 deletions.
741 changes: 402 additions & 339 deletions Cargo.lock

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,46 @@ pub struct BeaconChain<T: BeaconChainTypes> {
type BeaconBlockAndState<T, Payload> = (BeaconBlock<T, Payload>, BeaconState<T>);

impl<T: BeaconChainTypes> BeaconChain<T> {
/// Checks if a block is finalized.
/// The finalization check is done with the block slot. The block root is used to verify that
/// the finalized slot is in the canonical chain.
pub fn is_finalized_block(
&self,
block_root: &Hash256,
block_slot: Slot,
) -> Result<bool, Error> {
let finalized_slot = self
.canonical_head
.cached_head()
.finalized_checkpoint()
.epoch
.start_slot(T::EthSpec::slots_per_epoch());
let is_canonical = self
.block_root_at_slot(block_slot, WhenSlotSkipped::None)?
.map_or(false, |canonical_root| block_root == &canonical_root);
Ok(block_slot <= finalized_slot && is_canonical)
}

/// Checks if a state is finalized.
/// The finalization check is done with the slot. The state root is used to verify that
/// the finalized state is in the canonical chain.
pub fn is_finalized_state(
&self,
state_root: &Hash256,
state_slot: Slot,
) -> Result<bool, Error> {
let finalized_slot = self
.canonical_head
.cached_head()
.finalized_checkpoint()
.epoch
.start_slot(T::EthSpec::slots_per_epoch());
let is_canonical = self
.state_root_at_slot(state_slot)?
.map_or(false, |canonical_root| state_root == &canonical_root);
Ok(state_slot <= finalized_slot && is_canonical)
}

/// Persists the head tracker and fork choice.
///
/// We do it atomically even though no guarantees need to be made about blocks from
Expand Down
6 changes: 4 additions & 2 deletions beacon_node/http_api/src/attester_duties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@ fn compute_historic_attester_duties<T: BeaconChainTypes>(
)?;
(state, execution_optimistic)
} else {
StateId::from_slot(request_epoch.start_slot(T::EthSpec::slots_per_epoch()))
.state(chain)?
let (state, execution_optimistic, _finalized) =
StateId::from_slot(request_epoch.start_slot(T::EthSpec::slots_per_epoch()))
.state(chain)?;
(state, execution_optimistic)
};

// Sanity-check the state lookup.
Expand Down
73 changes: 56 additions & 17 deletions beacon_node/http_api/src/block_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use eth2::types::BlockId as CoreBlockId;
use std::fmt;
use std::str::FromStr;
use std::sync::Arc;
use types::{Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot};
use types::{EthSpec, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot};

/// Wraps `eth2::types::BlockId` and provides a simple way to obtain a block or root for a given
/// `BlockId`.
#[derive(Debug)]
pub struct BlockId(pub CoreBlockId);

type Finalized = bool;

impl BlockId {
pub fn from_slot(slot: Slot) -> Self {
Self(CoreBlockId::Slot(slot))
Expand All @@ -24,7 +26,7 @@ impl BlockId {
pub fn root<T: BeaconChainTypes>(
&self,
chain: &BeaconChain<T>,
) -> Result<(Hash256, ExecutionOptimistic), warp::Rejection> {
) -> Result<(Hash256, ExecutionOptimistic, Finalized), warp::Rejection> {
match &self.0 {
CoreBlockId::Head => {
let (cached_head, execution_status) = chain
Expand All @@ -34,22 +36,23 @@ impl BlockId {
Ok((
cached_head.head_block_root(),
execution_status.is_optimistic_or_invalid(),
false,
))
}
CoreBlockId::Genesis => Ok((chain.genesis_block_root, false)),
CoreBlockId::Genesis => Ok((chain.genesis_block_root, false, true)),
CoreBlockId::Finalized => {
let finalized_checkpoint =
chain.canonical_head.cached_head().finalized_checkpoint();
let (_slot, execution_optimistic) =
checkpoint_slot_and_execution_optimistic(chain, finalized_checkpoint)?;
Ok((finalized_checkpoint.root, execution_optimistic))
Ok((finalized_checkpoint.root, execution_optimistic, true))
}
CoreBlockId::Justified => {
let justified_checkpoint =
chain.canonical_head.cached_head().justified_checkpoint();
let (_slot, execution_optimistic) =
checkpoint_slot_and_execution_optimistic(chain, justified_checkpoint)?;
Ok((justified_checkpoint.root, execution_optimistic))
Ok((justified_checkpoint.root, execution_optimistic, false))
}
CoreBlockId::Slot(slot) => {
let execution_optimistic = chain
Expand All @@ -66,7 +69,14 @@ impl BlockId {
))
})
})?;
Ok((root, execution_optimistic))
let finalized = *slot
<= chain
.canonical_head
.cached_head()
.finalized_checkpoint()
.epoch
.start_slot(T::EthSpec::slots_per_epoch());
Ok((root, execution_optimistic, finalized))
}
CoreBlockId::Root(root) => {
// This matches the behaviour of other consensus clients (e.g. Teku).
Expand All @@ -88,7 +98,20 @@ impl BlockId {
.is_optimistic_or_invalid_block(root)
.map_err(BeaconChainError::ForkChoiceError)
.map_err(warp_utils::reject::beacon_chain_error)?;
Ok((*root, execution_optimistic))
let blinded_block = chain
.get_blinded_block(root)
.map_err(warp_utils::reject::beacon_chain_error)?
.ok_or_else(|| {
warp_utils::reject::custom_not_found(format!(
"beacon block with root {}",
root
))
})?;
let block_slot = blinded_block.slot();
let finalized = chain
.is_finalized_block(root, block_slot)
.map_err(warp_utils::reject::beacon_chain_error)?;
Ok((*root, execution_optimistic, finalized))
} else {
Err(warp_utils::reject::custom_not_found(format!(
"beacon block with root {}",
Expand All @@ -103,7 +126,14 @@ impl BlockId {
pub fn blinded_block<T: BeaconChainTypes>(
&self,
chain: &BeaconChain<T>,
) -> Result<(SignedBlindedBeaconBlock<T::EthSpec>, ExecutionOptimistic), warp::Rejection> {
) -> Result<
(
SignedBlindedBeaconBlock<T::EthSpec>,
ExecutionOptimistic,
Finalized,
),
warp::Rejection,
> {
match &self.0 {
CoreBlockId::Head => {
let (cached_head, execution_status) = chain
Expand All @@ -113,10 +143,11 @@ impl BlockId {
Ok((
cached_head.snapshot.beacon_block.clone_as_blinded(),
execution_status.is_optimistic_or_invalid(),
false,
))
}
CoreBlockId::Slot(slot) => {
let (root, execution_optimistic) = self.root(chain)?;
let (root, execution_optimistic, finalized) = self.root(chain)?;
chain
.get_blinded_block(&root)
.map_err(warp_utils::reject::beacon_chain_error)
Expand All @@ -128,7 +159,7 @@ impl BlockId {
slot
)));
}
Ok((block, execution_optimistic))
Ok((block, execution_optimistic, finalized))
}
None => Err(warp_utils::reject::custom_not_found(format!(
"beacon block with root {}",
Expand All @@ -137,7 +168,7 @@ impl BlockId {
})
}
_ => {
let (root, execution_optimistic) = self.root(chain)?;
let (root, execution_optimistic, finalized) = self.root(chain)?;
let block = chain
.get_blinded_block(&root)
.map_err(warp_utils::reject::beacon_chain_error)
Expand All @@ -149,7 +180,7 @@ impl BlockId {
))
})
})?;
Ok((block, execution_optimistic))
Ok((block, execution_optimistic, finalized))
}
}
}
Expand All @@ -158,7 +189,14 @@ impl BlockId {
pub async fn full_block<T: BeaconChainTypes>(
&self,
chain: &BeaconChain<T>,
) -> Result<(Arc<SignedBeaconBlock<T::EthSpec>>, ExecutionOptimistic), warp::Rejection> {
) -> Result<
(
Arc<SignedBeaconBlock<T::EthSpec>>,
ExecutionOptimistic,
Finalized,
),
warp::Rejection,
> {
match &self.0 {
CoreBlockId::Head => {
let (cached_head, execution_status) = chain
Expand All @@ -168,10 +206,11 @@ impl BlockId {
Ok((
cached_head.snapshot.beacon_block.clone(),
execution_status.is_optimistic_or_invalid(),
false,
))
}
CoreBlockId::Slot(slot) => {
let (root, execution_optimistic) = self.root(chain)?;
let (root, execution_optimistic, finalized) = self.root(chain)?;
chain
.get_block(&root)
.await
Expand All @@ -184,7 +223,7 @@ impl BlockId {
slot
)));
}
Ok((Arc::new(block), execution_optimistic))
Ok((Arc::new(block), execution_optimistic, finalized))
}
None => Err(warp_utils::reject::custom_not_found(format!(
"beacon block with root {}",
Expand All @@ -193,14 +232,14 @@ impl BlockId {
})
}
_ => {
let (root, execution_optimistic) = self.root(chain)?;
let (root, execution_optimistic, finalized) = self.root(chain)?;
chain
.get_block(&root)
.await
.map_err(warp_utils::reject::beacon_chain_error)
.and_then(|block_opt| {
block_opt
.map(|block| (Arc::new(block), execution_optimistic))
.map(|block| (Arc::new(block), execution_optimistic, finalized))
.ok_or_else(|| {
warp_utils::reject::custom_not_found(format!(
"beacon block with root {}",
Expand Down
Loading

0 comments on commit 036b797

Please sign in to comment.