Skip to content

Commit ac7b985

Browse files
Christopher Chongmichaelsproul
authored andcommitted
Add debug fork choice api (sigp#4003)
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 <[email protected]>
1 parent 0014c3f commit ac7b985

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

beacon_node/http_api/tests/tests.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use environment::null_logger;
88
use eth2::{
99
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
1010
reqwest::RequestBuilder,
11-
types::{BlockId as CoreBlockId, StateId as CoreStateId, *},
11+
types::{BlockId as CoreBlockId, ForkChoiceNode, StateId as CoreStateId, *},
1212
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
1313
};
1414
use execution_layer::test_utils::TestingBuilder;
@@ -1679,6 +1679,59 @@ impl ApiTester {
16791679
self
16801680
}
16811681

1682+
pub async fn test_get_debug_fork_choice(self) -> Self {
1683+
let result = self.client.get_debug_fork_choice().await.unwrap();
1684+
1685+
let beacon_fork_choice = self.chain.canonical_head.fork_choice_read_lock();
1686+
1687+
let expected_proto_array = beacon_fork_choice.proto_array().core_proto_array();
1688+
1689+
assert_eq!(
1690+
result.justified_checkpoint,
1691+
expected_proto_array.justified_checkpoint
1692+
);
1693+
assert_eq!(
1694+
result.finalized_checkpoint,
1695+
expected_proto_array.finalized_checkpoint
1696+
);
1697+
1698+
let expected_fork_choice_nodes: Vec<ForkChoiceNode> = expected_proto_array
1699+
.nodes
1700+
.iter()
1701+
.map(|node| {
1702+
let execution_status = if node.execution_status.is_execution_enabled() {
1703+
Some(node.execution_status.to_string())
1704+
} else {
1705+
None
1706+
};
1707+
ForkChoiceNode {
1708+
slot: node.slot,
1709+
block_root: node.root,
1710+
parent_root: node
1711+
.parent
1712+
.and_then(|index| expected_proto_array.nodes.get(index))
1713+
.map(|parent| parent.root),
1714+
justified_epoch: node.justified_checkpoint.map(|checkpoint| checkpoint.epoch),
1715+
finalized_epoch: node.finalized_checkpoint.map(|checkpoint| checkpoint.epoch),
1716+
weight: node.weight,
1717+
validity: execution_status,
1718+
execution_block_hash: node
1719+
.execution_status
1720+
.block_hash()
1721+
.map(|block_hash| block_hash.into_root()),
1722+
}
1723+
})
1724+
.collect();
1725+
1726+
assert_eq!(result.fork_choice_nodes, expected_fork_choice_nodes);
1727+
1728+
// need to drop beacon_fork_choice here, else borrow checker will complain
1729+
// that self cannot be moved out since beacon_fork_choice borrowed self.chain
1730+
// and might still live after self is moved out
1731+
drop(beacon_fork_choice);
1732+
self
1733+
}
1734+
16821735
fn validator_count(&self) -> usize {
16831736
self.chain.head_snapshot().beacon_state.validators().len()
16841737
}
@@ -4148,6 +4201,8 @@ async fn debug_get() {
41484201
.test_get_debug_beacon_states()
41494202
.await
41504203
.test_get_debug_beacon_heads()
4204+
.await
4205+
.test_get_debug_fork_choice()
41514206
.await;
41524207
}
41534208

common/eth2/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,18 @@ impl BeaconNodeHttpClient {
13641364
self.get(path).await
13651365
}
13661366

1367+
/// `GET v1/debug/fork_choice`
1368+
pub async fn get_debug_fork_choice(&self) -> Result<ForkChoice, Error> {
1369+
let mut path = self.eth_path(V1)?;
1370+
1371+
path.path_segments_mut()
1372+
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
1373+
.push("debug")
1374+
.push("fork_choice");
1375+
1376+
self.get(path).await
1377+
}
1378+
13671379
/// `GET validator/duties/proposer/{epoch}`
13681380
pub async fn get_validator_duties_proposer(
13691381
&self,

consensus/proto_array/src/proto_array_fork_choice.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use crate::{
1010
use serde_derive::{Deserialize, Serialize};
1111
use ssz::{Decode, Encode};
1212
use ssz_derive::{Decode, Encode};
13-
use std::collections::{BTreeSet, HashMap};
13+
use std::{
14+
collections::{BTreeSet, HashMap},
15+
fmt,
16+
};
1417
use types::{
1518
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256,
1619
Slot,
@@ -125,6 +128,17 @@ impl ExecutionStatus {
125128
}
126129
}
127130

131+
impl fmt::Display for ExecutionStatus {
132+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133+
match self {
134+
ExecutionStatus::Valid(_) => write!(f, "valid"),
135+
ExecutionStatus::Invalid(_) => write!(f, "invalid"),
136+
ExecutionStatus::Optimistic(_) => write!(f, "optimistic"),
137+
ExecutionStatus::Irrelevant(_) => write!(f, "irrelevant"),
138+
}
139+
}
140+
}
141+
128142
/// A block that is to be applied to the fork choice.
129143
///
130144
/// A simplified version of `types::BeaconBlock`.

0 commit comments

Comments
 (0)