Add debug fork choice api (#4003)
## Issue Addressed Which issue # does this PR address? https://github.com/sigp/lighthouse/issues/3669 ## Proposed Changes Please list or describe the changes introduced by this PR. - A new API to fetch fork choice data, as specified [here](https://github.com/ethereum/beacon-APIs/pull/232) - A new integration test to test the new API ## Additional Info 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>
This commit is contained in:
parent
d3c20ffa9d
commit
6bb28bc806
@ -30,7 +30,8 @@ use beacon_chain::{
|
|||||||
pub use block_id::BlockId;
|
pub use block_id::BlockId;
|
||||||
use directory::DEFAULT_ROOT_DIR;
|
use directory::DEFAULT_ROOT_DIR;
|
||||||
use eth2::types::{
|
use eth2::types::{
|
||||||
self as api_types, EndpointVersion, SkipRandaoVerification, ValidatorId, ValidatorStatus,
|
self as api_types, EndpointVersion, ForkChoice, ForkChoiceNode, SkipRandaoVerification,
|
||||||
|
ValidatorId, ValidatorStatus,
|
||||||
};
|
};
|
||||||
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
|
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
|
||||||
use lighthouse_version::version_with_platform;
|
use lighthouse_version::version_with_platform;
|
||||||
@ -2148,6 +2149,58 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// GET debug/fork_choice
|
||||||
|
let get_debug_fork_choice = eth_v1
|
||||||
|
.and(warp::path("debug"))
|
||||||
|
.and(warp::path("fork_choice"))
|
||||||
|
.and(warp::path::end())
|
||||||
|
.and(chain_filter.clone())
|
||||||
|
.and_then(|chain: Arc<BeaconChain<T>>| {
|
||||||
|
blocking_json_task(move || {
|
||||||
|
let beacon_fork_choice = chain.canonical_head.fork_choice_read_lock();
|
||||||
|
|
||||||
|
let proto_array = beacon_fork_choice.proto_array().core_proto_array();
|
||||||
|
|
||||||
|
let fork_choice_nodes = 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| 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::<Vec<_>>();
|
||||||
|
Ok(ForkChoice {
|
||||||
|
justified_checkpoint: proto_array.justified_checkpoint,
|
||||||
|
finalized_checkpoint: proto_array.finalized_checkpoint,
|
||||||
|
fork_choice_nodes,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* node
|
* node
|
||||||
*/
|
*/
|
||||||
@ -3676,6 +3729,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.uor(get_config_deposit_contract)
|
.uor(get_config_deposit_contract)
|
||||||
.uor(get_debug_beacon_states)
|
.uor(get_debug_beacon_states)
|
||||||
.uor(get_debug_beacon_heads)
|
.uor(get_debug_beacon_heads)
|
||||||
|
.uor(get_debug_fork_choice)
|
||||||
.uor(get_node_identity)
|
.uor(get_node_identity)
|
||||||
.uor(get_node_version)
|
.uor(get_node_version)
|
||||||
.uor(get_node_syncing)
|
.uor(get_node_syncing)
|
||||||
|
@ -8,7 +8,7 @@ use environment::null_logger;
|
|||||||
use eth2::{
|
use eth2::{
|
||||||
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
|
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
|
||||||
reqwest::RequestBuilder,
|
reqwest::RequestBuilder,
|
||||||
types::{BlockId as CoreBlockId, StateId as CoreStateId, *},
|
types::{BlockId as CoreBlockId, ForkChoiceNode, StateId as CoreStateId, *},
|
||||||
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
|
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
|
||||||
};
|
};
|
||||||
use execution_layer::test_utils::TestingBuilder;
|
use execution_layer::test_utils::TestingBuilder;
|
||||||
@ -1679,6 +1679,59 @@ impl ApiTester {
|
|||||||
self
|
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 {
|
fn validator_count(&self) -> usize {
|
||||||
self.chain.head_snapshot().beacon_state.validators().len()
|
self.chain.head_snapshot().beacon_state.validators().len()
|
||||||
}
|
}
|
||||||
@ -4148,6 +4201,8 @@ async fn debug_get() {
|
|||||||
.test_get_debug_beacon_states()
|
.test_get_debug_beacon_states()
|
||||||
.await
|
.await
|
||||||
.test_get_debug_beacon_heads()
|
.test_get_debug_beacon_heads()
|
||||||
|
.await
|
||||||
|
.test_get_debug_fork_choice()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1334,6 +1334,18 @@ impl BeaconNodeHttpClient {
|
|||||||
self.get(path).await
|
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}`
|
/// `GET validator/duties/proposer/{epoch}`
|
||||||
pub async fn get_validator_duties_proposer(
|
pub async fn get_validator_duties_proposer(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1197,6 +1197,26 @@ pub struct LivenessResponseData {
|
|||||||
pub is_live: bool,
|
pub is_live: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ForkChoice {
|
||||||
|
pub justified_checkpoint: Checkpoint,
|
||||||
|
pub finalized_checkpoint: Checkpoint,
|
||||||
|
pub fork_choice_nodes: Vec<ForkChoiceNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct ForkChoiceNode {
|
||||||
|
pub slot: Slot,
|
||||||
|
pub block_root: Hash256,
|
||||||
|
pub parent_root: Option<Hash256>,
|
||||||
|
pub justified_epoch: Option<Epoch>,
|
||||||
|
pub finalized_epoch: Option<Epoch>,
|
||||||
|
#[serde(with = "eth2_serde_utils::quoted_u64")]
|
||||||
|
pub weight: u64,
|
||||||
|
pub validity: Option<String>,
|
||||||
|
pub execution_block_hash: Option<Hash256>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -10,7 +10,10 @@ use crate::{
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use ssz::{Decode, Encode};
|
use ssz::{Decode, Encode};
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::{
|
||||||
|
collections::{BTreeSet, HashMap},
|
||||||
|
fmt,
|
||||||
|
};
|
||||||
use types::{
|
use types::{
|
||||||
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256,
|
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256,
|
||||||
Slot,
|
Slot,
|
||||||
@ -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 block that is to be applied to the fork choice.
|
||||||
///
|
///
|
||||||
/// A simplified version of `types::BeaconBlock`.
|
/// A simplified version of `types::BeaconBlock`.
|
||||||
|
Loading…
Reference in New Issue
Block a user