More testing for api

This commit is contained in:
Paul Hauner
2019-11-20 18:33:21 +11:00
parent ad40c4ac47
commit b60836be7c
2 changed files with 84 additions and 89 deletions

View File

@@ -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

View File

@@ -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))
attestation_slot: None, .collect::<Result<Vec<_>, _>>()?
attestation_shard: None, .into_iter()
block_proposal_slot: None, .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
))
})?;
// Get the validator index let block_proposal_slot = validator_proposers
// If it does not exist in the index, just add a null duty and move on. .iter()
let val_index: usize = match head_state.get_validator_index(&val_pk) { .find(|(i, _slot)| validator_index == *i)
Ok(Some(i)) => i, .map(|(_i, slot)| *slot);
Ok(None) => {
duties.append(&mut vec![duty]); Ok(ValidatorDuty {
continue; 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_shard: None,
block_proposal_slot: None,
})
} }
Err(e) => { })
return Err(ApiError::ServerError(format!( .collect::<Result<Vec<_>, ApiError>>()?;
"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]);
}
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| {