Start new Error structure in state_processing
				
					
				
			This commit is contained in:
		
							parent
							
								
									0be8e57fd3
								
							
						
					
					
						commit
						a15ed0acd3
					
				| @ -5,7 +5,11 @@ 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; | ||||
| @ -41,7 +45,6 @@ pub enum Error { | ||||
|     BeaconStateError(BeaconStateError), | ||||
|     SlotProcessingError(SlotProcessingError), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttestationValidationError { | ||||
|     IncludedTooEarly, | ||||
| @ -221,19 +224,10 @@ fn per_block_processing_signature_optional( | ||||
|         verify_slashable_attestation(&mut state, &attester_slashing, spec)?; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Attestations | ||||
|      */ | ||||
|     ensure!( | ||||
|         block.body.attestations.len() as u64 <= spec.max_attestations, | ||||
|         Error::MaxAttestationsExceeded | ||||
|     ); | ||||
| 
 | ||||
|     debug!("Verifying {} attestations.", block.body.attestations.len()); | ||||
|     validate_attestations(&mut state, &block, spec); | ||||
| 
 | ||||
|     // Convert each attestation into a `PendingAttestation` and insert into the state.
 | ||||
|     for attestation in &block.body.attestations { | ||||
|         validate_attestation(&state, attestation, spec)?; | ||||
| 
 | ||||
|         let pending_attestation = PendingAttestation { | ||||
|             data: attestation.data.clone(), | ||||
|             aggregation_bitfield: attestation.aggregation_bitfield.clone(), | ||||
| @ -293,7 +287,7 @@ fn per_block_processing_signature_optional( | ||||
|         ); | ||||
|         ensure!(state.current_epoch(spec) >= exit.epoch, Error::BadExit); | ||||
|         let exit_message = { | ||||
|             let exit_struct = Exit { | ||||
|             let exit_struct = VoluntaryExit { | ||||
|                 epoch: exit.epoch, | ||||
|                 validator_index: exit.validator_index, | ||||
|                 signature: spec.empty_signature.clone(), | ||||
| @ -317,110 +311,6 @@ fn per_block_processing_signature_optional( | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn validate_attestation( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), AttestationValidationError> { | ||||
|     validate_attestation_signature_optional(state, attestation, spec, true) | ||||
| } | ||||
| 
 | ||||
| pub fn validate_attestation_without_signature( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), AttestationValidationError> { | ||||
|     validate_attestation_signature_optional(state, attestation, spec, false) | ||||
| } | ||||
| 
 | ||||
| fn validate_attestation_signature_optional( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
|     spec: &ChainSpec, | ||||
|     verify_signature: bool, | ||||
| ) -> Result<(), AttestationValidationError> { | ||||
|     trace!( | ||||
|         "validate_attestation_signature_optional: attestation epoch: {}", | ||||
|         attestation.data.slot.epoch(spec.slots_per_epoch) | ||||
|     ); | ||||
|     ensure!( | ||||
|         attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot, | ||||
|         AttestationValidationError::IncludedTooEarly | ||||
|     ); | ||||
|     ensure!( | ||||
|         attestation.data.slot + spec.slots_per_epoch >= state.slot, | ||||
|         AttestationValidationError::IncludedTooLate | ||||
|     ); | ||||
|     if attestation.data.slot >= state.current_epoch_start_slot(spec) { | ||||
|         ensure!( | ||||
|             attestation.data.justified_epoch == state.justified_epoch, | ||||
|             AttestationValidationError::WrongJustifiedSlot | ||||
|         ); | ||||
|     } else { | ||||
|         ensure!( | ||||
|             attestation.data.justified_epoch == state.previous_justified_epoch, | ||||
|             AttestationValidationError::WrongJustifiedSlot | ||||
|         ); | ||||
|     } | ||||
|     ensure!( | ||||
|         attestation.data.justified_block_root | ||||
|             == *state | ||||
|                 .get_block_root( | ||||
|                     attestation | ||||
|                         .data | ||||
|                         .justified_epoch | ||||
|                         .start_slot(spec.slots_per_epoch), | ||||
|                     &spec | ||||
|                 ) | ||||
|                 .ok_or(AttestationValidationError::NoBlockRoot)?, | ||||
|         AttestationValidationError::WrongJustifiedRoot | ||||
|     ); | ||||
|     let potential_crosslink = Crosslink { | ||||
|         shard_block_root: attestation.data.shard_block_root, | ||||
|         epoch: attestation.data.slot.epoch(spec.slots_per_epoch), | ||||
|     }; | ||||
|     ensure!( | ||||
|         (attestation.data.latest_crosslink | ||||
|             == state.latest_crosslinks[attestation.data.shard as usize]) | ||||
|             | (attestation.data.latest_crosslink == potential_crosslink), | ||||
|         AttestationValidationError::BadLatestCrosslinkRoot | ||||
|     ); | ||||
|     if verify_signature { | ||||
|         let participants = state.get_attestation_participants( | ||||
|             &attestation.data, | ||||
|             &attestation.aggregation_bitfield, | ||||
|             spec, | ||||
|         )?; | ||||
|         trace!( | ||||
|             "slot: {}, shard: {}, participants: {:?}", | ||||
|             attestation.data.slot, | ||||
|             attestation.data.shard, | ||||
|             participants | ||||
|         ); | ||||
|         let mut group_public_key = AggregatePublicKey::new(); | ||||
|         for participant in participants { | ||||
|             group_public_key.add(&state.validator_registry[participant as usize].pubkey) | ||||
|         } | ||||
|         ensure!( | ||||
|             attestation.verify_signature( | ||||
|                 &group_public_key, | ||||
|                 PHASE_0_CUSTODY_BIT, | ||||
|                 get_domain( | ||||
|                     &state.fork, | ||||
|                     attestation.data.slot.epoch(spec.slots_per_epoch), | ||||
|                     spec.domain_attestation, | ||||
|                 ) | ||||
|             ), | ||||
|             AttestationValidationError::BadSignature | ||||
|         ); | ||||
|     } | ||||
|     ensure!( | ||||
|         attestation.data.shard_block_root == spec.zero_hash, | ||||
|         AttestationValidationError::ShardBlockRootNotZero | ||||
|     ); | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn get_domain(fork: &Fork, epoch: Epoch, domain_type: u64) -> u64 { | ||||
|     fork.get_domain(epoch, domain_type) | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,238 @@ | ||||
| use crate::errors::{AttestationInvalid as Invalid, AttestationValidationError as Error}; | ||||
| 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
 | ||||
| pub fn validate_attestation( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     validate_attestation_signature_optional(state, attestation, spec, true) | ||||
| } | ||||
| 
 | ||||
| /// Validate an attestation, without checking the aggregate signature.
 | ||||
| ///
 | ||||
| /// Spec v0.4.0
 | ||||
| pub fn validate_attestation_without_signature( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     validate_attestation_signature_optional(state, attestation, spec, false) | ||||
| } | ||||
| 
 | ||||
| /// Validate an attestation, optionally checking the aggregate signature.
 | ||||
| ///
 | ||||
| /// Spec v0.2.0
 | ||||
| fn validate_attestation_signature_optional( | ||||
|     state: &BeaconState, | ||||
|     attestation: &Attestation, | ||||
|     spec: &ChainSpec, | ||||
|     verify_signature: bool, | ||||
| ) -> Result<(), Error> { | ||||
|     // Verify that `attestation.data.slot >= GENESIS_SLOT`.
 | ||||
|     ensure!(attestation.data.slot >= spec.genesis_slot, PreGenesis); | ||||
| 
 | ||||
|     // Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`.
 | ||||
|     ensure!( | ||||
|         attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot, | ||||
|         IncludedTooEarly | ||||
|     ); | ||||
| 
 | ||||
|     // Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH`.
 | ||||
|     ensure!( | ||||
|         state.slot < attestation.data.slot + spec.slots_per_epoch, | ||||
|         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!( | ||||
|             attestation.data.justified_epoch == state.justified_epoch, | ||||
|             WrongJustifiedSlot | ||||
|         ); | ||||
|     } else { | ||||
|         ensure!( | ||||
|             attestation.data.justified_epoch == state.previous_justified_epoch, | ||||
|             WrongJustifiedSlot | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state,
 | ||||
|     // get_epoch_start_slot(attestation.data.justified_epoch))`.
 | ||||
|     ensure!( | ||||
|         attestation.data.justified_block_root | ||||
|             == *state | ||||
|                 .get_block_root( | ||||
|                     attestation | ||||
|                         .data | ||||
|                         .justified_epoch | ||||
|                         .start_slot(spec.slots_per_epoch), | ||||
|                     &spec | ||||
|                 ) | ||||
|                 .ok_or(BeaconStateError::InsufficientBlockRoots)?, | ||||
|         WrongJustifiedRoot | ||||
|     ); | ||||
| 
 | ||||
|     // Verify that either:
 | ||||
|     //
 | ||||
|     // (i)`state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink`,
 | ||||
|     //
 | ||||
|     // (ii) `state.latest_crosslinks[attestation.data.shard] ==
 | ||||
|     // Crosslink(crosslink_data_root=attestation.data.crosslink_data_root,
 | ||||
|     // epoch=slot_to_epoch(attestation.data.slot))`.
 | ||||
|     let potential_crosslink = Crosslink { | ||||
|         crosslink_data_root: attestation.data.crosslink_data_root, | ||||
|         epoch: attestation.data.slot.epoch(spec.slots_per_epoch), | ||||
|     }; | ||||
|     ensure!( | ||||
|         (attestation.data.latest_crosslink | ||||
|             == state.latest_crosslinks[attestation.data.shard as usize]) | ||||
|             | (state.latest_crosslinks[attestation.data.shard as usize] == potential_crosslink), | ||||
|         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) | ||||
|         .ok_or_else(|| Error::Invalid(Invalid::NoCommitteeForShard))?; | ||||
| 
 | ||||
|     // Custody bitfield is all zeros (phase 0 requirement).
 | ||||
|     ensure!( | ||||
|         attestation.custody_bitfield.num_set_bits() == 0, | ||||
|         CustodyBitfieldHasSetBits | ||||
|     ); | ||||
|     // Custody bitfield length is correct.
 | ||||
|     ensure!( | ||||
|         verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()), | ||||
|         BadCustodyBitfieldLength | ||||
|     ); | ||||
|     // Aggregation bitfield isn't empty.
 | ||||
|     ensure!( | ||||
|         attestation.aggregation_bitfield.num_set_bits() != 0, | ||||
|         AggregationBitfieldIsEmpty | ||||
|     ); | ||||
|     // Aggregation bitfield length is correct.
 | ||||
|     ensure!( | ||||
|         verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()), | ||||
|         BadAggregationBitfieldLength | ||||
|     ); | ||||
| 
 | ||||
|     if verify_signature { | ||||
|         ensure!( | ||||
|             verify_attestation_signature( | ||||
|                 state, | ||||
|                 committee, | ||||
|                 &attestation.custody_bitfield, | ||||
|                 &attestation.data, | ||||
|                 &attestation.aggregate_signature, | ||||
|                 spec | ||||
|             ), | ||||
|             BadSignature | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`.
 | ||||
|     ensure!( | ||||
|         attestation.data.crosslink_data_root == spec.zero_hash, | ||||
|         ShardBlockRootNotZero | ||||
|     ); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Verifies an aggregate signature for some given `AttestationData`, returning `true` if the
 | ||||
| /// `aggregate_signature` is valid.
 | ||||
| ///
 | ||||
| /// Returns `false` if:
 | ||||
| ///  - `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`.
 | ||||
| fn verify_attestation_signature( | ||||
|     state: &BeaconState, | ||||
|     committee: &[usize], | ||||
|     custody_bitfield: &Bitfield, | ||||
|     attestation_data: &AttestationData, | ||||
|     aggregate_signature: &AggregateSignature, | ||||
|     spec: &ChainSpec, | ||||
| ) -> bool { | ||||
|     let mut aggregate_pubs = vec![AggregatePublicKey::new(); 2]; | ||||
|     let mut message_exists = vec![false; 2]; | ||||
| 
 | ||||
|     for (i, v) in committee.iter().enumerate() { | ||||
|         let custody_bit = match custody_bitfield.get(i) { | ||||
|             Ok(bit) => bit, | ||||
|             // Invalidate signature if custody_bitfield.len() < committee
 | ||||
|             Err(_) => return false, | ||||
|         }; | ||||
| 
 | ||||
|         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); | ||||
|             } | ||||
|             // Invalidate signature if validator index is unknown.
 | ||||
|             None => return false, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     // Message when custody bitfield is `false`
 | ||||
|     let message_0 = AttestationDataAndCustodyBit { | ||||
|         data: attestation_data.clone(), | ||||
|         custody_bit: false, | ||||
|     } | ||||
|     .hash_tree_root(); | ||||
| 
 | ||||
|     // Message when custody bitfield is `true`
 | ||||
|     let message_1 = AttestationDataAndCustodyBit { | ||||
|         data: attestation_data.clone(), | ||||
|         custody_bit: true, | ||||
|     } | ||||
|     .hash_tree_root(); | ||||
| 
 | ||||
|     let mut messages = vec![]; | ||||
|     let mut keys = vec![]; | ||||
| 
 | ||||
|     // If any validator signed a message with a `false` custody bit.
 | ||||
|     if message_exists[0] { | ||||
|         messages.push(&message_0[..]); | ||||
|         keys.push(&aggregate_pubs[0]); | ||||
|     } | ||||
|     // If any validator signed a message with a `true` custody bit.
 | ||||
|     if message_exists[1] { | ||||
|         messages.push(&message_1[..]); | ||||
|         keys.push(&aggregate_pubs[1]); | ||||
|     } | ||||
| 
 | ||||
|     aggregate_signature.verify_multiple(&messages[..], spec.domain_attestation, &keys[..]) | ||||
| } | ||||
							
								
								
									
										30
									
								
								eth2/state_processing/src/errors.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								eth2/state_processing/src/errors.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| use types::BeaconStateError; | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttestationValidationError { | ||||
|     Invalid(AttestationInvalid), | ||||
|     ProcessingError(BeaconStateError), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum AttestationInvalid { | ||||
|     PreGenesis, | ||||
|     IncludedTooEarly, | ||||
|     IncludedTooLate, | ||||
|     WrongJustifiedSlot, | ||||
|     WrongJustifiedRoot, | ||||
|     BadLatestCrosslinkRoot, | ||||
|     CustodyBitfieldHasSetBits, | ||||
|     AggregationBitfieldIsEmpty, | ||||
|     BadAggregationBitfieldLength, | ||||
|     BadCustodyBitfieldLength, | ||||
|     NoCommitteeForShard, | ||||
|     BadSignature, | ||||
|     ShardBlockRootNotZero, | ||||
| } | ||||
| 
 | ||||
| impl From<BeaconStateError> for AttestationValidationError { | ||||
|     fn from(e: BeaconStateError) -> AttestationValidationError { | ||||
|         AttestationValidationError::ProcessingError(e) | ||||
|     } | ||||
| } | ||||
| @ -1,10 +1,13 @@ | ||||
| #[macro_use] | ||||
| mod macros; | ||||
| mod block_processable; | ||||
| mod epoch_processable; | ||||
| mod slot_processable; | ||||
| // mod epoch_processable;
 | ||||
| mod errors; | ||||
| // mod slot_processable;
 | ||||
| 
 | ||||
| pub use block_processable::{ | ||||
|     validate_attestation, validate_attestation_without_signature, BlockProcessable, | ||||
|     Error as BlockProcessingError, | ||||
| }; | ||||
| pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError}; | ||||
| pub use slot_processable::{Error as SlotProcessingError, SlotProcessable}; | ||||
| // pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError};
 | ||||
| // pub use slot_processable::{Error as SlotProcessingError, SlotProcessable};
 | ||||
|  | ||||
							
								
								
									
										8
									
								
								eth2/state_processing/src/macros.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								eth2/state_processing/src/macros.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| #[macro_use] | ||||
| macro_rules! ensure { | ||||
|     ($condition: expr, $result: ident) => { | ||||
|         if !$condition { | ||||
|             return Err(Error::Invalid(Invalid::$result)); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user