Add comments to attestation fns, fix bug

This commit is contained in:
Paul Hauner 2018-09-25 09:29:56 +10:00
parent 209076e371
commit bb81bacb4e
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
2 changed files with 94 additions and 1 deletions

View File

@ -18,6 +18,13 @@ pub enum SignatureVerificationError {
DBError(String),
}
/// Verify an aggregate signature across the supplied message.
///
/// The public keys used for verification are collected by mapping
/// each true bitfield bit to canonical ValidatorRecord index through
/// the attestation_indicies map.
///
/// Each public key is loaded from the store on-demand.
pub fn verify_aggregate_signature_for_indices<T>(
message: &[u8],
agg_sig: &AggregateSignature,
@ -33,13 +40,32 @@ pub fn verify_aggregate_signature_for_indices<T>(
for i in 0..attestation_indices.len() {
let voted = bitfield.get_bit(i);
if voted {
let validator = attestation_indices[i];
/*
* De-reference the attestation index into a canonical ValidatorRecord index.
*/
let validator = attestation_indices.get(i)
.ok_or(SignatureVerificationError::BadValidatorIndex)?;
/*
* Load the validators public key from our store.
*/
let pub_key = validator_store.get_public_key_by_index(i)?
.ok_or(SignatureVerificationError::NoPublicKeyForValidator)?;
/*
* Add the validators public key to the aggregate public key.
*/
agg_pub_key.add(&pub_key);
/*
* Add to the validator to the set of voters for this attestation record.
*/
voters.insert(validator);
}
}
/*
* Verify the aggregate public key against the aggregate signature.
*
* This verification will only succeed if the exact set of public keys
* were added to the aggregate public key as those that signed the aggregate signature.
*/
if agg_sig.verify(&message, &agg_pub_key) {
Ok(Some(voters))
} else {

View File

@ -46,6 +46,19 @@ pub enum SszBlockValidationError {
DatabaseError(String),
}
/// Validate some SszBlock. An SszBlock varies from a Block in that is a read-only structure
/// that reads directly from encoded SSZ.
///
/// The reason to validate an SzzBlock is to avoid decoding it in its entirety if there is
/// a suspicion that the block might be invalid. Such a suspicion should be applied to
/// all blocks coming from the network.
///
/// Of course, this function will only be more efficient if a block is already serialized.
/// Serializing a complete block and then validating with this function will be less
/// efficient than just validating the original block.
///
/// This function will determine if the block is new, already known or invalid (either
/// intrinsically or due to some application error.)
#[allow(dead_code)]
pub fn validate_ssz_block<T>(b: &SszBlock,
expected_slot: u64,
@ -61,27 +74,59 @@ pub fn validate_ssz_block<T>(b: &SszBlock,
-> Result<BlockStatus, SszBlockValidationError>
where T: ClientDB + Sized
{
/*
* If this block is already known, return immediately.
*/
if block_store.block_exists(&b.block_hash())? {
return Ok(BlockStatus::KnownBlock);
}
/*
* Copy the block slot (will be used multiple times)
*/
let block_slot = b.slot_number();
/*
* If the block slot corresponds to a slot in the future (according to the local time),
* drop it.
*/
if block_slot > expected_slot {
return Err(SszBlockValidationError::FutureSlot);
}
/*
* If the PoW chain hash is not known to us, drop it.
*
* We only accept blocks that reference a known PoW hash.
*
* Note: it is not clear what a "known" PoW chain ref is. Likely,
* it means "sufficienty deep in the canonical PoW chain".
*/
if pow_store.block_hash_exists(b.pow_chain_ref())? == false {
return Err(SszBlockValidationError::UnknownPoWChainRef);
}
/*
* Store a reference to the serialized attestations from the block.
*/
let attestations_ssz = &b.attestations();
/*
* Get a slice of the first serialized attestation (the 0th) and decode it into
* a full AttestationRecord object.
*/
let (first_attestation_ssz, next_index) = split_one_attestation(
&attestations_ssz,
0)?;
let (first_attestation, _) = AttestationRecord::ssz_decode(
&first_attestation_ssz, 0)?;
/*
* Validate this first attestation.
*
* It is a requirement that the block proposer for this slot
* must have signed the 0th attestation record.
*/
let attestation_voters = validate_attestation(
&first_attestation,
block_slot,
@ -92,19 +137,37 @@ pub fn validate_ssz_block<T>(b: &SszBlock,
validator_store.clone(),
attester_map.clone())?;
/*
* If the set of voters is None, the attestation was invalid.
*/
let attestation_voters = attestation_voters
.ok_or(SszBlockValidationError::InvalidAttestation)?;
/*
* Read the proposer from the map of slot -> validator index.
*/
let proposer = proposer_map.get(&block_slot)
.ok_or(SszBlockValidationError::BadProposerMap)?;
/*
* If the proposer for this slot was not a voter, reject the block.
*/
if !attestation_voters.contains(&proposer) {
return Err(SszBlockValidationError::NoProposerSignature);
}
/*
* Split the remaining attestations into a vector of slices, each containing
* a single serialized attestation record.
*/
let other_attestations = split_all_attestations(attestations_ssz,
next_index)?;
/*
* Verify each other AttestationRecord.
*
* TODO: make this parallelized.
*/
for attestation in other_attestations {
let (a, _) = AttestationRecord::ssz_decode(&attestation, 0)?;
let attestation_voters = validate_attestation(
@ -121,6 +184,10 @@ pub fn validate_ssz_block<T>(b: &SszBlock,
}
}
/*
* If we have reached this point, the block is a new valid block that is worthy of
* processing.
*/
Ok(BlockStatus::NewBlock)
}