mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-05 05:44:30 +00:00
More testing for api
This commit is contained in:
@@ -15,10 +15,10 @@ use std::sync::Arc;
|
|||||||
use store::{iter::AncestorIter, Store};
|
use store::{iter::AncestorIter, Store};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, BeaconBlock, BeaconState, EthSpec, Hash256, RelativeEpoch, Signature, Slot,
|
Attestation, BeaconBlock, BeaconState, Epoch, EthSpec, Hash256, RelativeEpoch, Signature, Slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parse a slot from a `0x` preixed string.
|
/// Parse a slot.
|
||||||
///
|
///
|
||||||
/// E.g., `"1234"`
|
/// E.g., `"1234"`
|
||||||
pub fn parse_slot(string: &str) -> Result<Slot, ApiError> {
|
pub fn parse_slot(string: &str) -> Result<Slot, ApiError> {
|
||||||
@@ -28,6 +28,16 @@ pub fn parse_slot(string: &str) -> Result<Slot, ApiError> {
|
|||||||
.map_err(|e| ApiError::BadRequest(format!("Unable to parse slot: {:?}", e)))
|
.map_err(|e| ApiError::BadRequest(format!("Unable to parse slot: {:?}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse an epoch.
|
||||||
|
///
|
||||||
|
/// E.g., `"13"`
|
||||||
|
pub fn parse_epoch(string: &str) -> Result<Epoch, ApiError> {
|
||||||
|
string
|
||||||
|
.parse::<u64>()
|
||||||
|
.map(Epoch::from)
|
||||||
|
.map_err(|e| ApiError::BadRequest(format!("Unable to parse epoch: {:?}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks the provided request to ensure that the `content-type` header.
|
/// Checks the provided request to ensure that the `content-type` header.
|
||||||
///
|
///
|
||||||
/// The content-type header should either be omitted, in which case JSON is assumed, or it should
|
/// The content-type header should either be omitted, in which case JSON is assumed, or it should
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::helpers::{
|
use crate::helpers::{
|
||||||
check_content_type_for_json, parse_pubkey, parse_signature, publish_attestation_to_network,
|
check_content_type_for_json, parse_epoch, parse_pubkey, parse_signature,
|
||||||
publish_beacon_block_to_network,
|
publish_attestation_to_network, publish_beacon_block_to_network,
|
||||||
};
|
};
|
||||||
use crate::response_builder::ResponseBuilder;
|
use crate::response_builder::ResponseBuilder;
|
||||||
use crate::{ApiError, ApiResult, BoxFut, NetworkChannel, UrlQuery};
|
use crate::{ApiError, ApiResult, BoxFut, NetworkChannel, UrlQuery};
|
||||||
@@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use slog::{info, trace, warn, Logger};
|
use slog::{info, trace, warn, Logger};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::beacon_state::EthSpec;
|
use types::beacon_state::EthSpec;
|
||||||
use types::{Attestation, BeaconBlock, BitList, Epoch, RelativeEpoch, Shard, Slot};
|
use types::{Attestation, BeaconBlock, BitList, RelativeEpoch, Shard, Slot};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ValidatorDuty {
|
pub struct ValidatorDuty {
|
||||||
@@ -30,56 +30,46 @@ pub struct ValidatorDuty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to retrieve a the duties for a set of validators during a particular epoch
|
/// HTTP Handler to retrieve a the duties for a set of validators during a particular epoch
|
||||||
|
///
|
||||||
|
/// The given `epoch` must be within one epoch of the current epoch.
|
||||||
pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
|
pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
beacon_chain: Arc<BeaconChain<T>>,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
) -> ApiResult {
|
) -> ApiResult {
|
||||||
slog::trace!(log, "Validator duties requested of API: {:?}", &req);
|
slog::trace!(log, "Validator duties requested of API: {:?}", &req);
|
||||||
|
|
||||||
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
|
let epoch = query
|
||||||
|
.first_of(&["epoch"])
|
||||||
|
.and_then(|(_key, value)| parse_epoch(&value))?;
|
||||||
|
|
||||||
let mut head_state = beacon_chain.head().beacon_state;
|
let mut head_state = beacon_chain.head().beacon_state;
|
||||||
|
|
||||||
slog::trace!(log, "Got head state from request.");
|
|
||||||
// Parse and check query parameters
|
|
||||||
let query = UrlQuery::from_request(&req)?;
|
|
||||||
let current_epoch = head_state.current_epoch();
|
let current_epoch = head_state.current_epoch();
|
||||||
let epoch = match query.first_of(&["epoch"]) {
|
let relative_epoch = RelativeEpoch::from_epoch(current_epoch, epoch).map_err(|_| {
|
||||||
Ok((_, v)) => {
|
|
||||||
slog::trace!(log, "Requested epoch {:?}", v);
|
|
||||||
Epoch::new(v.parse::<u64>().map_err(|e| {
|
|
||||||
slog::info!(log, "Invalid epoch {:?}", e);
|
|
||||||
ApiError::BadRequest(format!("Invalid epoch parameter, must be a u64. {:?}", e))
|
|
||||||
})?)
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// epoch not supplied, use the current epoch
|
|
||||||
slog::info!(log, "Using default epoch {:?}", current_epoch);
|
|
||||||
current_epoch
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let relative_epoch = RelativeEpoch::from_epoch(current_epoch, epoch).map_err(|e| {
|
|
||||||
slog::info!(log, "Requested epoch out of range.");
|
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Cannot get RelativeEpoch, epoch out of range: {:?}",
|
"Epoch must be within one epoch of the current epoch",
|
||||||
e
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let validators: Vec<PublicKey> = query
|
|
||||||
.all_of("validator_pubkeys")?
|
|
||||||
.iter()
|
|
||||||
.map(|pk| parse_pubkey(pk))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
let mut duties: Vec<ValidatorDuty> = Vec::new();
|
|
||||||
|
|
||||||
// Build cache for the requested epoch
|
|
||||||
head_state
|
head_state
|
||||||
.build_committee_cache(relative_epoch, &beacon_chain.spec)
|
.build_committee_cache(relative_epoch, &beacon_chain.spec)
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to build committee cache: {:?}", e)))?;
|
.map_err(|e| ApiError::ServerError(format!("Unable to build committee cache: {:?}", e)))?;
|
||||||
// Get a list of all validators for this epoch
|
head_state
|
||||||
let validator_proposers: Vec<usize> = epoch
|
.update_pubkey_cache()
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Unable to build pubkey cache: {:?}", e)))?;
|
||||||
|
|
||||||
|
// Get a list of all validators for this epoch.
|
||||||
|
//
|
||||||
|
// Used for quickly determining the slot for a proposer.
|
||||||
|
let validator_proposers: Vec<(usize, Slot)> = epoch
|
||||||
.slot_iter(T::EthSpec::slots_per_epoch())
|
.slot_iter(T::EthSpec::slots_per_epoch())
|
||||||
.map(|slot| {
|
.map(|slot| {
|
||||||
head_state
|
head_state
|
||||||
.get_beacon_proposer_index(slot, relative_epoch, &beacon_chain.spec)
|
.get_beacon_proposer_index(slot, relative_epoch, &beacon_chain.spec)
|
||||||
|
.map(|i| (i, slot))
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::ServerError(format!(
|
ApiError::ServerError(format!(
|
||||||
"Unable to get proposer index for validator: {:?}",
|
"Unable to get proposer index for validator: {:?}",
|
||||||
@@ -87,61 +77,51 @@ pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<usize>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
// Look up duties for each validator
|
let duties = query
|
||||||
for val_pk in validators {
|
.all_of("validator_pubkeys")?
|
||||||
let mut duty = ValidatorDuty {
|
.iter()
|
||||||
validator_pubkey: val_pk.clone(),
|
.map(|string| parse_pubkey(string))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
|
.into_iter()
|
||||||
|
.map(|validator_pubkey| {
|
||||||
|
if let Some(validator_index) = head_state
|
||||||
|
.get_validator_index(&validator_pubkey)
|
||||||
|
.map_err(|e| {
|
||||||
|
ApiError::ServerError(format!("Unable to read pubkey cache: {:?}", e))
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
let duties = head_state
|
||||||
|
.get_attestation_duties(validator_index, relative_epoch)
|
||||||
|
.map_err(|e| {
|
||||||
|
ApiError::ServerError(format!(
|
||||||
|
"Unable to obtain attestation duties: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let block_proposal_slot = validator_proposers
|
||||||
|
.iter()
|
||||||
|
.find(|(i, _slot)| validator_index == *i)
|
||||||
|
.map(|(_i, slot)| *slot);
|
||||||
|
|
||||||
|
Ok(ValidatorDuty {
|
||||||
|
validator_pubkey,
|
||||||
|
attestation_slot: duties.map(|d| d.slot),
|
||||||
|
attestation_shard: duties.map(|d| d.shard),
|
||||||
|
block_proposal_slot,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(ValidatorDuty {
|
||||||
|
validator_pubkey,
|
||||||
attestation_slot: None,
|
attestation_slot: None,
|
||||||
attestation_shard: None,
|
attestation_shard: None,
|
||||||
block_proposal_slot: None,
|
block_proposal_slot: None,
|
||||||
};
|
})
|
||||||
|
|
||||||
// Get the validator index
|
|
||||||
// If it does not exist in the index, just add a null duty and move on.
|
|
||||||
let val_index: usize = match head_state.get_validator_index(&val_pk) {
|
|
||||||
Ok(Some(i)) => i,
|
|
||||||
Ok(None) => {
|
|
||||||
duties.append(&mut vec![duty]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ApiError::ServerError(format!(
|
|
||||||
"Unable to read validator index cache. {:?}",
|
|
||||||
e
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set attestation duties
|
|
||||||
match head_state.get_attestation_duties(val_index, relative_epoch) {
|
|
||||||
Ok(Some(d)) => {
|
|
||||||
duty.attestation_slot = Some(d.slot);
|
|
||||||
duty.attestation_shard = Some(d.shard);
|
|
||||||
}
|
|
||||||
Ok(None) => {}
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ApiError::ServerError(format!(
|
|
||||||
"Unable to read cache for attestation duties: {:?}",
|
|
||||||
e
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the validator is to propose a block, identify the slot
|
|
||||||
if let Some(slot) = validator_proposers.iter().position(|&v| val_index == v) {
|
|
||||||
duty.block_proposal_slot = Some(Slot::new(
|
|
||||||
relative_epoch
|
|
||||||
.into_epoch(current_epoch)
|
|
||||||
.start_slot(T::EthSpec::slots_per_epoch())
|
|
||||||
.as_u64()
|
|
||||||
+ slot as u64,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
duties.append(&mut vec![duty]);
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, ApiError>>()?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&duties)
|
ResponseBuilder::new(&req)?.body_no_ssz(&duties)
|
||||||
}
|
}
|
||||||
@@ -278,9 +258,12 @@ pub fn get_new_attestation<T: BeaconChainTypes + 'static>(
|
|||||||
let present_slot = beacon_chain.slot().map_err(|e| ApiError::ServerError(
|
let present_slot = beacon_chain.slot().map_err(|e| ApiError::ServerError(
|
||||||
format!("Beacon node is unable to determine present slot, either the state isn't generated or the chain hasn't begun. {:?}", e)
|
format!("Beacon node is unable to determine present slot, either the state isn't generated or the chain hasn't begun. {:?}", e)
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
|
/*
|
||||||
if val_duty.slot != present_slot {
|
if val_duty.slot != present_slot {
|
||||||
return Err(ApiError::BadRequest(format!("Validator is only able to request an attestation during the slot they are allocated. Current slot: {:?}, allocated slot: {:?}", head_state.slot, val_duty.slot)));
|
return Err(ApiError::BadRequest(format!("Validator is only able to request an attestation during the slot they are allocated. Current slot: {:?}, allocated slot: {:?}", head_state.slot, val_duty.slot)));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Parse the POC bit and insert it into the aggregation bits
|
// Parse the POC bit and insert it into the aggregation bits
|
||||||
let poc_bit = query
|
let poc_bit = query
|
||||||
@@ -291,8 +274,10 @@ pub fn get_new_attestation<T: BeaconChainTypes + 'static>(
|
|||||||
ApiError::BadRequest(format!("Invalid slot parameter, must be a u64. {:?}", e))
|
ApiError::BadRequest(format!("Invalid slot parameter, must be a u64. {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut aggregation_bits = BitList::with_capacity(val_duty.committee_len)
|
let mut aggregation_bits = BitList::with_capacity(val_duty.committee_len).map_err(|e| {
|
||||||
.expect("An empty BitList should always be created, or we have bigger problems.");
|
ApiError::ServerError(format!("Unable to create aggregation bitlist: {:?}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
aggregation_bits
|
aggregation_bits
|
||||||
.set(val_duty.committee_index, poc_bit)
|
.set(val_duty.committee_index, poc_bit)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
|||||||
Reference in New Issue
Block a user