WIP: Furthered attestation production for validator.
This commit is contained in:
parent
eaec5e7b69
commit
5ee1bb20b7
@ -1,12 +1,12 @@
|
|||||||
use super::{success_response, ApiResult};
|
use super::{success_response, ApiResult};
|
||||||
use crate::{helpers::*, ApiError, UrlQuery};
|
use crate::{helpers::*, ApiError, UrlQuery};
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||||
use bls::{PublicKey, Signature};
|
use bls::{AggregateSignature, PublicKey, Signature};
|
||||||
use hyper::{Body, Request};
|
use hyper::{Body, Request};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::beacon_state::EthSpec;
|
use types::beacon_state::EthSpec;
|
||||||
use types::{Epoch, RelativeEpoch, Shard, Slot};
|
use types::{Attestation, BitList, Epoch, RelativeEpoch, Shard, Slot};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ValidatorDuty {
|
pub struct ValidatorDuty {
|
||||||
@ -212,53 +212,130 @@ pub fn get_new_attestation<T: BeaconChainTypes + 'static>(req: Request<Body>) ->
|
|||||||
let _ = beacon_chain
|
let _ = beacon_chain
|
||||||
.ensure_state_caches_are_built()
|
.ensure_state_caches_are_built()
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to build state caches: {:?}", e)))?;
|
.map_err(|e| ApiError::ServerError(format!("Unable to build state caches: {:?}", e)))?;
|
||||||
|
let head_state = &beacon_chain.head().beacon_state;
|
||||||
|
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
let validator: PublicKey = match query.first_of(&["validator_pubkey"]) {
|
let val_pk: PublicKey = match query.first_of(&["validator_pubkey"]) {
|
||||||
Ok((_, v)) => parse_pubkey(v.as_str())?,
|
Ok((_, v)) => parse_pubkey(v.as_str())?,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Get the validator index from the supplied public key
|
||||||
|
// If it does not exist in the index, we cannot continue.
|
||||||
|
let val_index: usize = match head_state.get_validator_index(&val_pk) {
|
||||||
|
Ok(Some(i)) => i,
|
||||||
|
Ok(None) => {
|
||||||
|
return Err(ApiError::InvalidQueryParams(
|
||||||
|
"The provided validator public key does not correspond to a validator index."
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(ApiError::ServerError(format!(
|
||||||
|
"Unable to read validator index cache. {:?}",
|
||||||
|
e
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Get the duties of the validator, to make sure they match up.
|
||||||
|
// If they don't have duties this epoch, then return an error
|
||||||
|
let val_duty = match head_state.get_attestation_duties(val_index, RelativeEpoch::Current) {
|
||||||
|
Ok(Some(d)) => d,
|
||||||
|
Ok(None) => {
|
||||||
|
return Err(ApiError::InvalidQueryParams("No validator duties could be found for the requested validator. Cannot provide valid attestation.".into()));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(ApiError::ServerError(format!(
|
||||||
|
"unable to read cache for attestation duties: {:?}",
|
||||||
|
e
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that we are requesting an attestation during the slot where it is relevant.
|
||||||
|
let present_slot = match beacon_chain.read_slot_clock() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
return Err(ApiError::ServerError(
|
||||||
|
"Beacon node is unable to determine present slot, either the state isn't generated or the chain hasn't begun.".into()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if val_duty.slot != present_slot {
|
||||||
|
return Err(ApiError::InvalidQueryParams(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
|
||||||
let poc_bit: bool = match query.first_of(&["poc_bit"]) {
|
let poc_bit: bool = match query.first_of(&["poc_bit"]) {
|
||||||
Ok((_, v)) => v.parse::<bool>().map_err(|e| ApiError::InvalidQueryParams(format!("poc_bit is not a valid boolean value: {:?}", e)))?,
|
Ok((_, v)) => v.parse::<bool>().map_err(|e| {
|
||||||
|
ApiError::InvalidQueryParams(format!("poc_bit is not a valid boolean value: {:?}", e))
|
||||||
|
})?,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//TODO: this is probably unnecessary if we're always doing it by current slot.
|
let mut aggregation_bits: BitList<bool> = BitList::with_capacity(val_duty.committee_len)
|
||||||
|
.expect("An empty BitList should always be created, or we have bigger problems.")
|
||||||
|
.into();
|
||||||
|
aggregation_bits.set(val_duty.committee_index, poc_bit);
|
||||||
|
|
||||||
|
// Allow a provided slot parameter to check against the expected slot as a sanity check.
|
||||||
|
// Presently, we don't support attestations at future or past slots.
|
||||||
let _slot = match query.first_of(&["slot"]) {
|
let _slot = match query.first_of(&["slot"]) {
|
||||||
Ok((_, v)) => {
|
Ok((_, v)) => {
|
||||||
let requested_slot = v.parse::<u64>().map_err(|e| {
|
let requested_slot = v.parse::<u64>().map_err(|e| {
|
||||||
ApiError::InvalidQueryParams(format!("Invalid slot parameter, must be a u64. {:?}", e))
|
ApiError::InvalidQueryParams(format!(
|
||||||
|
"Invalid slot parameter, must be a u64. {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
let current_slot = beacon_chain.head().beacon_state.slot.as_u64();
|
let current_slot = beacon_chain.head().beacon_state.slot.as_u64();
|
||||||
if requested_slot != current_slot {
|
if requested_slot != current_slot {
|
||||||
return Err(ApiError::InvalidQueryParams(format!("Attestation data can only be requested for the current slot ({:?}), not your requested slot ({:?})", current_slot, requested_slot)));
|
return Err(ApiError::InvalidQueryParams(format!("Attestation data can only be requested for the current slot ({:?}), not your requested slot ({:?})", current_slot, requested_slot)));
|
||||||
}
|
}
|
||||||
Slot::new(requested_slot)
|
Slot::new(requested_slot)
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
}
|
||||||
};
|
Err(ApiError::InvalidQueryParams(_)) => {
|
||||||
let shard: Shard = match query.first_of(&["shard"]) {
|
// Just fill _slot with a dummy value for now, making the slot parameter optional
|
||||||
Ok((_, v)) => v.parse::<u64>().map_err(|e| ApiError::InvalidQueryParams(format!("Shard is not a valid u64 value: {:?}", e)))?,
|
// We'll get the real slot from the ValidatorDuty
|
||||||
|
Slot::new(0)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let shard: Shard = match query.first_of(&["shard"]) {
|
||||||
|
Ok((_, v)) => v.parse::<u64>().map_err(|e| {
|
||||||
|
ApiError::InvalidQueryParams(format!("Shard is not a valid u64 value: {:?}", e))
|
||||||
|
})?,
|
||||||
|
Err(e) => {
|
||||||
|
// This is a mandatory parameter, return the error
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
let attestation_data = match beacon_chain.produce_attestation_data(shard) {
|
let attestation_data = match beacon_chain.produce_attestation_data(shard) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ApiError::ServerError(format!("Could not produce an attestation: {:?}", e)));
|
return Err(ApiError::ServerError(format!(
|
||||||
|
"Could not produce an attestation: {:?}",
|
||||||
|
e
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let attestation = Attestation {
|
||||||
|
aggregation_bits,
|
||||||
|
data: attestation_data,
|
||||||
|
custody_bits: BitList::with_capacity(val_duty.committee_len)
|
||||||
|
.expect("Should be able to create an empty BitList for the custody bits."),
|
||||||
|
signature: AggregateSignature::new(),
|
||||||
|
};
|
||||||
|
|
||||||
//TODO: This is currently AttestationData, but should be IndexedAttestation?
|
//TODO: This is currently AttestationData, but should be IndexedAttestation?
|
||||||
let body = Body::from(
|
let body = Body::from(
|
||||||
serde_json::to_string(&attestation_data)
|
serde_json::to_string(&attestation)
|
||||||
.expect("We should always be able to serialize a new attestation that we produced."),
|
.expect("We should always be able to serialize a new attestation that we produced."),
|
||||||
);
|
);
|
||||||
Ok(success_response(body))
|
Ok(success_response(body))
|
||||||
|
Loading…
Reference in New Issue
Block a user