Add validator duties tests

This commit is contained in:
Paul Hauner
2019-11-17 19:48:50 +11:00
parent 501a2559d7
commit b0b2010700
5 changed files with 163 additions and 26 deletions

View File

@@ -55,7 +55,7 @@ pub fn parse_signature(string: &str) -> Result<Signature, ApiError> {
.map_err(|e| ApiError::BadRequest(format!("Unable to parse signature bytes: {:?}", e)))
} else {
Err(ApiError::BadRequest(
"Signature must have a '0x' prefix".to_string(),
"Signature must have a 0x prefix".to_string(),
))
}
}
@@ -73,7 +73,7 @@ pub fn parse_root(string: &str) -> Result<Hash256, ApiError> {
.map_err(|e| ApiError::BadRequest(format!("Unable to parse root: {:?}", e)))
} else {
Err(ApiError::BadRequest(
"Root must have a '0x' prefix".to_string(),
"Root must have a 0x prefix".to_string(),
))
}
}
@@ -90,7 +90,7 @@ pub fn parse_pubkey(string: &str) -> Result<PublicKey, ApiError> {
Ok(pubkey)
} else {
Err(ApiError::BadRequest(
"Public key must have a '0x' prefix".to_string(),
"Public key must have a 0x prefix".to_string(),
))
}
}

View File

@@ -7,7 +7,7 @@ use crate::{ApiError, ApiResult, BoxFut, NetworkChannel, UrlQuery};
use beacon_chain::{
AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BlockProcessingOutcome,
};
use bls::{AggregateSignature, PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE};
use bls::{AggregateSignature, PublicKey};
use futures::future::Future;
use futures::stream::Stream;
use hyper::{Body, Request};
@@ -29,18 +29,6 @@ pub struct ValidatorDuty {
pub block_proposal_slot: Option<Slot>,
}
impl ValidatorDuty {
pub fn new() -> ValidatorDuty {
ValidatorDuty {
validator_pubkey: PublicKey::from_bytes(vec![0; BLS_PUBLIC_KEY_BYTE_SIZE].as_slice())
.expect("Should always be able to create a 'zero' BLS public key."),
attestation_slot: None,
attestation_shard: None,
block_proposal_slot: None,
}
}
}
/// HTTP Handler to retrieve a the duties for a set of validators during a particular epoch
pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
req: Request<Body>,
@@ -103,8 +91,12 @@ pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
// Look up duties for each validator
for val_pk in validators {
let mut duty = ValidatorDuty::new();
duty.validator_pubkey = val_pk.clone();
let mut duty = ValidatorDuty {
validator_pubkey: val_pk.clone(),
attestation_slot: None,
attestation_shard: 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.
@@ -131,7 +123,7 @@ pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
Ok(None) => {}
Err(e) => {
return Err(ApiError::ServerError(format!(
"unable to read cache for attestation duties: {:?}",
"Unable to read cache for attestation duties: {:?}",
e
)))
}
@@ -150,6 +142,7 @@ pub fn get_validator_duties<T: BeaconChainTypes + 'static>(
duties.append(&mut vec![duty]);
}
ResponseBuilder::new(&req)?.body_no_ssz(&duties)
}

View File

@@ -9,8 +9,8 @@ use remote_beacon_node::BeaconBlockPublishStatus;
use std::sync::Arc;
use tree_hash::{SignedRoot, TreeHash};
use types::{
test_utils::generate_deterministic_keypair, BeaconBlock, ChainSpec, Domain, EthSpec,
MinimalEthSpec, Signature, Slot,
test_utils::generate_deterministic_keypair, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec,
MinimalEthSpec, RelativeEpoch, Signature, Slot,
};
type E = MinimalEthSpec;
@@ -60,6 +60,93 @@ fn sign_block<T: BeaconChainTypes>(
block.signature = Signature::new(&message, domain, &keypair.sk);
}
#[test]
fn validator_duties() {
let mut env = build_env();
let spec = &E::default_spec();
let node = LocalBeaconNode::production(env.core_context());
let remote_node = node.remote_node().expect("should produce remote node");
let beacon_chain = node
.client
.beacon_chain()
.expect("client should have beacon chain");
let epoch = Epoch::new(0);
let validators = beacon_chain
.head()
.beacon_state
.validators
.iter()
.map(|v| v.pubkey.clone())
.collect::<Vec<_>>();
let duties = env
.runtime()
.block_on(remote_node.http.validator().get_duties(epoch, &validators))
.expect("should fetch block from http api");
assert_eq!(
validators.len(),
duties.len(),
"there should be a duty for each validator"
);
let state = beacon_chain.head().beacon_state.clone();
validators
.iter()
.zip(duties.iter())
.for_each(|(validator, duty)| {
assert_eq!(*validator, duty.validator_pubkey, "pubkey should match");
let validator_index = state
.get_validator_index(validator)
.expect("should have pubkey cache")
.expect("pubkey should exist");
let attestation_duty = state
.get_attestation_duties(validator_index, RelativeEpoch::Current)
.expect("should have attestation duties cache")
.expect("should have attestation duties");
assert_eq!(
Some(attestation_duty.slot),
duty.attestation_slot,
"attestation slot should match"
);
assert_eq!(
Some(attestation_duty.shard),
duty.attestation_shard,
"attestation shard should match"
);
if let Some(slot) = duty.block_proposal_slot {
let expected_proposer = state
.get_beacon_proposer_index(slot, RelativeEpoch::Current, spec)
.expect("should know proposer");
assert_eq!(
expected_proposer, validator_index,
"should get correct proposal slot"
);
} else {
epoch.slot_iter(E::slots_per_epoch()).for_each(|slot| {
let slot_proposer = state
.get_beacon_proposer_index(slot, RelativeEpoch::Current, spec)
.expect("should know proposer");
assert!(
slot_proposer != validator_index,
"validator should not have proposal slot in this epoch"
)
})
}
});
}
#[test]
fn validator_block_post() {
let mut env = build_env();