From 59128f842af60fcc42f9d2a6449ec44e00409d5e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sun, 3 Mar 2019 11:16:59 +1100 Subject: [PATCH] Add `verify_slashable_attestation` spec method As per v0.2.0 spec --- eth2/types/src/beacon_state.rs | 108 +++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 5 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 6dfbf78ee..505d4d9de 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -1,10 +1,6 @@ use self::epoch_cache::EpochCache; use crate::test_utils::TestRandom; -use crate::{ - validator::StatusFlags, validator_registry::get_active_validator_indices, AttestationData, - Bitfield, ChainSpec, Crosslink, Deposit, DepositData, DepositInput, Epoch, Eth1Data, - Eth1DataVote, Fork, Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, -}; +use crate::{validator::StatusFlags, validator_registry::get_active_validator_indices, *}; use bls::verify_proof_of_possession; use honey_badger_split::SplitExt; use log::{debug, error, trace}; @@ -1145,6 +1141,108 @@ impl BeaconState { ) } + pub fn verify_bitfield(&self, bitfield: &Bitfield, committee_size: usize) -> bool { + if bitfield.num_bytes() != ((committee_size + 7) / 8) { + return false; + } + + for i in committee_size..(bitfield.num_bytes() * 8) { + match bitfield.get(i) { + Ok(bit) => { + if bit { + return false; + } + } + Err(_) => unreachable!(), + } + } + + true + } + + pub fn verify_slashable_attestation( + &self, + slashable_attestation: &SlashableAttestation, + spec: &ChainSpec, + ) -> bool { + if slashable_attestation.custody_bitfield.num_set_bits() > 0 { + return false; + } + + if slashable_attestation.validator_indices.is_empty() { + return false; + } + + for i in 0..(slashable_attestation.validator_indices.len() - 1) { + if slashable_attestation.validator_indices[i] + >= slashable_attestation.validator_indices[i + 1] + { + return false; + } + } + + if !self.verify_bitfield( + &slashable_attestation.custody_bitfield, + slashable_attestation.validator_indices.len(), + ) { + return false; + } + + if slashable_attestation.validator_indices.len() + > spec.max_indices_per_slashable_vote as usize + { + return false; + } + + let mut aggregate_pubs = vec![AggregatePublicKey::new(); 2]; + let mut message_exists = vec![false; 2]; + + for (i, v) in slashable_attestation.validator_indices.iter().enumerate() { + let custody_bit = match slashable_attestation.custody_bitfield.get(i) { + Ok(bit) => bit, + Err(_) => unreachable!(), + }; + + message_exists[custody_bit as usize] = true; + + match self.validator_registry.get(*v as usize) { + Some(validator) => { + aggregate_pubs[custody_bit as usize].add(&validator.pubkey); + } + None => return false, + }; + } + + let message_0 = AttestationDataAndCustodyBit { + data: slashable_attestation.data.clone(), + custody_bit: false, + } + .hash_tree_root(); + let message_1 = AttestationDataAndCustodyBit { + data: slashable_attestation.data.clone(), + custody_bit: true, + } + .hash_tree_root(); + + let mut messages = vec![]; + let mut keys = vec![]; + + if message_exists[0] { + messages.push(&message_0[..]); + keys.push(&aggregate_pubs[0]); + } + if message_exists[1] { + messages.push(&message_1[..]); + keys.push(&aggregate_pubs[1]); + } + + slashable_attestation.aggregate_signature.verify_multiple( + &messages[..], + spec.domain_attestation, + &keys[..], + ) + } + /// Return the block root at a recent `slot`. /// /// Spec v0.2.0