Skip to content

Commit

Permalink
Add debug fork choice api (sigp#4003)
Browse files Browse the repository at this point in the history
Which issue # does this PR address?
sigp#3669

Please list or describe the changes introduced by this PR.
- A new API to fetch fork choice data, as specified [here](ethereum/beacon-APIs#232)
- A new integration test to test the new API

Please provide any additional information. For example, future considerations
or information useful for reviewers.

- `extra_data` field specified in the beacon-API spec is not implemented, please let me know if I should instead.

Co-authored-by: Michael Sproul <micsproul@gmail.com>
  • Loading branch information
2 people authored and Woodpile37 committed Jan 6, 2024
1 parent 0014c3f commit ac7b985
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 2 deletions.
57 changes: 56 additions & 1 deletion beacon_node/http_api/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use environment::null_logger;
use eth2::{
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
reqwest::RequestBuilder,
types::{BlockId as CoreBlockId, StateId as CoreStateId, *},
types::{BlockId as CoreBlockId, ForkChoiceNode, StateId as CoreStateId, *},
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
};
use execution_layer::test_utils::TestingBuilder;
Expand Down Expand Up @@ -1679,6 +1679,59 @@ impl ApiTester {
self
}

pub async fn test_get_debug_fork_choice(self) -> Self {
let result = self.client.get_debug_fork_choice().await.unwrap();

let beacon_fork_choice = self.chain.canonical_head.fork_choice_read_lock();

let expected_proto_array = beacon_fork_choice.proto_array().core_proto_array();

assert_eq!(
result.justified_checkpoint,
expected_proto_array.justified_checkpoint
);
assert_eq!(
result.finalized_checkpoint,
expected_proto_array.finalized_checkpoint
);

let expected_fork_choice_nodes: Vec<ForkChoiceNode> = expected_proto_array
.nodes
.iter()
.map(|node| {
let execution_status = if node.execution_status.is_execution_enabled() {
Some(node.execution_status.to_string())
} else {
None
};
ForkChoiceNode {
slot: node.slot,
block_root: node.root,
parent_root: node
.parent
.and_then(|index| expected_proto_array.nodes.get(index))
.map(|parent| parent.root),
justified_epoch: node.justified_checkpoint.map(|checkpoint| checkpoint.epoch),
finalized_epoch: node.finalized_checkpoint.map(|checkpoint| checkpoint.epoch),
weight: node.weight,
validity: execution_status,
execution_block_hash: node
.execution_status
.block_hash()
.map(|block_hash| block_hash.into_root()),
}
})
.collect();

assert_eq!(result.fork_choice_nodes, expected_fork_choice_nodes);

// need to drop beacon_fork_choice here, else borrow checker will complain
// that self cannot be moved out since beacon_fork_choice borrowed self.chain
// and might still live after self is moved out
drop(beacon_fork_choice);
self
}

fn validator_count(&self) -> usize {
self.chain.head_snapshot().beacon_state.validators().len()
}
Expand Down Expand Up @@ -4148,6 +4201,8 @@ async fn debug_get() {
.test_get_debug_beacon_states()
.await
.test_get_debug_beacon_heads()
.await
.test_get_debug_fork_choice()
.await;
}

Expand Down
12 changes: 12 additions & 0 deletions common/eth2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,18 @@ impl BeaconNodeHttpClient {
self.get(path).await
}

/// `GET v1/debug/fork_choice`
pub async fn get_debug_fork_choice(&self) -> Result<ForkChoice, Error> {
let mut path = self.eth_path(V1)?;

path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("debug")
.push("fork_choice");

self.get(path).await
}

/// `GET validator/duties/proposer/{epoch}`
pub async fn get_validator_duties_proposer(
&self,
Expand Down
16 changes: 15 additions & 1 deletion consensus/proto_array/src/proto_array_fork_choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
use serde_derive::{Deserialize, Serialize};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use std::collections::{BTreeSet, HashMap};
use std::{
collections::{BTreeSet, HashMap},
fmt,
};
use types::{
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256,
Slot,
Expand Down Expand Up @@ -125,6 +128,17 @@ impl ExecutionStatus {
}
}

impl fmt::Display for ExecutionStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExecutionStatus::Valid(_) => write!(f, "valid"),
ExecutionStatus::Invalid(_) => write!(f, "invalid"),
ExecutionStatus::Optimistic(_) => write!(f, "optimistic"),
ExecutionStatus::Irrelevant(_) => write!(f, "irrelevant"),
}
}
}

/// A block that is to be applied to the fork choice.
///
/// A simplified version of `types::BeaconBlock`.
Expand Down

0 comments on commit ac7b985

Please sign in to comment.