2018-12-21 02:22:08 +00:00
|
|
|
use bls::{PublicKey, verify_proof_of_possession};
|
|
|
|
use types::{BeaconState, Deposit, ValidatorRecord, ValidatorStatus};
|
|
|
|
use spec::ChainSpec;
|
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;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub enum ValidatorInductionError {
|
|
|
|
InvalidShard,
|
|
|
|
InvaidProofOfPossession,
|
2018-12-21 02:22:08 +00:00
|
|
|
InvalidWithdrawalCredentials
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
|
2018-12-21 02:22:08 +00:00
|
|
|
pub fn process_deposit(
|
|
|
|
state: &mut BeaconState,
|
|
|
|
deposit: &Deposit,
|
|
|
|
spec: &ChainSpec)
|
|
|
|
-> Result<usize, ValidatorInductionError> {
|
|
|
|
let deposit_input = &deposit.deposit_data.deposit_input;
|
|
|
|
let validator_index = state.validator_registry.iter()
|
|
|
|
.position(|validator| validator.pubkey == deposit_input.pubkey);
|
|
|
|
|
|
|
|
match validator_index {
|
|
|
|
Some(i) => {
|
|
|
|
if state.validator_registry[i].withdrawal_credentials == deposit_input.withdrawal_credentials {
|
|
|
|
state.validator_balances[i] += DEPOSIT_GWEI;
|
|
|
|
return Ok(i);
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
2018-12-21 02:22:08 +00:00
|
|
|
|
|
|
|
Err(ValidatorInductionError::InvalidWithdrawalCredentials)
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
let validator = ValidatorRecord {
|
|
|
|
pubkey: deposit_input.pubkey.clone(),
|
|
|
|
withdrawal_credentials: deposit_input.withdrawal_credentials,
|
|
|
|
randao_commitment: deposit_input.randao_commitment,
|
|
|
|
randao_layers: 0,
|
|
|
|
status: ValidatorStatus::PendingActivation,
|
|
|
|
latest_status_change_slot: state.validator_registry_latest_change_slot.clone(),
|
|
|
|
exit_count: 0
|
|
|
|
};
|
|
|
|
|
|
|
|
match min_empty_validator_index(state, spec) {
|
|
|
|
Some(i) => {
|
|
|
|
state.validator_registry[i] = validator;
|
|
|
|
state.validator_balances[i] = DEPOSIT_GWEI;
|
|
|
|
Ok(i)
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
state.validator_registry.push(validator);
|
|
|
|
state.validator_balances.push(DEPOSIT_GWEI);
|
|
|
|
Ok(state.validator_registry.len() - 1)
|
|
|
|
}
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-21 02:22:08 +00:00
|
|
|
}
|
2018-10-20 10:01:35 +00:00
|
|
|
|
2018-12-21 02:22:08 +00:00
|
|
|
fn min_empty_validator_index(
|
|
|
|
state: &BeaconState,
|
|
|
|
spec: &ChainSpec
|
|
|
|
) -> Option<usize> {
|
|
|
|
for i in 0..state.validator_registry.len() {
|
|
|
|
if state.validator_balances[i] == 0
|
|
|
|
&& state.validator_registry[i].latest_status_change_slot
|
|
|
|
+ spec.zero_balance_validator_ttl <= state.slot {
|
|
|
|
return Some(i);
|
|
|
|
}
|
2018-10-20 10:01:35 +00:00
|
|
|
}
|
2018-12-21 02:22:08 +00:00
|
|
|
None
|
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-21 02:22:08 +00:00
|
|
|
use types::{Hash256, DepositData, DepositInput};
|
|
|
|
|
|
|
|
fn get_deposit() -> Deposit {
|
2018-10-19 16:11:45 +00:00
|
|
|
let kp = Keypair::random();
|
2018-12-21 02:22:08 +00:00
|
|
|
let deposit_input = 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-12-21 02:22:08 +00:00
|
|
|
};
|
|
|
|
let deposit_data = DepositData {
|
|
|
|
deposit_input: deposit_input,
|
|
|
|
value: 0,
|
|
|
|
timestamp: 0
|
|
|
|
};
|
|
|
|
Deposit {
|
|
|
|
merkle_branch: Vec::new(),
|
|
|
|
merkle_tree_index: 0,
|
|
|
|
deposit_data: deposit_data
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-22 00:41:13 +00:00
|
|
|
fn deposit_equals_record(dep: &Deposit, rec: &ValidatorRecord) -> bool {
|
|
|
|
(dep.deposit_data.deposit_input.pubkey == rec.pubkey)
|
|
|
|
& (dep.deposit_data.deposit_input.withdrawal_credentials == rec.withdrawal_credentials)
|
|
|
|
& (dep.deposit_data.deposit_input.randao_commitment == rec.randao_commitment)
|
|
|
|
//& (verify_proof_of_possession(®.proof_of_possession, &rec.pubkey))
|
|
|
|
}
|
|
|
|
|
2018-12-21 02:22:08 +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-10-19 16:11:45 +00:00
|
|
|
#[test]
|
2018-12-22 00:41:13 +00:00
|
|
|
fn test_process_deposit_valid_empty_validators() {
|
2018-12-21 02:22:08 +00:00
|
|
|
let mut state = BeaconState::default();
|
|
|
|
let deposit = get_deposit();
|
|
|
|
let spec = ChainSpec::foundation();
|
2018-10-19 16:11:45 +00:00
|
|
|
|
2018-12-21 02:22:08 +00:00
|
|
|
let result = process_deposit(&mut state, &deposit, &spec);
|
2018-10-19 16:11:45 +00:00
|
|
|
|
|
|
|
assert_eq!(result.unwrap(), 0);
|
2018-12-22 00:41:13 +00:00
|
|
|
assert!(deposit_equals_record(&deposit, &state.validator_registry[0]));
|
|
|
|
assert_eq!(state.validator_registry.len(), 1);
|
|
|
|
assert_eq!(state.validator_balances.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_deposit_empty_validators() {
|
|
|
|
let mut state = BeaconState::default();
|
|
|
|
let deposit = get_deposit();
|
|
|
|
let spec = ChainSpec::foundation();
|
|
|
|
|
|
|
|
let result = process_deposit(&mut state, &deposit, &spec);
|
|
|
|
|
|
|
|
assert_eq!(result.unwrap(), 0);
|
|
|
|
assert!(deposit_equals_record(&deposit, &state.validator_registry[0]));
|
|
|
|
assert_eq!(state.validator_registry.len(), 1);
|
|
|
|
assert_eq!(state.validator_balances.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_deposits_empty_validators() {
|
|
|
|
let mut state = BeaconState::default();
|
|
|
|
let spec = ChainSpec::foundation();
|
|
|
|
|
|
|
|
for i in 0..5 {
|
|
|
|
let deposit = get_deposit();
|
|
|
|
let result = process_deposit(&mut state, &deposit, &spec);
|
|
|
|
assert_eq!(result.unwrap(), i);
|
|
|
|
assert!(deposit_equals_record(&deposit, &state.validator_registry[i]));
|
|
|
|
assert_eq!(state.validator_registry.len(), i + 1);
|
|
|
|
assert_eq!(state.validator_balances.len(), i + 1);
|
|
|
|
}
|
2018-10-19 16:11:45 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|