First compiling version of per-block-proc refactor
This commit is contained in:
		
							parent
							
								
									a15ed0acd3
								
							
						
					
					
						commit
						40f74c9b26
					
				| @ -10,7 +10,6 @@ harness = false | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| criterion = "0.2" | ||||
| test_harness = { path = "../../beacon_node/beacon_chain/test_harness" } | ||||
| env_logger = "0.6.0" | ||||
| 
 | ||||
| [dependencies] | ||||
|  | ||||
| @ -1,344 +0,0 @@ | ||||
| use self::verify_slashable_attestation::verify_slashable_attestation; | ||||
| use crate::SlotProcessingError; | ||||
| use hashing::hash; | ||||
| use int_to_bytes::int_to_bytes32; | ||||
| use log::{debug, trace}; | ||||
| use ssz::{ssz_encode, TreeHash}; | ||||
| use types::*; | ||||
| use validate_attestation::validate_attestations; | ||||
| 
 | ||||
| pub use validate_attestation::{validate_attestation, validate_attestation_without_signature}; | ||||
| 
 | ||||
| mod validate_attestation; | ||||
| mod verify_slashable_attestation; | ||||
| 
 | ||||
| const PHASE_0_CUSTODY_BIT: bool = false; | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum Error { | ||||
|     DBError(String), | ||||
|     StateAlreadyTransitioned, | ||||
|     PresentSlotIsNone, | ||||
|     UnableToDecodeBlock, | ||||
|     MissingParentState(Hash256), | ||||
|     InvalidParentState(Hash256), | ||||
|     MissingBeaconBlock(Hash256), | ||||
|     InvalidBeaconBlock(Hash256), | ||||
|     MissingParentBlock(Hash256), | ||||
|     StateSlotMismatch, | ||||
|     BadBlockSignature, | ||||
|     BadRandaoSignature, | ||||
|     MaxProposerSlashingsExceeded, | ||||
|     BadProposerSlashing, | ||||
|     MaxAttesterSlashingsExceed, | ||||
|     MaxAttestationsExceeded, | ||||
|     BadAttesterSlashing, | ||||
|     InvalidAttestation(AttestationValidationError), | ||||
|     NoBlockRoot, | ||||
|     MaxDepositsExceeded, | ||||
|     BadDeposit, | ||||
|     MaxExitsExceeded, | ||||
|     BadExit, | ||||
|     BadCustodyReseeds, | ||||
|     BadCustodyChallenges, | ||||
|     BadCustodyResponses, | ||||
|     BeaconStateError(BeaconStateError), | ||||
|     SlotProcessingError(SlotProcessingError), | ||||
| } | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttestationValidationError { | ||||
|     IncludedTooEarly, | ||||
|     IncludedTooLate, | ||||
|     WrongJustifiedSlot, | ||||
|     WrongJustifiedRoot, | ||||
|     BadLatestCrosslinkRoot, | ||||
|     BadSignature, | ||||
|     ShardBlockRootNotZero, | ||||
|     NoBlockRoot, | ||||
|     BeaconStateError(BeaconStateError), | ||||
| } | ||||
| 
 | ||||
