2018-11-04 14:35:00 +00:00
|
|
|
use bls::verify_proof_of_possession;
|
2018-12-16 23:00:53 +00:00
|
|
|
use types::{ValidatorRecord, DepositInput, ValidatorStatus, BeaconState};
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
/// The size of a validators deposit in GWei.
|
|
|
|
pub const DEPOSIT_GWEI: u64 = 32_000_000_000;
|
|
|
|
|
|
|
|
/// Inducts validators into a `CrystallizedState`.
|
2018-10-20 10:28:57 +00:00
|
|
|
pub struct ValidatorInductor {
|
2018-10-19 16:11:45 +00:00
|
|
|
pub current_slot: u64,
|
|
|
|
pub shard_count: u16,
|
2018-12-16 05:19:15 +00:00
|
|
|
beacon_state: BeaconState,
|
2018-10-19 16:11:45 +00:00
|
|
|
empty_validator_start: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub enum ValidatorInductionError {
|
|
|
|
InvalidShard,
|
|
|
|
InvaidProofOfPossession,
|
|
|
|
}
|
|
|
|
|
2018-10-20 10:28:57 +00:00
|
|
|
impl ValidatorInductor {
|
2018-12-16 05:19:15 +00:00
|
|
|
pub fn new(current_slot: u64, shard_count: u16, beacon_state: BeaconState) -> Self {
|
2018-10-20 08:18:16 +00:00
|
|
|
Self {
|
|
|
|
current_slot,
|
|
|
|
shard_count,
|
2018-12-16 05:19:15 +00:00
|
|
|
beacon_state,
|
2018-10-20 08:18:16 +00:00
|
|
|
empty_validator_start: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-19 16:11:45 +00:00
|
|
|
/// Attempt to induct a validator into the CrystallizedState.
|
|
|
|
///
|
|
|
|
/// Returns an error if the registration is invalid, otherwise returns the index of the
|
|
|
|
/// validator in `CrystallizedState.validators`.
|
2018-11-04 14:35:00 +00:00
|
|
|
pub fn induct(
|
|
|
|
&mut self,
|
2018-12-16 23:00:53 +00:00
|
|
|
deposit_input: &DepositInput,
|
2018-11-04 14:35:00 +00:00
|
|
|
status: ValidatorStatus,
|
|
|
|
) -> Result<usize, ValidatorInductionError> {
|
2018-12-16 23:00:53 +00:00
|
|
|
let v = self.process_deposit(deposit_input, status)?;
|
2018-10-19 16:11:45 +00:00
|
|
|
Ok(self.add_validator(v))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Verify a `ValidatorRegistration` and return a `ValidatorRecord` if valid.
|
2018-12-16 05:19:15 +00:00
|
|
|
fn process_deposit(
|
2018-11-04 14:35:00 +00:00
|
|
|
&self,
|
2018-12-16 23:00:53 +00:00
|
|
|
deposit_input: &DepositInput,
|
2018-11-04 14:35:00 +00:00
|
|
|
status: ValidatorStatus,
|
|
|
|
) -> Result<ValidatorRecord, ValidatorInductionError> {
|
2018-10-19 16:11:45 +00:00
|
|
|
/*
|
|
|
|
* Ensure withdrawal shard is not too high.
|
|
|
|
*/
|
2018-12-16 05:19:15 +00:00
|
|
|
/*
|
2018-10-19 16:11:45 +00:00
|
|
|
if r.withdrawal_shard > self.shard_count {
|
2018-11-04 14:35:00 +00:00
|
|
|
return Err(ValidatorInductionError::InvalidShard);
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
2018-12-16 05:19:15 +00:00
|
|
|
*/
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Prove validator has knowledge of their secret key.
|
|
|
|
*/
|
2018-12-16 05:19:15 +00:00
|
|
|
if !verify_proof_of_possession(&deposit_input.proof_of_possession, &deposit_input.pubkey) {
|
2018-11-04 14:35:00 +00:00
|
|
|
return Err(ValidatorInductionError::InvaidProofOfPossession);
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(ValidatorRecord {
|
2018-12-16 05:19:15 +00:00
|
|
|
pubkey: deposit_input.pubkey.clone(),
|
|
|
|
withdrawal_credentials: deposit_input.withdrawal_credentials,
|
|
|
|
randao_commitment: deposit_input.randao_commitment,
|
|
|
|
// TODO: revisit this
|
|
|
|
randao_layers: 0,
|
2018-10-19 16:11:45 +00:00
|
|
|
balance: DEPOSIT_GWEI,
|
2018-12-11 23:17:55 +00:00
|
|
|
status: status,
|
2018-12-16 05:19:15 +00:00
|
|
|
latest_status_change_slot: self.beacon_state.validator_registry_latest_change_slot,
|
|
|
|
exit_count: self.beacon_state.validator_registry_exit_count
|
2018-10-19 16:11:45 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the index of the first `ValidatorRecord` in the `CrystallizedState` where
|
|
|
|
/// `validator.status == Withdrawn`. If no such record exists, `None` is returned.
|
2018-11-04 14:35:00 +00:00
|
|
|
fn first_withdrawn_validator(&mut self) -> Option<usize> {
|
2018-12-16 23:00:53 +00:00
|
|
|
for i in self.empty_validator_start..self.beacon_state.validator_registry.len() {
|
|
|
|
if self.beacon_state.validator_registry[i].status == ValidatorStatus::Withdrawn {
|
2018-10-19 16:11:45 +00:00
|
|
|
self.empty_validator_start = i + 1;
|
2018-11-04 14:35:00 +00:00
|
|
|
return Some(i);
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a `ValidatorRecord` to the `CrystallizedState` by replacing first validator where
|
|
|
|
/// `validator.status == Withdraw`. If no such withdrawn validator exists, adds the new
|
|
|
|
/// validator to the end of the list.
|
2018-11-04 14:35:00 +00:00
|
|
|
fn add_validator(&mut self, v: ValidatorRecord) -> usize {
|
2018-10-19 16:11:45 +00:00
|
|
|
match self.first_withdrawn_validator() {
|
|
|
|
Some(i) => {
|
2018-12-16 05:19:15 +00:00
|
|
|
self.beacon_state.validator_registry[i] = v;
|
2018-10-19 16:11:45 +00:00
|
|
|
i
|
|
|
|
}
|
|
|
|
None => {
|
2018-12-16 05:19:15 +00:00
|
|
|
self.beacon_state.validator_registry.push(v);
|
|
|
|
self.beacon_state.validator_registry.len() - 1
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-20 10:01:35 +00:00
|
|
|
|
2018-11-04 14:35:00 +00:00
|
|
|
pub fn to_vec(self) -> Vec<ValidatorRecord> {
|
2018-12-16 05:19:15 +00:00
|
|
|
self.beacon_state.validator_registry
|
2018-10-20 10:01:35 +00:00
|
|
|
}
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2018-11-04 14:35:00 +00:00
|
|
|
use bls::{Keypair, Signature};
|
2018-10-19 16:11:45 +00:00
|
|
|
use hashing::proof_of_possession_hash;
|
2018-12-16 23:00:53 +00:00
|
|
|
use types::{Hash256};
|
2018-11-04 14:35:00 +00:00
|
|
|
|
2018-12-16 05:19:15 +00:00
|
|
|
/*
|
2018-11-04 14:35:00 +00:00
|
|
|
fn registration_equals_record(reg: &ValidatorRegistration, rec: &ValidatorRecord) -> bool {
|
|
|
|
(reg.pubkey == rec.pubkey)
|
|
|
|
& (reg.withdrawal_shard == rec.withdrawal_shard)
|
|
|
|
& (reg.withdrawal_address == rec.withdrawal_address)
|
|
|
|
& (reg.randao_commitment == rec.randao_commitment)
|
|
|
|
& (verify_proof_of_possession(®.proof_of_possession, &rec.pubkey))
|
2018-10-21 18:22:16 +00:00
|
|
|
}
|
2018-12-16 05:19:15 +00:00
|
|
|
*/
|
2018-10-21 18:22:16 +00:00
|
|
|
|
2018-10-19 16:11:45 +00:00
|
|
|
/// Generate a proof of possession for some keypair.
|
|
|
|
fn get_proof_of_possession(kp: &Keypair) -> Signature {
|
|
|
|
let pop_message = proof_of_possession_hash(&kp.pk.as_bytes());
|
|
|
|
Signature::new_hashed(&pop_message, &kp.sk)
|
|
|
|
}
|
|
|
|
|
2018-12-16 23:00:53 +00:00
|
|
|
/// Generate a basic working Deposit for use in tests.
|
|
|
|
fn get_deposit_input() -> DepositInput {
|
2018-10-19 16:11:45 +00:00
|
|
|
let kp = Keypair::random();
|
2018-12-16 23:00:53 +00:00
|
|
|
DepositInput {
|
2018-10-19 16:11:45 +00:00
|
|
|
pubkey: kp.pk.clone(),
|
2018-12-16 23:00:53 +00:00
|
|
|
withdrawal_credentials: Hash256::zero(),
|
2018-10-19 16:11:45 +00:00
|
|
|
randao_commitment: Hash256::zero(),
|
2018-12-16 23:00:53 +00:00
|
|
|
proof_of_possession: get_proof_of_possession(&kp)
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_valid_empty_validators() {
|
2018-12-16 23:00:53 +00:00
|
|
|
let state = BeaconState::default();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
2018-12-16 23:00:53 +00:00
|
|
|
let d = get_deposit_input();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
2018-12-16 23:00:53 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, state);
|
|
|
|
let result = inductor.induct(&d, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
assert_eq!(result.unwrap(), 0);
|
2018-12-16 05:19:15 +00:00
|
|
|
//assert!(registration_equals_record(&r, &validators[0]));
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(validators.len(), 1);
|
|
|
|
}
|
|
|
|
|
2018-12-16 23:00:53 +00:00
|
|
|
/*
|
2018-10-24 08:13:47 +00:00
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_status() {
|
|
|
|
let validators = vec![];
|
|
|
|
|
|
|
|
let r = get_registration();
|
|
|
|
|
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
|
|
|
let _ = inductor.induct(&r, ValidatorStatus::PendingActivation);
|
|
|
|
let _ = inductor.induct(&r, ValidatorStatus::Active);
|
|
|
|
let validators = inductor.to_vec();
|
|
|
|
|
2018-12-11 23:17:55 +00:00
|
|
|
assert!(validators[0].status == ValidatorStatus::PendingActivation);
|
|
|
|
assert!(validators[1].status == ValidatorStatus::Active);
|
2018-10-24 08:13:47 +00:00
|
|
|
assert_eq!(validators.len(), 2);
|
|
|
|
}
|
|
|
|
|
2018-10-19 16:11:45 +00:00
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_valid_all_active_validators() {
|
|
|
|
let mut validators = vec![];
|
|
|
|
for _ in 0..5 {
|
|
|
|
let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
|
2018-12-11 23:17:55 +00:00
|
|
|
v.status = ValidatorStatus::Active;
|
2018-10-19 16:11:45 +00:00
|
|
|
validators.push(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
let r = get_registration();
|
|
|
|
|
2018-10-20 10:28:57 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
2018-10-24 08:13:47 +00:00
|
|
|
let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
assert_eq!(result.unwrap(), 5);
|
2018-12-16 05:19:15 +00:00
|
|
|
//assert!(registration_equals_record(&r, &validators[5]));
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(validators.len(), 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_valid_all_second_validator_withdrawn() {
|
|
|
|
let mut validators = vec![];
|
|
|
|
let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
|
2018-12-11 23:17:55 +00:00
|
|
|
v.status = ValidatorStatus::Active;
|
2018-10-19 16:11:45 +00:00
|
|
|
validators.push(v);
|
|
|
|
for _ in 0..4 {
|
|
|
|
let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
|
2018-12-11 23:17:55 +00:00
|
|
|
v.status = ValidatorStatus::Withdrawn;
|
2018-10-19 16:11:45 +00:00
|
|
|
validators.push(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
let r = get_registration();
|
|
|
|
|
2018-10-20 10:28:57 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
2018-10-24 08:13:47 +00:00
|
|
|
let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
assert_eq!(result.unwrap(), 1);
|
2018-12-16 05:19:15 +00:00
|
|
|
//assert!(registration_equals_record(&r, &validators[1]));
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(validators.len(), 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_valid_all_withdrawn_validators() {
|
|
|
|
let mut validators = vec![];
|
|
|
|
for _ in 0..5 {
|
|
|
|
let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
|
2018-12-11 23:17:55 +00:00
|
|
|
v.status = ValidatorStatus::Withdrawn;
|
2018-10-19 16:11:45 +00:00
|
|
|
validators.push(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure the first validator gets the 0'th slot
|
|
|
|
*/
|
|
|
|
let r = get_registration();
|
2018-10-20 10:28:57 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
2018-10-24 08:13:47 +00:00
|
|
|
let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(result.unwrap(), 0);
|
2018-12-16 05:19:15 +00:00
|
|
|
//assert!(registration_equals_record(&r, &validators[0]));
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure the second validator gets the 1'st slot
|
|
|
|
*/
|
|
|
|
let r_two = get_registration();
|
2018-10-20 10:28:57 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
2018-10-24 08:13:47 +00:00
|
|
|
let result = inductor.induct(&r_two, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(result.unwrap(), 1);
|
2018-12-16 05:19:15 +00:00
|
|
|
//assert!(registration_equals_record(&r_two, &validators[1]));
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(validators.len(), 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_shard_too_high() {
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = vec![];
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
let mut r = get_registration();
|
|
|
|
r.withdrawal_shard = 1025;
|
|
|
|
|
2018-10-20 10:28:57 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
2018-10-24 08:13:47 +00:00
|
|
|
let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
assert_eq!(result, Err(ValidatorInductionError::InvalidShard));
|
|
|
|
assert_eq!(validators.len(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validator_inductor_shard_proof_of_possession_failure() {
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = vec![];
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
let mut r = get_registration();
|
|
|
|
let kp = Keypair::random();
|
|
|
|
r.proof_of_possession = get_proof_of_possession(&kp);
|
|
|
|
|
2018-10-20 10:28:57 +00:00
|
|
|
let mut inductor = ValidatorInductor::new(0, 1024, validators);
|
2018-10-24 08:13:47 +00:00
|
|
|
let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
|
2018-10-20 10:28:57 +00:00
|
|
|
let validators = inductor.to_vec();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
2018-11-04 14:35:00 +00:00
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
Err(ValidatorInductionError::InvaidProofOfPossession)
|
|
|
|
);
|
2018-10-19 16:11:45 +00:00
|
|
|
assert_eq!(validators.len(), 0);
|
|
|
|
}
|
2018-12-16 23:00:53 +00:00
|
|
|
*/
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|