Implement liveness BeaconAPI (#4343)
## Issue Addressed #4243 ## Proposed Changes - create a new endpoint for liveness/{endpoint} ## Additional Info This is my first PR.
This commit is contained in:
parent
071dd4cd9c
commit
85a3340d0e
@ -3301,6 +3301,45 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
},
|
||||
);
|
||||
|
||||
// POST vaidator/liveness/{epoch}
|
||||
let post_validator_liveness_epoch = eth_v1
|
||||
.and(warp::path("validator"))
|
||||
.and(warp::path("liveness"))
|
||||
.and(warp::path::param::<Epoch>())
|
||||
.and(warp::path::end())
|
||||
.and(warp::body::json())
|
||||
.and(chain_filter.clone())
|
||||
.and_then(
|
||||
|epoch: Epoch, indices: Vec<u64>, chain: Arc<BeaconChain<T>>| {
|
||||
blocking_json_task(move || {
|
||||
// Ensure the request is for either the current, previous or next epoch.
|
||||
let current_epoch = chain
|
||||
.epoch()
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
let prev_epoch = current_epoch.saturating_sub(Epoch::new(1));
|
||||
let next_epoch = current_epoch.saturating_add(Epoch::new(1));
|
||||
|
||||
if epoch < prev_epoch || epoch > next_epoch {
|
||||
return Err(warp_utils::reject::custom_bad_request(format!(
|
||||
"request epoch {} is more than one epoch from the current epoch {}",
|
||||
epoch, current_epoch
|
||||
)));
|
||||
}
|
||||
|
||||
let liveness: Vec<api_types::StandardLivenessResponseData> = indices
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|index| {
|
||||
let is_live = chain.validator_seen_at_epoch(index as usize, epoch);
|
||||
api_types::StandardLivenessResponseData { index, is_live }
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(api_types::GenericResponse::from(liveness))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// POST lighthouse/liveness
|
||||
let post_lighthouse_liveness = warp::path("lighthouse")
|
||||
.and(warp::path("liveness"))
|
||||
@ -3963,6 +4002,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.uor(post_validator_sync_committee_subscriptions)
|
||||
.uor(post_validator_prepare_beacon_proposer)
|
||||
.uor(post_validator_register_validator)
|
||||
.uor(post_validator_liveness_epoch)
|
||||
.uor(post_lighthouse_liveness)
|
||||
.uor(post_lighthouse_database_reconstruct)
|
||||
.uor(post_lighthouse_database_historical_blocks)
|
||||
|
@ -2980,6 +2980,69 @@ impl ApiTester {
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_post_validator_liveness_epoch(self) -> Self {
|
||||
let epoch = self.chain.epoch().unwrap();
|
||||
let head_state = self.chain.head_beacon_state_cloned();
|
||||
let indices = (0..head_state.validators().len())
|
||||
.map(|i| i as u64)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Construct the expected response
|
||||
let expected: Vec<StandardLivenessResponseData> = head_state
|
||||
.validators()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| StandardLivenessResponseData {
|
||||
index: index as u64,
|
||||
is_live: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let result = self
|
||||
.client
|
||||
.post_validator_liveness_epoch(epoch, indices.clone())
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
|
||||
assert_eq!(result, expected);
|
||||
|
||||
// Attest to the current slot
|
||||
self.client
|
||||
.post_beacon_pool_attestations(self.attestations.as_slice())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let result = self
|
||||
.client
|
||||
.post_validator_liveness_epoch(epoch, indices.clone())
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
|
||||
let committees = head_state
|
||||
.get_beacon_committees_at_slot(self.chain.slot().unwrap())
|
||||
.unwrap();
|
||||
let attesting_validators: Vec<usize> = committees
|
||||
.into_iter()
|
||||
.flat_map(|committee| committee.committee.iter().cloned())
|
||||
.collect();
|
||||
// All attesters should now be considered live
|
||||
let expected = expected
|
||||
.into_iter()
|
||||
.map(|mut a| {
|
||||
if attesting_validators.contains(&(a.index as usize)) {
|
||||
a.is_live = true;
|
||||
}
|
||||
a
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(result, expected);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// Helper function for tests that require a valid RANDAO signature.
|
||||
async fn get_test_randao(&self, slot: Slot, epoch: Epoch) -> (u64, SignatureBytes) {
|
||||
let fork = self.chain.canonical_head.cached_head().head_fork();
|
||||
@ -4870,6 +4933,14 @@ async fn builder_works_post_capella() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn post_validator_liveness_epoch() {
|
||||
ApiTester::new()
|
||||
.await
|
||||
.test_post_validator_liveness_epoch()
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn lighthouse_endpoints() {
|
||||
ApiTester::new()
|
||||
|
@ -1636,6 +1636,24 @@ impl BeaconNodeHttpClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// `POST validator/liveness/{epoch}`
|
||||
pub async fn post_validator_liveness_epoch(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
indices: Vec<u64>,
|
||||
) -> Result<GenericResponse<Vec<StandardLivenessResponseData>>, Error> {
|
||||
let mut path = self.eth_path(V1)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("liveness")
|
||||
.push(&epoch.to_string());
|
||||
|
||||
self.post_with_timeout_and_response(path, &indices, self.timeouts.liveness)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `POST validator/duties/attester/{epoch}`
|
||||
pub async fn post_validator_duties_attester(
|
||||
&self,
|
||||
|
@ -1225,6 +1225,13 @@ impl FromStr for Accept {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct StandardLivenessResponseData {
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub index: u64,
|
||||
pub is_live: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LivenessRequestData {
|
||||
pub epoch: Epoch,
|
||||
|
Loading…
Reference in New Issue
Block a user