| macro_rules! ensure { | ||||
|     ($condition: expr, $result: expr) => { | ||||
|         if !$condition { | ||||
|             return Err($result); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| pub trait BlockProcessable { | ||||
|     fn per_block_processing(&mut self, block: &BeaconBlock, spec: &ChainSpec) -> Result<(), Error>; | ||||
|     fn per_block_processing_without_verifying_block_signature( | ||||
|         &mut self, | ||||
|         block: &BeaconBlock, | ||||
|         spec: &ChainSpec, | ||||
|     ) -> Result<(), Error>; | ||||
| } | ||||
| 
 | ||||
| impl BlockProcessable for BeaconState { | ||||
|     fn per_block_processing(&mut self, block: &BeaconBlock, spec: &ChainSpec) -> Result<(), Error> { | ||||
|         per_block_processing_signature_optional(self, block, true, spec) | ||||
|     } | ||||
| 
 | ||||
|     fn per_block_processing_without_verifying_block_signature( | ||||
|         &mut self, | ||||
|         block: &BeaconBlock, | ||||
|         spec: &ChainSpec, | ||||
|     ) -> Result<(), Error> { | ||||
|         per_block_processing_signature_optional(self, block, false, spec) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn per_block_processing_signature_optional( | ||||
|     mut state: &mut BeaconState, | ||||
|     block: &BeaconBlock, | ||||
|     verify_block_signature: bool, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     ensure!(block.slot == state.slot, Error::StateSlotMismatch); | ||||
| 
 | ||||
|     // Building the previous epoch could be delayed until an attestation from a previous epoch is
 | ||||
|     // included. This is left for future optimisation.
 | ||||
|     state.build_epoch_cache(RelativeEpoch::Previous, spec)?; | ||||
|     state.build_epoch_cache(RelativeEpoch::Current, spec)?; | ||||
| 
 | ||||
|     /* | ||||
|      * Proposer Signature | ||||
|      */ | ||||
|     let block_proposer_index = state.get_beacon_proposer_index(block.slot, spec)?; | ||||
|     let block_proposer = &state.validator_registry[block_proposer_index]; | ||||
| 
 | ||||
|     if verify_block_signature { | ||||
|         ensure!( | ||||
|             bls_verify( | ||||
|                 &block_proposer.pubkey, | ||||
|                 &block.proposal_root(spec)[..], | ||||
|                 &block.signature, | ||||
|                 get_domain(&state.fork, state.current_epoch(spec), spec.domain_proposal) | ||||
|             ), | ||||
|             Error::BadBlockSignature | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * RANDAO | ||||
|      */ | ||||
|     ensure!( | ||||
|         bls_verify( | ||||
|             &block_proposer.pubkey, | ||||
|             &int_to_bytes32(state.current_epoch(spec).as_u64()), | ||||
|             &block.randao_reveal, | ||||
|             get_domain(&state.fork, state.current_epoch(spec), spec.domain_randao) | ||||
|         ), | ||||
|         Error::BadRandaoSignature | ||||
|     ); | ||||
| 
 | ||||
|     // TODO: check this is correct.
 | ||||
|     let new_mix = { | ||||
|         let mut mix = state.latest_randao_mixes | ||||
|             [state.slot.as_usize() % spec.latest_randao_mixes_length] | ||||
|             .to_vec(); | ||||
|         mix.append(&mut ssz_encode(&block.randao_reveal)); | ||||
|         Hash256::from(&hash(&mix)[..]) | ||||
|     }; | ||||
| 
 | ||||
|     state.latest_randao_mixes[state.slot.as_usize() % spec.latest_randao_mixes_length] = new_mix; | ||||
| 
 | ||||
|     /* | ||||
|      * Eth1 data | ||||
|      */ | ||||
|     // TODO: Eth1 data processing.
 | ||||
| 
 | ||||
|     /* | ||||
|      * Proposer slashings | ||||
|      */ | ||||
|     ensure!( | ||||
|         block.body.proposer_slashings.len() as u64 <= spec.max_proposer_slashings, | ||||
|         Error::MaxProposerSlashingsExceeded | ||||
|     ); | ||||
|     for proposer_slashing in &block.body.proposer_slashings { | ||||
|         let proposer = state | ||||
|             .validator_registry | ||||
|             .get(proposer_slashing.proposer_index as usize) | ||||
|             .ok_or(Error::BadProposerSlashing)?; | ||||
|         ensure!( | ||||
|             proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot, | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         ensure!( | ||||
|             proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard, | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         ensure!( | ||||
|             proposer_slashing.proposal_data_1.block_root | ||||
|                 != proposer_slashing.proposal_data_2.block_root, | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         ensure!( | ||||
|             proposer.penalized_epoch > state.current_epoch(spec), | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         ensure!( | ||||
|             bls_verify( | ||||
|                 &proposer.pubkey, | ||||
|                 &proposer_slashing.proposal_data_1.hash_tree_root(), | ||||
|                 &proposer_slashing.proposal_signature_1, | ||||
|                 get_domain( | ||||
|                     &state.fork, | ||||
|                     proposer_slashing | ||||
|                         .proposal_data_1 | ||||
|                         .slot | ||||
|                         .epoch(spec.slots_per_epoch), | ||||
|                     spec.domain_proposal | ||||
|                 ) | ||||
|             ), | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         ensure!( | ||||
|             bls_verify( | ||||
|                 &proposer.pubkey, | ||||
|                 &proposer_slashing.proposal_data_2.hash_tree_root(), | ||||
|                 &proposer_slashing.proposal_signature_2, | ||||
|                 get_domain( | ||||
|                     &state.fork, | ||||
|                     proposer_slashing | ||||
|                         .proposal_data_2 | ||||
|                         .slot | ||||
|                         .epoch(spec.slots_per_epoch), | ||||
|                     spec.domain_proposal | ||||
|                 ) | ||||
|             ), | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         state.penalize_validator(proposer_slashing.proposer_index as usize, spec)?; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Attester slashings | ||||
|      */ | ||||
|     ensure!( | ||||
|         block.body.attester_slashings.len() as u64 <= spec.max_attester_slashings, | ||||
|         Error::MaxAttesterSlashingsExceed | ||||
|     ); | ||||
|     for attester_slashing in &block.body.attester_slashings { | ||||
|         verify_slashable_attestation(&mut state, &attester_slashing, spec)?; | ||||
|     } | ||||
| 
 | ||||
|     validate_attestations(&mut state, &block, spec); | ||||
| 
 | ||||
|     // Convert each attestation into a `PendingAttestation` and insert into the state.
 | ||||
|     for attestation in &block.body.attestations { | ||||
|         let pending_attestation = PendingAttestation { | ||||
|             data: attestation.data.clone(), | ||||
|             aggregation_bitfield: attestation.aggregation_bitfield.clone(), | ||||
|             custody_bitfield: attestation.custody_bitfield.clone(), | ||||
|             inclusion_slot: state.slot, | ||||
|         }; | ||||
|         state.latest_attestations.push(pending_attestation); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Deposits | ||||
|      */ | ||||
|     ensure!( | ||||
|         block.body.deposits.len() as u64 <= spec.max_deposits, | ||||
|         Error::MaxDepositsExceeded | ||||
|     ); | ||||
| 
 | ||||
|     // TODO: verify deposit merkle branches.
 | ||||
|     for deposit in &block.body.deposits { | ||||
|         debug!( | ||||
|             "Processing deposit for pubkey {:?}", | ||||
|             deposit.deposit_data.deposit_input.pubkey | ||||
|         ); | ||||
|         state | ||||
|             .process_deposit( | ||||
|                 deposit.deposit_data.deposit_input.pubkey.clone(), | ||||
|                 deposit.deposit_data.amount, | ||||
|                 deposit | ||||
|                     .deposit_data | ||||
|                     .deposit_input | ||||
|                     .proof_of_possession | ||||
|                     .clone(), | ||||
|                 deposit.deposit_data.deposit_input.withdrawal_credentials, | ||||
|                 None, | ||||
|                 spec, | ||||
|             ) | ||||
|             .map_err(|_| Error::BadDeposit)?; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Exits | ||||
|      */ | ||||
|     ensure!( | ||||
|         block.body.exits.len() as u64 <= spec.max_exits, | ||||
|         Error::MaxExitsExceeded | ||||
|     ); | ||||
| 
 | ||||
|     for exit in &block.body.exits { | ||||
|         let validator = state | ||||
|             .validator_registry | ||||
|             .get(exit.validator_index as usize) | ||||
|             .ok_or(Error::BadExit)?; | ||||
|         ensure!( | ||||
|             validator.exit_epoch | ||||
|                 > state.get_entry_exit_effect_epoch(state.current_epoch(spec), spec), | ||||
|             Error::BadExit | ||||
|         ); | ||||
|         ensure!(state.current_epoch(spec) >= exit.epoch, Error::BadExit); | ||||
|         let exit_message = { | ||||
|             let exit_struct = VoluntaryExit { | ||||
|                 epoch: exit.epoch, | ||||
|                 validator_index: exit.validator_index, | ||||
|                 signature: spec.empty_signature.clone(), | ||||
|             }; | ||||
|             exit_struct.hash_tree_root() | ||||
|         }; | ||||
|         ensure!( | ||||
|             bls_verify( | ||||
|                 &validator.pubkey, | ||||
|                 &exit_message, | ||||
|                 &exit.signature, | ||||
|                 get_domain(&state.fork, exit.epoch, spec.domain_exit) | ||||
|             ), | ||||
|             Error::BadProposerSlashing | ||||
|         ); | ||||
|         state.initiate_validator_exit(exit.validator_index as usize); | ||||
|     } | ||||
| 
 | ||||
|     debug!("State transition complete."); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn get_domain(fork: &Fork, epoch: Epoch, domain_type: u64) -> u64 { | ||||
|     fork.get_domain(epoch, domain_type) | ||||
| } | ||||
| 
 | ||||
| fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, domain: u64) -> bool { | ||||
|     signature.verify(message, domain, pubkey) | ||||
| } | ||||
| 
 | ||||
| impl From<AttestationValidationError> for Error { | ||||
|     fn from(e: AttestationValidationError) -> Error { | ||||
|         Error::InvalidAttestation(e) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<BeaconStateError> for Error { | ||||
|     fn from(e: BeaconStateError) -> Error { | ||||
|         Error::BeaconStateError(e) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SlotProcessingError> for Error { | ||||
|     fn from(e: SlotProcessingError) -> Error { | ||||
|         Error::SlotProcessingError(e) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<BeaconStateError> for AttestationValidationError { | ||||
|     fn from(e: BeaconStateError) -> AttestationValidationError { | ||||
|         AttestationValidationError::BeaconStateError(e) | ||||
|     } | ||||
| } | ||||
| @ -1,61 +0,0 @@ | ||||
| use super::Error; | ||||
| use types::*; | ||||
| 
 | ||||
| macro_rules! ensure { | ||||
|     ($condition: expr, $result: expr) => { | ||||
|         if !$condition { | ||||
|             return Err($result); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// Returns `Ok(())` if some `AttesterSlashing` is valid to be included in some `BeaconState`,
 | ||||
| /// otherwise returns an `Err`.
 | ||||
| pub fn verify_slashable_attestation( | ||||
|     state: &mut BeaconState, | ||||
|     attester_slashing: &AttesterSlashing, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     let slashable_attestation_1 = &attester_slashing.slashable_attestation_1; | ||||
|     let slashable_attestation_2 = &attester_slashing.slashable_attestation_2; | ||||
| 
 | ||||
|     ensure!( | ||||
|         slashable_attestation_1.data != slashable_attestation_2.data, | ||||
|         Error::BadAttesterSlashing | ||||
|     ); | ||||
|     ensure!( | ||||
|         slashable_attestation_1.is_double_vote(slashable_attestation_2, spec) | ||||
|             | slashable_attestation_1.is_surround_vote(slashable_attestation_2, spec), | ||||
|         Error::BadAttesterSlashing | ||||
|     ); | ||||
|     ensure!( | ||||
|         state.verify_slashable_attestation(&slashable_attestation_1, spec), | ||||
|         Error::BadAttesterSlashing | ||||
|     ); | ||||
|     ensure!( | ||||
|         state.verify_slashable_attestation(&slashable_attestation_2, spec), | ||||
|         Error::BadAttesterSlashing | ||||
|     ); | ||||
| 
 | ||||
|     let mut slashable_indices = vec![]; | ||||
|     for i in &slashable_attestation_1.validator_indices { | ||||
|         let validator = state | ||||
|             .validator_registry | ||||
|             .get(*i as usize) | ||||
|             .ok_or_else(|| Error::BadAttesterSlashing)?; | ||||
| 
 | ||||
|         if slashable_attestation_1.validator_indices.contains(&i) | ||||
|             & !validator.is_penalized_at(state.current_epoch(spec)) | ||||
|         { | ||||
|             slashable_indices.push(i); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ensure!(!slashable_indices.is_empty(), Error::BadAttesterSlashing); | ||||
| 
 | ||||
|     for i in slashable_indices { | ||||
|         state.penalize_validator(*i as usize, spec)?; | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| @ -1,9 +1,98 @@ | ||||
| use types::BeaconStateError; | ||||
| 
 | ||||
| macro_rules! impl_from_beacon_state_error { | ||||
|     ($type: ident) => { | ||||
|         impl From<BeaconStateError> for $type { | ||||
|             fn from(e: BeaconStateError) -> $type { | ||||
|                 $type::BeaconStateError(e) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_into_with_index_with_beacon_error { | ||||
|     ($error_type: ident, $invalid_type: ident) => { | ||||
|         impl IntoWithIndex<BlockProcessingError> for $error_type { | ||||
|             fn into_with_index(self, i: usize) -> BlockProcessingError { | ||||
|                 match self { | ||||
|                     $error_type::Invalid(e) => { | ||||
|                         BlockProcessingError::Invalid(BlockInvalid::$invalid_type(i, e)) | ||||
|                     } | ||||
|                     $error_type::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_into_with_index_without_beacon_error { | ||||
|     ($error_type: ident, $invalid_type: ident) => { | ||||
|         impl IntoWithIndex<BlockProcessingError> for $error_type { | ||||
|             fn into_with_index(self, i: usize) -> BlockProcessingError { | ||||
|                 match self { | ||||
|                     $error_type::Invalid(e) => { | ||||
|                         BlockProcessingError::Invalid(BlockInvalid::$invalid_type(i, e)) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| pub trait IntoWithIndex<T>: Sized { | ||||
|     fn into_with_index(self, i: usize) -> T; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * Block Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum BlockProcessingError { | ||||
|     /// The `BeaconBlock` is invalid.
 | ||||
|     Invalid(BlockInvalid), | ||||
|     BeaconStateError(BeaconStateError), | ||||
| } | ||||
| 
 | ||||
| impl_from_beacon_state_error!(BlockProcessingError); | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum BlockInvalid { | ||||
|     StateSlotMismatch, | ||||
|     BadSignature, | ||||
|     BadRandaoSignature, | ||||
|     MaxAttestationsExceeded, | ||||
|     MaxAttesterSlashingsExceed, | ||||
|     MaxProposerSlashingsExceeded, | ||||
|     MaxDepositsExceeded, | ||||
|     MaxExitsExceeded, | ||||
|     MaxTransfersExceed, | ||||
|     AttestationInvalid(usize, AttestationInvalid), | ||||
|     AttesterSlashingInvalid(usize, AttesterSlashingInvalid), | ||||
|     ProposerSlashingInvalid(usize, ProposerSlashingInvalid), | ||||
|     DepositInvalid(usize, DepositInvalid), | ||||
|     // TODO: merge this into the `DepositInvalid` error.
 | ||||
|     DepositProcessingFailed(usize), | ||||
|     ExitInvalid(usize, ExitInvalid), | ||||
|     TransferInvalid(usize, TransferInvalid), | ||||
| } | ||||
| 
 | ||||
| impl Into<BlockProcessingError> for BlockInvalid { | ||||
|     fn into(self) -> BlockProcessingError { | ||||
|         BlockProcessingError::Invalid(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * Attestation Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttestationValidationError { | ||||
|     /// The `Attestation` is invalid.
 | ||||
|     Invalid(AttestationInvalid), | ||||
|     ProcessingError(BeaconStateError), | ||||
|     /// Encountered a `BeaconStateError` whilst attempting to determine validity.
 | ||||
|     BeaconStateError(BeaconStateError), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| @ -23,8 +112,132 @@ pub enum AttestationInvalid { | ||||
|     ShardBlockRootNotZero, | ||||
| } | ||||
| 
 | ||||
| impl From<BeaconStateError> for AttestationValidationError { | ||||
|     fn from(e: BeaconStateError) -> AttestationValidationError { | ||||
|         AttestationValidationError::ProcessingError(e) | ||||
| impl_from_beacon_state_error!(AttestationValidationError); | ||||
| impl_into_with_index_with_beacon_error!(AttestationValidationError, AttestationInvalid); | ||||
| 
 | ||||
| /* | ||||
|  * `AttesterSlashing` Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttesterSlashingValidationError { | ||||
|     /// The `SlashableAttestation` is invalid.
 | ||||
|     Invalid(AttesterSlashingInvalid), | ||||
|     /// Encountered a `BeaconStateError` whilst attempting to determine validity.
 | ||||
|     BeaconStateError(BeaconStateError), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttesterSlashingInvalid { | ||||
|     AttestationDataIdentical, | ||||
|     NotSlashable, | ||||
|     SlashableAttestation1Invalid(SlashableAttestationInvalid), | ||||
|     SlashableAttestation2Invalid(SlashableAttestationInvalid), | ||||
|     UnknownValidator, | ||||
|     NoSlashableIndices, | ||||
| } | ||||
| 
 | ||||
| impl_from_beacon_state_error!(AttesterSlashingValidationError); | ||||
| impl_into_with_index_with_beacon_error!(AttesterSlashingValidationError, AttesterSlashingInvalid); | ||||
| 
 | ||||
| /* | ||||
|  * `SlashableAttestation` Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum SlashableAttestationValidationError { | ||||
|     Invalid(SlashableAttestationInvalid), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum SlashableAttestationInvalid { | ||||
|     CustodyBitfieldHasSetBits, | ||||
|     NoValidatorIndices, | ||||
|     BadValidatorIndicesOrdering, | ||||
|     BadCustodyBitfieldLength, | ||||
|     MaxIndicesExceed, | ||||
|     UnknownValidator, | ||||
|     BadSignature, | ||||
| } | ||||
| 
 | ||||
| impl Into<SlashableAttestationInvalid> for SlashableAttestationValidationError { | ||||
|     fn into(self) -> SlashableAttestationInvalid { | ||||
|         match self { | ||||
|             SlashableAttestationValidationError::Invalid(e) => e, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * `ProposerSlashing` Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum ProposerSlashingValidationError { | ||||
|     Invalid(ProposerSlashingInvalid), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum ProposerSlashingInvalid { | ||||
|     ProposerUnknown, | ||||
|     ProposalSlotMismatch, | ||||
|     ProposalShardMismatch, | ||||
|     ProposalBlockRootMismatch, | ||||
|     ProposerAlreadySlashed, | ||||
|     BadProposal1Signature, | ||||
|     BadProposal2Signature, | ||||
| } | ||||
| 
 | ||||
| impl_into_with_index_without_beacon_error!( | ||||
|     ProposerSlashingValidationError, | ||||
|     ProposerSlashingInvalid | ||||
| ); | ||||
| 
 | ||||
| /* | ||||
|  * `Deposit` Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum DepositValidationError { | ||||
|     Invalid(DepositInvalid), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum DepositInvalid { | ||||
|     BadIndex, | ||||
| } | ||||
| 
 | ||||
| impl_into_with_index_without_beacon_error!(DepositValidationError, DepositInvalid); | ||||
| 
 | ||||
| /* | ||||
|  * `Exit` Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum ExitValidationError { | ||||
|     Invalid(ExitInvalid), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum ExitInvalid { | ||||
|     ValidatorUnknown, | ||||
|     AlreadyExited, | ||||
|     FutureEpoch, | ||||
|     BadSignature, | ||||
| } | ||||
| 
 | ||||
| impl_into_with_index_without_beacon_error!(ExitValidationError, ExitInvalid); | ||||
| 
 | ||||
| /* | ||||
|  * `Transfer` Validation | ||||
|  */ | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum TransferValidationError { | ||||
|     Invalid(TransferInvalid), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum TransferInvalid {} | ||||
| 
 | ||||
| impl_into_with_index_without_beacon_error!(TransferValidationError, TransferInvalid); | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| #[macro_use] | ||||
| mod macros; | ||||
| mod block_processable; | ||||
| pub mod per_block_processing; | ||||
| // mod epoch_processable;
 | ||||
| mod errors; | ||||
| pub mod errors; | ||||
| // mod slot_processable;
 | ||||
| 
 | ||||
| pub use block_processable::{ | ||||
|     validate_attestation, validate_attestation_without_signature, BlockProcessable, | ||||
|     Error as BlockProcessingError, | ||||
| pub use errors::{BlockInvalid, BlockProcessingError}; | ||||
| pub use per_block_processing::{ | ||||
|     per_block_processing, per_block_processing_without_verifying_block_signature, | ||||
| }; | ||||
| // pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError};
 | ||||
| // pub use slot_processable::{Error as SlotProcessingError, SlotProcessable};
 | ||||
|  | ||||
| @ -1,8 +1,13 @@ | ||||
| #[macro_use] | ||||
| macro_rules! ensure { | ||||
|     ($condition: expr, $result: ident) => { | ||||
| macro_rules! verify { | ||||
|     ($condition: expr, $result: expr) => { | ||||
|         if !$condition { | ||||
|             return Err(Error::Invalid(Invalid::$result)); | ||||
|             return Err(Error::Invalid($result)); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| macro_rules! invalid { | ||||
|     ($result: expr) => { | ||||
|         return Err(Error::Invalid($result)); | ||||
|     }; | ||||
| } | ||||
|  | ||||
							
								
								
									
										268
									
								
								eth2/state_processing/src/per_block_processing.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								eth2/state_processing/src/per_block_processing.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,268 @@ | ||||
| use self::verify_proposer_slashing::verify_proposer_slashing; | ||||
| use crate::errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex}; | ||||
| use hashing::hash; | ||||
| use log::debug; | ||||
| use ssz::{ssz_encode, SignedRoot, TreeHash}; | ||||
| use types::*; | ||||
| 
 | ||||
| pub use self::verify_attester_slashing::verify_attester_slashing; | ||||
| pub use validate_attestation::{validate_attestation, validate_attestation_without_signature}; | ||||
| pub use verify_deposit::verify_deposit; | ||||
| pub use verify_exit::verify_exit; | ||||
| pub use verify_transfer::verify_transfer; | ||||
| 
 | ||||
| mod validate_attestation; | ||||
| mod verify_attester_slashing; | ||||
| mod verify_deposit; | ||||
| mod verify_exit; | ||||
| mod verify_proposer_slashing; | ||||
| mod verify_slashable_attestation; | ||||
| mod verify_transfer; | ||||
| 
 | ||||
| pub fn per_block_processing( | ||||
|     state: &mut BeaconState, | ||||
|     block: &BeaconBlock, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     per_block_processing_signature_optional(state, block, true, spec) | ||||
| } | ||||
| 
 | ||||
| pub fn per_block_processing_without_verifying_block_signature( | ||||
|     state: &mut BeaconState, | ||||
|     block: &BeaconBlock, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     per_block_processing_signature_optional(state, block, false, spec) | ||||
| } | ||||
| 
 | ||||
| fn per_block_processing_signature_optional( | ||||
|     mut state: &mut BeaconState, | ||||
|     block: &BeaconBlock, | ||||
|     should_verify_block_signature: bool, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     // Verify that `block.slot == state.slot`.
 | ||||
|     verify!(block.slot == state.slot, Invalid::StateSlotMismatch); | ||||
| 
 | ||||
|     // Get the epoch for future ergonomics.
 | ||||
|     let epoch = block.slot.epoch(spec.slots_per_epoch); | ||||
| 
 | ||||
|     // Ensure the current epoch cache is built.
 | ||||
|     state.build_epoch_cache(RelativeEpoch::Current, spec)?; | ||||
| 
 | ||||
|     // Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`.
 | ||||
|     let block_proposer = | ||||
|         &state.validator_registry[state.get_beacon_proposer_index(block.slot, spec)?]; | ||||
| 
 | ||||
|     // Block signature
 | ||||
|     if should_verify_block_signature { | ||||
|         verify!( | ||||
|             verify_block_signature(&block, &block_proposer, &state.fork, spec,), | ||||
|             Invalid::BadSignature | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // Randao
 | ||||
| 
 | ||||
|     // Verify that `bls_verify(pubkey=proposer.pubkey,
 | ||||
|     // message_hash=hash_tree_root(get_current_epoch(state)), signature=block.randao_reveal,
 | ||||
|     // domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`.
 | ||||
|     verify!( | ||||
|         block.randao_reveal.verify( | ||||
|             &state.current_epoch(spec).hash_tree_root()[..], | ||||
|             spec.get_domain(epoch, Domain::Randao, &state.fork), | ||||
|             &block_proposer.pubkey | ||||
|         ), | ||||
|         Invalid::BadRandaoSignature | ||||
|     ); | ||||
| 
 | ||||
|     // Update the state's RANDAO mix with the one revealed in the block.
 | ||||
|     update_randao(&mut state, &block.randao_reveal, spec)?; | ||||
| 
 | ||||
|     // Eth1 Data
 | ||||
| 
 | ||||
|     // Either increment the eth1_data vote count, or add a new eth1_data.
 | ||||
|     let matching_eth1_vote_index = state | ||||
|         .eth1_data_votes | ||||
|         .iter() | ||||
|         .position(|vote| vote.eth1_data == block.eth1_data); | ||||
|     if let Some(index) = matching_eth1_vote_index { | ||||
|         state.eth1_data_votes[index].vote_count += 1; | ||||
|     } else { | ||||
|         state.eth1_data_votes.push(Eth1DataVote { | ||||
|             eth1_data: block.eth1_data.clone(), | ||||
|             vote_count: 1, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     //Proposer slashings
 | ||||
| 
 | ||||
|     verify!( | ||||
|         block.body.proposer_slashings.len() as u64 <= spec.max_proposer_slashings, | ||||
|         Invalid::MaxProposerSlashingsExceeded | ||||
|     ); | ||||
|     for (i, proposer_slashing) in block.body.proposer_slashings.iter().enumerate() { | ||||
|         verify_proposer_slashing(proposer_slashing, &state, spec) | ||||
|             .map_err(|e| e.into_with_index(i))?; | ||||
|         state.slash_validator(proposer_slashing.proposer_index as usize, spec)?; | ||||
|     } | ||||
| 
 | ||||
|     // Attester Slashings
 | ||||
| 
 | ||||
|     verify!( | ||||
|         block.body.attester_slashings.len() as u64 <= spec.max_attester_slashings, | ||||
|         Invalid::MaxAttesterSlashingsExceed | ||||
|     ); | ||||
|     for (i, attester_slashing) in block.body.attester_slashings.iter().enumerate() { | ||||
|         let slashable_indices = verify_attester_slashing(&state, &attester_slashing, spec) | ||||
|             .map_err(|e| e.into_with_index(i))?; | ||||
|         for i in slashable_indices { | ||||
|             state.slash_validator(i as usize, spec)?; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Attestations
 | ||||
| 
 | ||||
|     verify!( | ||||
|         block.body.attestations.len() as u64 <= spec.max_attestations, | ||||
|         Invalid::MaxAttestationsExceeded | ||||
|     ); | ||||
|     for (i, attestation) in block.body.attestations.iter().enumerate() { | ||||
|         // Build the previous epoch cache only if required by an attestation.
 | ||||
|         if attestation.data.slot.epoch(spec.slots_per_epoch) == state.previous_epoch(spec) { | ||||
|             state.build_epoch_cache(RelativeEpoch::Previous, spec)?; | ||||
|         } | ||||
| 
 | ||||
|         validate_attestation(&mut state, attestation, spec).map_err(|e| e.into_with_index(i))?; | ||||
| 
 | ||||
|         let pending_attestation = PendingAttestation { | ||||
|             data: attestation.data.clone(), | ||||
|             aggregation_bitfield: attestation.aggregation_bitfield.clone(), | ||||
|             custody_bitfield: attestation.custody_bitfield.clone(), | ||||
|             inclusion_slot: state.slot, | ||||
|         }; | ||||
|         state.latest_attestations.push(pending_attestation); | ||||
|     } | ||||
| 
 | ||||
|     // Deposits
 | ||||
| 
 | ||||
|     verify!( | ||||
|         block.body.deposits.len() as u64 <= spec.max_deposits, | ||||
|         Invalid::MaxDepositsExceeded | ||||
|     ); | ||||
|     for (i, deposit) in block.body.deposits.iter().enumerate() { | ||||
|         verify_deposit(&mut state, deposit, spec).map_err(|e| e.into_with_index(i))?; | ||||
| 
 | ||||
|         state | ||||
|             .process_deposit( | ||||
|                 deposit.deposit_data.deposit_input.pubkey.clone(), | ||||
|                 deposit.deposit_data.amount, | ||||
|                 deposit | ||||
|                     .deposit_data | ||||
|                     .deposit_input | ||||
|                     .proof_of_possession | ||||
|                     .clone(), | ||||
|                 deposit.deposit_data.deposit_input.withdrawal_credentials, | ||||
|                 None, | ||||
|                 spec, | ||||
|             ) | ||||
|             .map_err(|_| Error::Invalid(Invalid::DepositProcessingFailed(i)))?; | ||||
| 
 | ||||
|         state.deposit_index += 1; | ||||
|     } | ||||
| 
 | ||||
|     // Exits
 | ||||
| 
 | ||||
|     verify!( | ||||
|         block.body.voluntary_exits.len() as u64 <= spec.max_voluntary_exits, | ||||
|         Invalid::MaxExitsExceeded | ||||
|     ); | ||||
|     for (i, exit) in block.body.voluntary_exits.iter().enumerate() { | ||||
|         verify_exit(&state, exit, spec).map_err(|e| e.into_with_index(i))?; | ||||
| 
 | ||||
|         state.initiate_validator_exit(exit.validator_index as usize); | ||||
|     } | ||||
| 
 | ||||
|     // Transfers
 | ||||
|     verify!( | ||||
|         block.body.transfers.len() as u64 <= spec.max_transfers, | ||||
|         Invalid::MaxTransfersExceed | ||||
|     ); | ||||
|     for (i, transfer) in block.body.transfers.iter().enumerate() { | ||||
|         verify_transfer(&state, transfer, spec).map_err(|e| e.into_with_index(i))?; | ||||
| 
 | ||||
|         let block_proposer = state.get_beacon_proposer_index(state.slot, spec)?; | ||||
| 
 | ||||
|         state.validator_balances[transfer.from as usize] -= transfer.amount + transfer.fee; | ||||
|         state.validator_balances[transfer.to as usize] += transfer.amount + transfer.fee; | ||||
|         state.validator_balances[block_proposer as usize] += transfer.fee; | ||||
|     } | ||||
| 
 | ||||
|     debug!("State transition complete."); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Verifies the signature of a block.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_block_signature( | ||||
|     block: &BeaconBlock, | ||||
|     block_proposer: &Validator, | ||||
|     fork: &Fork, | ||||
|     spec: &ChainSpec, | ||||
| ) -> bool { | ||||
|     // Let proposal = `Proposal(block.slot, BEACON_CHAIN_SHARD_NUMBER, signed_root(block,
 | ||||
|     // "signature"), block.signature)`.
 | ||||
|     let proposal = Proposal { | ||||
|         slot: block.slot, | ||||
|         shard: spec.beacon_chain_shard_number, | ||||
|         block_root: Hash256::from(&block.signed_root()[..]), | ||||
|         signature: block.signature.clone(), | ||||
|     }; | ||||
|     let domain = spec.get_domain( | ||||
|         block.slot.epoch(spec.slots_per_epoch), | ||||
|         Domain::Proposal, | ||||
|         fork, | ||||
|     ); | ||||
|     // Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposal,
 | ||||
|     // "signature"), signature=proposal.signature, domain=get_domain(state.fork,
 | ||||
|     // get_current_epoch(state), DOMAIN_PROPOSAL))`.
 | ||||
|     proposal | ||||
|         .signature | ||||
|         .verify(&proposal.signed_root()[..], domain, &block_proposer.pubkey) | ||||
| } | ||||
| 
 | ||||
| /// Updates the present randao mix.
 | ||||
| ///
 | ||||
| /// Set `state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] =
 | ||||
| /// xor(get_randao_mix(state, get_current_epoch(state)), hash(block.randao_reveal))`.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn update_randao( | ||||
|     state: &mut BeaconState, | ||||
|     reveal: &Signature, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), BeaconStateError> { | ||||
|     let hashed_reveal = { | ||||
|         let encoded_signature = ssz_encode(reveal); | ||||
|         Hash256::from(&hash(&encoded_signature[..])[..]) | ||||
|     }; | ||||
| 
 | ||||
|     let current_epoch = state.slot.epoch(spec.slots_per_epoch); | ||||
| 
 | ||||
|     let current_mix = state | ||||
|         .get_randao_mix(current_epoch, spec) | ||||
|         .ok_or_else(|| BeaconStateError::InsufficientRandaoMixes)?; | ||||
| 
 | ||||
|     let new_mix = *current_mix ^ hashed_reveal; | ||||
| 
 | ||||
|     let index = current_epoch.as_usize() % spec.latest_randao_mixes_length; | ||||
| 
 | ||||
|     if index < state.latest_randao_mixes.len() { | ||||
|         state.latest_randao_mixes[index] = new_mix; | ||||
|         Ok(()) | ||||
|     } else { | ||||
|         Err(BeaconStateError::InsufficientRandaoMixes) | ||||
|     } | ||||
| } | ||||
| @ -3,27 +3,6 @@ use ssz::TreeHash; | ||||
| use types::beacon_state::helpers::*; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Validate the attestations in some block, converting each into a `PendingAttestation` which is
 | ||||
| /// then added to `state.latest_attestations`.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn validate_attestations( | ||||
|     state: &BeaconState, | ||||
|     block: &BeaconBlock, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     ensure!( | ||||
|         block.body.attestations.len() as u64 <= spec.max_attestations, | ||||
|         MaxAttestationsExceeded | ||||
|     ); | ||||
| 
 | ||||
|     for attestation in &block.body.attestations { | ||||
|         validate_attestation(&state, attestation, spec)?; | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Validate an attestation, checking the aggregate signature.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| @ -48,7 +27,7 @@ pub fn validate_attestation_without_signature( | ||||
| 
 | ||||
| /// Validate an attestation, optionally checking the aggregate signature.
 | ||||
| ///
 | ||||
| /// Spec v0.2.0
 | ||||
| /// Spec v0.4.0
 | ||||
| fn validate_attestation_signature_optional( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
| @ -56,38 +35,41 @@ fn validate_attestation_signature_optional( | ||||
|     verify_signature: bool, | ||||
| ) -> Result<(), Error> { | ||||
|     // Verify that `attestation.data.slot >= GENESIS_SLOT`.
 | ||||
|     ensure!(attestation.data.slot >= spec.genesis_slot, PreGenesis); | ||||
|     verify!( | ||||
|         attestation.data.slot >= spec.genesis_slot, | ||||
|         Invalid::PreGenesis | ||||
|     ); | ||||
| 
 | ||||
|     // Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot, | ||||
|         IncludedTooEarly | ||||
|         Invalid::IncludedTooEarly | ||||
|     ); | ||||
| 
 | ||||
|     // Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH`.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         state.slot < attestation.data.slot + spec.slots_per_epoch, | ||||
|         IncludedTooLate | ||||
|         Invalid::IncludedTooLate | ||||
|     ); | ||||
| 
 | ||||
|     // Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch` if
 | ||||
|     // `slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state) else
 | ||||
|     // state.previous_justified_epoch`.
 | ||||
|     if (attestation.data.slot + 1).epoch(spec.slots_per_epoch) >= state.current_epoch(spec) { | ||||
|         ensure!( | ||||
|         verify!( | ||||
|             attestation.data.justified_epoch == state.justified_epoch, | ||||
|             WrongJustifiedSlot | ||||
|             Invalid::WrongJustifiedSlot | ||||
|         ); | ||||
|     } else { | ||||
|         ensure!( | ||||
|         verify!( | ||||
|             attestation.data.justified_epoch == state.previous_justified_epoch, | ||||
|             WrongJustifiedSlot | ||||
|             Invalid::WrongJustifiedSlot | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state,
 | ||||
|     // get_epoch_start_slot(attestation.data.justified_epoch))`.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         attestation.data.justified_block_root | ||||
|             == *state | ||||
|                 .get_block_root( | ||||
| @ -98,7 +80,7 @@ fn validate_attestation_signature_optional( | ||||
|                     &spec | ||||
|                 ) | ||||
|                 .ok_or(BeaconStateError::InsufficientBlockRoots)?, | ||||
|         WrongJustifiedRoot | ||||
|         Invalid::WrongJustifiedRoot | ||||
|     ); | ||||
| 
 | ||||
|     // Verify that either:
 | ||||
| @ -112,59 +94,61 @@ fn validate_attestation_signature_optional( | ||||
|         crosslink_data_root: attestation.data.crosslink_data_root, | ||||
|         epoch: attestation.data.slot.epoch(spec.slots_per_epoch), | ||||
|     }; | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         (attestation.data.latest_crosslink | ||||
|             == state.latest_crosslinks[attestation.data.shard as usize]) | ||||
|             | (state.latest_crosslinks[attestation.data.shard as usize] == potential_crosslink), | ||||
|         BadLatestCrosslinkRoot | ||||
|         Invalid::BadLatestCrosslinkRoot | ||||
|     ); | ||||
| 
 | ||||
|     // Get the committee for this attestation
 | ||||
|     let (committee, _shard) = state | ||||
|         .get_crosslink_committees_at_slot(attestation.data.slot, spec)? | ||||
|         .iter() | ||||
|         .find(|(committee, shard)| *shard == attestation.data.shard) | ||||
|         .find(|(_committee, shard)| *shard == attestation.data.shard) | ||||
|         .ok_or_else(|| Error::Invalid(Invalid::NoCommitteeForShard))?; | ||||
| 
 | ||||
|     // Custody bitfield is all zeros (phase 0 requirement).
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         attestation.custody_bitfield.num_set_bits() == 0, | ||||
|         CustodyBitfieldHasSetBits | ||||
|         Invalid::CustodyBitfieldHasSetBits | ||||
|     ); | ||||
|     // Custody bitfield length is correct.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()), | ||||
|         BadCustodyBitfieldLength | ||||
|         Invalid::BadCustodyBitfieldLength | ||||
|     ); | ||||
|     // Aggregation bitfield isn't empty.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         attestation.aggregation_bitfield.num_set_bits() != 0, | ||||
|         AggregationBitfieldIsEmpty | ||||
|         Invalid::AggregationBitfieldIsEmpty | ||||
|     ); | ||||
|     // Aggregation bitfield length is correct.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()), | ||||
|         BadAggregationBitfieldLength | ||||
|         Invalid::BadAggregationBitfieldLength | ||||
|     ); | ||||
| 
 | ||||
|     if verify_signature { | ||||
|         ensure!( | ||||
|         let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch); | ||||
|         verify!( | ||||
|             verify_attestation_signature( | ||||
|                 state, | ||||
|                 committee, | ||||
|                 attestation_epoch, | ||||
|                 &attestation.custody_bitfield, | ||||
|                 &attestation.data, | ||||
|                 &attestation.aggregate_signature, | ||||
|                 spec | ||||
|             ), | ||||
|             BadSignature | ||||
|             Invalid::BadSignature | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`.
 | ||||
|     ensure!( | ||||
|     verify!( | ||||
|         attestation.data.crosslink_data_root == spec.zero_hash, | ||||
|         ShardBlockRootNotZero | ||||
|         Invalid::ShardBlockRootNotZero | ||||
|     ); | ||||
| 
 | ||||
|     Ok(()) | ||||
| @ -177,9 +161,12 @@ fn validate_attestation_signature_optional( | ||||
| ///  - `aggregate_signature` was not signed correctly.
 | ||||
| ///  - `custody_bitfield` does not have a bit for each index of `committee`.
 | ||||
| ///  - A `validator_index` in `committee` is not in `state.validator_registry`.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| fn verify_attestation_signature( | ||||
|     state: &BeaconState, | ||||
|     committee: &[usize], | ||||
|     attestation_epoch: Epoch, | ||||
|     custody_bitfield: &Bitfield, | ||||
|     attestation_data: &AttestationData, | ||||
|     aggregate_signature: &AggregateSignature, | ||||
| @ -234,5 +221,7 @@ fn verify_attestation_signature( | ||||
|         keys.push(&aggregate_pubs[1]); | ||||
|     } | ||||
| 
 | ||||
|     aggregate_signature.verify_multiple(&messages[..], spec.domain_attestation, &keys[..]) | ||||
|     let domain = spec.get_domain(attestation_epoch, Domain::Attestation, &state.fork); | ||||
| 
 | ||||
|     aggregate_signature.verify_multiple(&messages[..], domain, &keys[..]) | ||||
| } | ||||
| @ -0,0 +1,49 @@ | ||||
| use super::verify_slashable_attestation::verify_slashable_attestation; | ||||
| use crate::errors::{AttesterSlashingInvalid as Invalid, AttesterSlashingValidationError as Error}; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Returns `Ok(())` if some `AttesterSlashing` is valid to be included in some `BeaconState`,
 | ||||
| /// otherwise returns an `Err`.
 | ||||
| ///
 | ||||
| /// Returns the slashable indices from the `AttesterSlashing`.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_attester_slashing( | ||||
|     state: &BeaconState, | ||||
|     attester_slashing: &AttesterSlashing, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<Vec<u64>, Error> { | ||||
|     let slashable_attestation_1 = &attester_slashing.slashable_attestation_1; | ||||
|     let slashable_attestation_2 = &attester_slashing.slashable_attestation_2; | ||||
| 
 | ||||
|     verify!( | ||||
|         slashable_attestation_1.data != slashable_attestation_2.data, | ||||
|         Invalid::AttestationDataIdentical | ||||
|     ); | ||||
|     verify!( | ||||
|         slashable_attestation_1.is_double_vote(slashable_attestation_2, spec) | ||||
|             | slashable_attestation_1.is_surround_vote(slashable_attestation_2, spec), | ||||
|         Invalid::NotSlashable | ||||
|     ); | ||||
| 
 | ||||
|     verify_slashable_attestation(state, &slashable_attestation_1, spec) | ||||
|         .map_err(|e| Error::Invalid(Invalid::SlashableAttestation1Invalid(e.into())))?; | ||||
|     verify_slashable_attestation(state, &slashable_attestation_2, spec) | ||||
|         .map_err(|e| Error::Invalid(Invalid::SlashableAttestation2Invalid(e.into())))?; | ||||
| 
 | ||||
|     let mut slashable_indices = vec![]; | ||||
|     for i in &slashable_attestation_1.validator_indices { | ||||
|         let validator = state | ||||
|             .validator_registry | ||||
|             .get(*i as usize) | ||||
|             .ok_or_else(|| Error::Invalid(Invalid::UnknownValidator))?; | ||||
| 
 | ||||
|         if slashable_attestation_1.validator_indices.contains(&i) & !validator.slashed { | ||||
|             slashable_indices.push(*i); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     verify!(!slashable_indices.is_empty(), Invalid::NoSlashableIndices); | ||||
| 
 | ||||
|     Ok(slashable_indices) | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| use crate::errors::{DepositInvalid as Invalid, DepositValidationError as Error}; | ||||
| use ssz::TreeHash; | ||||
| use types::beacon_state::helpers::verify_bitfield_length; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Verify validity of ``slashable_attestation`` fields.
 | ||||
| ///
 | ||||
| /// Returns `Ok(())` if all fields are valid.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_deposit( | ||||
|     state: &BeaconState, | ||||
|     deposit: &Deposit, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     // TODO: verify serialized deposit data.
 | ||||
| 
 | ||||
|     // TODO: verify deposit index.
 | ||||
|     verify!(deposit.index == state.deposit_index, Invalid::BadIndex); | ||||
| 
 | ||||
|     // TODO: verify merkle branch.
 | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| use crate::errors::{ExitInvalid as Invalid, ExitValidationError as Error}; | ||||
| use ssz::SignedRoot; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Verify validity of ``slashable_attestation`` fields.
 | ||||
| ///
 | ||||
| /// Returns `Ok(())` if all fields are valid.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_exit( | ||||
|     state: &BeaconState, | ||||
|     exit: &VoluntaryExit, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     let validator = state | ||||
|         .validator_registry | ||||
|         .get(exit.validator_index as usize) | ||||
|         .ok_or(Error::Invalid(Invalid::ValidatorUnknown))?; | ||||
| 
 | ||||
|     verify!( | ||||
|         validator.exit_epoch | ||||
|             > state.get_delayed_activation_exit_epoch(state.current_epoch(spec), spec), | ||||
|         Invalid::AlreadyExited | ||||
|     ); | ||||
| 
 | ||||
|     verify!( | ||||
|         state.current_epoch(spec) >= exit.epoch, | ||||
|         Invalid::FutureEpoch | ||||
|     ); | ||||
| 
 | ||||
|     let message = exit.signed_root(); | ||||
|     let domain = spec.get_domain(exit.epoch, Domain::Exit, &state.fork); | ||||
| 
 | ||||
|     verify!( | ||||
|         exit.signature | ||||
|             .verify(&message[..], domain, &validator.pubkey), | ||||
|         Invalid::BadSignature | ||||
|     ); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| @ -0,0 +1,71 @@ | ||||
| use crate::errors::{ProposerSlashingInvalid as Invalid, ProposerSlashingValidationError as Error}; | ||||
| use ssz::SignedRoot; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Returns `Ok(())` if some `ProposerSlashing` is valid to be included in some `BeaconState`,
 | ||||
| /// otherwise returns an `Err`.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_proposer_slashing( | ||||
|     proposer_slashing: &ProposerSlashing, | ||||
|     state: &BeaconState, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     let proposer = state | ||||
|         .validator_registry | ||||
|         .get(proposer_slashing.proposer_index as usize) | ||||
|         .ok_or(Error::Invalid(Invalid::ProposerUnknown))?; | ||||
| 
 | ||||
|     verify!( | ||||
|         proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot, | ||||
|         Invalid::ProposalSlotMismatch | ||||
|     ); | ||||
| 
 | ||||
|     verify!( | ||||
|         proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard, | ||||
|         Invalid::ProposalShardMismatch | ||||
|     ); | ||||
| 
 | ||||
|     verify!( | ||||
|         proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root, | ||||
|         Invalid::ProposalBlockRootMismatch | ||||
|     ); | ||||
| 
 | ||||
|     verify!(!proposer.slashed, Invalid::ProposerAlreadySlashed); | ||||
| 
 | ||||
|     verify!( | ||||
|         verify_proposal_signature( | ||||
|             &proposer_slashing.proposal_1, | ||||
|             &proposer.pubkey, | ||||
|             &state.fork, | ||||
|             spec | ||||
|         ), | ||||
|         Invalid::BadProposal1Signature | ||||
|     ); | ||||
|     verify!( | ||||
|         verify_proposal_signature( | ||||
|             &proposer_slashing.proposal_2, | ||||
|             &proposer.pubkey, | ||||
|             &state.fork, | ||||
|             spec | ||||
|         ), | ||||
|         Invalid::BadProposal2Signature | ||||
|     ); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn verify_proposal_signature( | ||||
|     proposal: &Proposal, | ||||
|     pubkey: &PublicKey, | ||||
|     fork: &Fork, | ||||
|     spec: &ChainSpec, | ||||
| ) -> bool { | ||||
|     let message = proposal.signed_root(); | ||||
|     let domain = spec.get_domain( | ||||
|         proposal.slot.epoch(spec.slots_per_epoch), | ||||
|         Domain::Proposal, | ||||
|         fork, | ||||
|     ); | ||||
|     proposal.signature.verify(&message[..], domain, pubkey) | ||||
| } | ||||
| @ -0,0 +1,105 @@ | ||||
| use crate::errors::{ | ||||
|     SlashableAttestationInvalid as Invalid, SlashableAttestationValidationError as Error, | ||||
| }; | ||||
| use ssz::TreeHash; | ||||
| use types::beacon_state::helpers::verify_bitfield_length; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Verify validity of ``slashable_attestation`` fields.
 | ||||
| ///
 | ||||
| /// Returns `Ok(())` if all fields are valid.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_slashable_attestation( | ||||
|     state: &BeaconState, | ||||
|     slashable_attestation: &SlashableAttestation, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     if slashable_attestation.custody_bitfield.num_set_bits() > 0 { | ||||
|         invalid!(Invalid::CustodyBitfieldHasSetBits); | ||||
|     } | ||||
| 
 | ||||
|     if slashable_attestation.validator_indices.is_empty() { | ||||
|         invalid!(Invalid::NoValidatorIndices); | ||||
|     } | ||||
| 
 | ||||
|     for i in 0..(slashable_attestation.validator_indices.len() - 1) { | ||||
|         if slashable_attestation.validator_indices[i] | ||||
|             >= slashable_attestation.validator_indices[i + 1] | ||||
|         { | ||||
|             invalid!(Invalid::BadValidatorIndicesOrdering); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if !verify_bitfield_length( | ||||
|         &slashable_attestation.custody_bitfield, | ||||
|         slashable_attestation.validator_indices.len(), | ||||
|     ) { | ||||
|         invalid!(Invalid::BadCustodyBitfieldLength); | ||||
|     } | ||||
| 
 | ||||
|     if slashable_attestation.validator_indices.len() > spec.max_indices_per_slashable_vote as usize | ||||
|     { | ||||
|         invalid!(Invalid::MaxIndicesExceed); | ||||
|     } | ||||
| 
 | ||||
|     // TODO: this signature verification could likely be replaced with:
 | ||||
|     //
 | ||||
|     // super::validate_attestation::validate_attestation_signature(..)
 | ||||
| 
 | ||||
|     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 state.validator_registry.get(*v as usize) { | ||||
|             Some(validator) => { | ||||
|                 aggregate_pubs[custody_bit as usize].add(&validator.pubkey); | ||||
|             } | ||||
|             None => invalid!(Invalid::UnknownValidator), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     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]); | ||||
|     } | ||||
| 
 | ||||
|     let domain = { | ||||
|         let epoch = slashable_attestation.data.slot.epoch(spec.slots_per_epoch); | ||||
|         spec.get_domain(epoch, Domain::Attestation, &state.fork) | ||||
|     }; | ||||
| 
 | ||||
|     verify!( | ||||
|         slashable_attestation | ||||
|             .aggregate_signature | ||||
|             .verify_multiple(&messages[..], domain, &keys[..]), | ||||
|         Invalid::BadSignature | ||||
|     ); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| use crate::errors::{TransferInvalid as Invalid, TransferValidationError as Error}; | ||||
| use ssz::TreeHash; | ||||
| use types::beacon_state::helpers::verify_bitfield_length; | ||||
| use types::*; | ||||
| 
 | ||||
| /// Verify validity of ``slashable_attestation`` fields.
 | ||||
| ///
 | ||||
| /// Returns `Ok(())` if all fields are valid.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn verify_transfer( | ||||
|     state: &BeaconState, | ||||
|     transfer: &Transfer, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     // TODO: verify transfer.
 | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| @ -12,7 +12,7 @@ impl AttesterSlashingBuilder { | ||||
|     /// - `validator_index: u64`
 | ||||
|     /// - `message: &[u8]`
 | ||||
|     /// - `epoch: Epoch`
 | ||||
|     /// - `domain: u64`
 | ||||
|     /// - `domain: Domain`
 | ||||
|     ///
 | ||||
|     /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
 | ||||
|     pub fn double_vote<F>( | ||||
| @ -21,7 +21,7 @@ impl AttesterSlashingBuilder { | ||||
|         spec: &ChainSpec, | ||||
|     ) -> AttesterSlashing | ||||
|     where | ||||
|         F: Fn(u64, &[u8], Epoch, u64) -> Signature, | ||||
|         F: Fn(u64, &[u8], Epoch, Domain) -> Signature, | ||||
|     { | ||||
|         let double_voted_slot = Slot::new(0); | ||||
|         let shard = 0; | ||||
| @ -75,12 +75,7 @@ impl AttesterSlashingBuilder { | ||||
|                     custody_bit: attestation.custody_bitfield.get(i).unwrap(), | ||||
|                 }; | ||||
|                 let message = attestation_data_and_custody_bit.hash_tree_root(); | ||||
|                 let signature = signer( | ||||
|                     *validator_index, | ||||
|                     &message[..], | ||||
|                     epoch, | ||||
|                     spec.domain_attestation, | ||||
|                 ); | ||||
|                 let signature = signer(*validator_index, &message[..], epoch, Domain::Attestation); | ||||
|                 attestation.aggregate_signature.add(&signature); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @ -112,6 +112,7 @@ pub struct BeaconState { | ||||
|     // Ethereum 1.0 chain data
 | ||||
|     pub latest_eth1_data: Eth1Data, | ||||
|     pub eth1_data_votes: Vec<Eth1DataVote>, | ||||
|     pub deposit_index: u64, | ||||
| 
 | ||||
|     // Caching (not in the spec)
 | ||||
|     pub cache_index_offset: usize, | ||||
| @ -187,6 +188,7 @@ impl BeaconState { | ||||
|              */ | ||||
|             latest_eth1_data, | ||||
|             eth1_data_votes: vec![], | ||||
|             deposit_index: 0, | ||||
| 
 | ||||
|             /* | ||||
|              * Caching (not in spec) | ||||
| @ -224,6 +226,8 @@ impl BeaconState { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         genesis_state.deposit_index = initial_validator_deposits.len() as u64; | ||||
| 
 | ||||
|         let genesis_active_index_root = hash_tree_root(get_active_validator_indices( | ||||
|             &genesis_state.validator_registry, | ||||
|             spec.genesis_epoch, | ||||
| @ -566,92 +570,6 @@ impl BeaconState { | ||||
|             .fold(0, |acc, i| acc + self.get_effective_balance(*i, spec)) | ||||
|     } | ||||
| 
 | ||||
|     /// Verify validity of ``slashable_attestation`` fields.
 | ||||
|     ///
 | ||||
|     /// Spec v0.4.0
 | ||||
|     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 !verify_bitfield_length( | ||||
|             &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 epoch at which an activation or exit triggered in ``epoch`` takes effect.
 | ||||
|     ///
 | ||||
|     ///  Spec v0.4.0
 | ||||
| @ -1163,8 +1081,11 @@ impl BeaconState { | ||||
| 
 | ||||
|         proof_of_possession.verify( | ||||
|             &proof_of_possession_data.hash_tree_root(), | ||||
|             self.fork | ||||
|                 .get_domain(self.slot.epoch(spec.slots_per_epoch), spec.domain_deposit), | ||||
|             spec.get_domain( | ||||
|                 self.slot.epoch(spec.slots_per_epoch), | ||||
|                 Domain::Deposit, | ||||
|                 &self.fork, | ||||
|             ), | ||||
|             &pubkey, | ||||
|         ) | ||||
|     } | ||||
| @ -1338,6 +1259,7 @@ impl Encodable for BeaconState { | ||||
|         s.append(&self.batched_block_roots); | ||||
|         s.append(&self.latest_eth1_data); | ||||
|         s.append(&self.eth1_data_votes); | ||||
|         s.append(&self.deposit_index); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -1368,6 +1290,7 @@ impl Decodable for BeaconState { | ||||
|         let (batched_block_roots, i) = <_>::ssz_decode(bytes, i)?; | ||||
|         let (latest_eth1_data, i) = <_>::ssz_decode(bytes, i)?; | ||||
|         let (eth1_data_votes, i) = <_>::ssz_decode(bytes, i)?; | ||||
|         let (deposit_index, i) = <_>::ssz_decode(bytes, i)?; | ||||
| 
 | ||||
|         Ok(( | ||||
|             Self { | ||||
| @ -1396,6 +1319,7 @@ impl Decodable for BeaconState { | ||||
|                 batched_block_roots, | ||||
|                 latest_eth1_data, | ||||
|                 eth1_data_votes, | ||||
|                 deposit_index, | ||||
|                 cache_index_offset: 0, | ||||
|                 caches: vec![EpochCache::empty(); CACHED_EPOCHS], | ||||
|             }, | ||||
| @ -1440,6 +1364,7 @@ impl TreeHash for BeaconState { | ||||
|         result.append(&mut self.batched_block_roots.hash_tree_root_internal()); | ||||
|         result.append(&mut self.latest_eth1_data.hash_tree_root_internal()); | ||||
|         result.append(&mut self.eth1_data_votes.hash_tree_root_internal()); | ||||
|         result.append(&mut self.deposit_index.hash_tree_root_internal()); | ||||
|         hash(&result) | ||||
|     } | ||||
| } | ||||
| @ -1472,6 +1397,7 @@ impl<T: RngCore> TestRandom<T> for BeaconState { | ||||
|             batched_block_roots: <_>::random_for_test(rng), | ||||
|             latest_eth1_data: <_>::random_for_test(rng), | ||||
|             eth1_data_votes: <_>::random_for_test(rng), | ||||
|             deposit_index: <_>::random_for_test(rng), | ||||
|             cache_index_offset: 0, | ||||
|             caches: vec![EpochCache::empty(); CACHED_EPOCHS], | ||||
|         } | ||||
|  | ||||
| @ -1,8 +1,17 @@ | ||||
| use crate::{Address, Epoch, Hash256, Slot}; | ||||
| use crate::{Address, Epoch, Fork, Hash256, Slot}; | ||||
| use bls::Signature; | ||||
| 
 | ||||
| const GWEI: u64 = 1_000_000_000; | ||||
| 
 | ||||
| pub enum Domain { | ||||
|     Deposit, | ||||
|     Attestation, | ||||
|     Proposal, | ||||
|     Exit, | ||||
|     Randao, | ||||
|     Transfer, | ||||
| } | ||||
| 
 | ||||
| /// Holds all the "constants" for a BeaconChain.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| @ -85,14 +94,20 @@ pub struct ChainSpec { | ||||
| 
 | ||||
|     /* | ||||
|      * Signature domains | ||||
|      * | ||||
|      * Fields should be private to prevent accessing a domain that hasn't been modified to suit | ||||
|      * some `Fork`. | ||||
|      * | ||||
|      * Use `ChainSpec::get_domain(..)` to access these values. | ||||
|      */ | ||||
|     pub domain_deposit: u64, | ||||
|     pub domain_attestation: u64, | ||||
|     pub domain_proposal: u64, | ||||
|     pub domain_exit: u64, | ||||
|     pub domain_randao: u64, | ||||
|     pub domain_transfer: u64, | ||||
|     domain_deposit: u64, | ||||
|     domain_attestation: u64, | ||||
|     domain_proposal: u64, | ||||
|     domain_exit: u64, | ||||
|     domain_randao: u64, | ||||
|     domain_transfer: u64, | ||||
| } | ||||
| 
 | ||||
| impl ChainSpec { | ||||
|     /// Return the number of committees in one epoch.
 | ||||
|     ///
 | ||||
| @ -107,6 +122,23 @@ impl ChainSpec { | ||||
|         ) * self.slots_per_epoch | ||||
|     } | ||||
| 
 | ||||
|     /// Get the domain number that represents the fork meta and signature domain.
 | ||||
|     ///
 | ||||
|     /// Spec v0.4.0
 | ||||
|     pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 { | ||||
|         let domain_constant = match domain { | ||||
|             Domain::Deposit => self.domain_deposit, | ||||
|             Domain::Attestation => self.domain_attestation, | ||||
|             Domain::Proposal => self.domain_proposal, | ||||
|             Domain::Exit => self.domain_exit, | ||||
|             Domain::Randao => self.domain_randao, | ||||
|             Domain::Transfer => self.domain_transfer, | ||||
|         }; | ||||
| 
 | ||||
|         let fork_version = fork.get_fork_version(epoch); | ||||
|         fork_version * u64::pow(2, 32) + domain_constant | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
 | ||||
|     ///
 | ||||
|     /// Spec v0.4.0
 | ||||
|  | ||||
| @ -24,14 +24,6 @@ impl Fork { | ||||
|         } | ||||
|         self.current_version | ||||
|     } | ||||
| 
 | ||||
|     /// Get the domain number that represents the fork meta and signature domain.
 | ||||
|     ///
 | ||||
|     /// Spec v0.4.0
 | ||||
|     pub fn get_domain(&self, epoch: Epoch, domain_type: u64) -> u64 { | ||||
|         let fork_version = self.get_fork_version(epoch); | ||||
|         fork_version * u64::pow(2, 32) + domain_type | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  | ||||
| @ -43,7 +43,7 @@ pub use crate::beacon_block_body::BeaconBlockBody; | ||||
| pub use crate::beacon_state::{ | ||||
|     BeaconState, Error as BeaconStateError, InclusionError, RelativeEpoch, | ||||
| }; | ||||
| pub use crate::chain_spec::ChainSpec; | ||||
| pub use crate::chain_spec::{ChainSpec, Domain}; | ||||
| pub use crate::crosslink::Crosslink; | ||||
| pub use crate::deposit::Deposit; | ||||
| pub use crate::deposit_data::DepositData; | ||||
|  | ||||
| @ -12,12 +12,12 @@ impl ProposerSlashingBuilder { | ||||
|     /// - `validator_index: u64`
 | ||||
|     /// - `message: &[u8]`
 | ||||
|     /// - `epoch: Epoch`
 | ||||
|     /// - `domain: u64`
 | ||||
|     /// - `domain: Domain`
 | ||||
|     ///
 | ||||
|     /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
 | ||||
|     pub fn double_vote<F>(proposer_index: u64, signer: F, spec: &ChainSpec) -> ProposerSlashing | ||||
|     where | ||||
|         F: Fn(u64, &[u8], Epoch, u64) -> Signature, | ||||
|         F: Fn(u64, &[u8], Epoch, Domain) -> Signature, | ||||
|     { | ||||
|         let slot = Slot::new(0); | ||||
|         let shard = 0; | ||||
| @ -39,15 +39,13 @@ impl ProposerSlashingBuilder { | ||||
|         proposal_1.signature = { | ||||
|             let message = proposal_1.signed_root(); | ||||
|             let epoch = slot.epoch(spec.slots_per_epoch); | ||||
|             let domain = spec.domain_proposal; | ||||
|             signer(proposer_index, &message[..], epoch, domain) | ||||
|             signer(proposer_index, &message[..], epoch, Domain::Proposal) | ||||
|         }; | ||||
| 
 | ||||
|         proposal_2.signature = { | ||||
|             let message = proposal_2.signed_root(); | ||||
|             let epoch = slot.epoch(spec.slots_per_epoch); | ||||
|             let domain = spec.domain_proposal; | ||||
|             signer(proposer_index, &message[..], epoch, domain) | ||||
|             signer(proposer_index, &message[..], epoch, Domain::Proposal) | ||||
|         }; | ||||
| 
 | ||||
|         ProposerSlashing { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user