mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-18 20:32:45 +00:00
This adds block and attestation validation code that was written previously. There were many non-validation specific changes made whilst building these functions (e.g., db, hashing, etc) -- these changes have already been merged into master and this branch has been created just to make it easy to review this code.
227 lines
6.6 KiB
Rust
227 lines
6.6 KiB
Rust
use super::types::Hash256;
|
|
|
|
#[derive(Debug)]
|
|
pub enum ParentHashesError {
|
|
BadCurrentHashes,
|
|
BadObliqueHashes,
|
|
SlotTooHigh,
|
|
SlotTooLow,
|
|
IntWrapping,
|
|
}
|
|
|
|
/// This function is used to select the hashes used in
|
|
/// the signing of an AttestationRecord.
|
|
///
|
|
/// It either returns Result with a vector of length `cycle_length,` or
|
|
/// returns an Error.
|
|
///
|
|
/// This function corresponds to the `get_signed_parent_hashes` function
|
|
/// in the Python reference implentation.
|
|
///
|
|
/// See this slide for more information:
|
|
/// https://tinyurl.com/ybzn2spw
|
|
pub fn attestation_parent_hashes(
|
|
cycle_length: u8,
|
|
block_slot: u64,
|
|
attestation_slot: u64,
|
|
current_hashes: &[Hash256],
|
|
oblique_hashes: &[Hash256])
|
|
-> Result<Vec<Hash256>, ParentHashesError>
|
|
{
|
|
// This cast places a limit on cycle_length. If you change it, check math
|
|
// for overflow.
|
|
let cycle_length: u64 = u64::from(cycle_length);
|
|
|
|
if current_hashes.len() as u64 != (cycle_length * 2) {
|
|
return Err(ParentHashesError::BadCurrentHashes);
|
|
}
|
|
if oblique_hashes.len() as u64 > cycle_length {
|
|
return Err(ParentHashesError::BadObliqueHashes);
|
|
}
|
|
if attestation_slot >= block_slot {
|
|
return Err(ParentHashesError::SlotTooHigh);
|
|
}
|
|
|
|
/*
|
|
* Cannot underflow as block_slot cannot be less
|
|
* than attestation_slot.
|
|
*/
|
|
let attestation_distance = block_slot - attestation_slot;
|
|
|
|
if attestation_distance > cycle_length {
|
|
return Err(ParentHashesError::SlotTooLow);
|
|
}
|
|
|
|
/*
|
|
* Cannot underflow because attestation_distance cannot
|
|
* be larger than cycle_length.
|
|
*/
|
|
let start = cycle_length - attestation_distance;
|
|
|
|
/*
|
|
* Overflow is potentially impossible, but proof is complicated
|
|
* enough to just use checked math.
|
|
*
|
|
* Arithmetic is:
|
|
* start + cycle_length - oblique_hashes.len()
|
|
*/
|
|
let end = start.checked_add(cycle_length)
|
|
.and_then(|x| x.checked_sub(oblique_hashes.len() as u64))
|
|
.ok_or(ParentHashesError::IntWrapping)?;
|
|
|
|
|
|
let mut hashes = Vec::new();
|
|
hashes.extend_from_slice(
|
|
¤t_hashes[(start as usize)..(end as usize)]);
|
|
hashes.extend_from_slice(oblique_hashes);
|
|
|
|
Ok(hashes)
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn get_range_of_hashes(from: usize, to: usize) -> Vec<Hash256> {
|
|
(from..to).map(|i| get_hash(&vec![i as u8])).collect()
|
|
}
|
|
|
|
fn get_hash(value: &[u8]) -> Hash256 {
|
|
Hash256::from_slice(value)
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_signed_hashes_oblique_scenario_1() {
|
|
/*
|
|
* Two oblique hashes.
|
|
*/
|
|
let cycle_length: u8 = 8;
|
|
let block_slot: u64 = 19;
|
|
let attestation_slot: u64 = 15;
|
|
let current_hashes = get_range_of_hashes(3, 19);
|
|
let oblique_hashes = get_range_of_hashes(100, 102);
|
|
let result = attestation_parent_hashes(
|
|
cycle_length,
|
|
block_slot,
|
|
attestation_slot,
|
|
¤t_hashes,
|
|
&oblique_hashes);
|
|
assert!(result.is_ok());
|
|
let result = result.unwrap();
|
|
assert_eq!(result.len(), cycle_length as usize);
|
|
|
|
let mut expected_result = get_range_of_hashes(7, 13);
|
|
expected_result.append(&mut get_range_of_hashes(100, 102));
|
|
assert_eq!(result, expected_result);
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_signed_hashes_oblique_scenario_2() {
|
|
/*
|
|
* All oblique hashes.
|
|
*/
|
|
let cycle_length: u8 = 8;
|
|
let block_slot: u64 = 19;
|
|
let attestation_slot: u64 = 15;
|
|
let current_hashes = get_range_of_hashes(3, 19);
|
|
let oblique_hashes = get_range_of_hashes(100, 108);
|
|
let result = attestation_parent_hashes(
|
|
cycle_length,
|
|
block_slot,
|
|
attestation_slot,
|
|
¤t_hashes,
|
|
&oblique_hashes);
|
|
assert!(result.is_ok());
|
|
let result = result.unwrap();
|
|
assert_eq!(result.len(), cycle_length as usize);
|
|
|
|
let expected_result = get_range_of_hashes(100, 108);
|
|
assert_eq!(result, expected_result);
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_signed_hashes_scenario_1() {
|
|
/*
|
|
* Google Slides example.
|
|
* https://tinyurl.com/ybzn2spw
|
|
*/
|
|
let cycle_length: u8 = 8;
|
|
let block_slot: u64 = 19;
|
|
let attestation_slot: u64 = 15;
|
|
let current_hashes = get_range_of_hashes(3, 19);
|
|
let oblique_hashes = vec![];
|
|
let result = attestation_parent_hashes(
|
|
cycle_length,
|
|
block_slot,
|
|
attestation_slot,
|
|
¤t_hashes,
|
|
&oblique_hashes);
|
|
assert!(result.is_ok());
|
|
let result = result.unwrap();
|
|
assert_eq!(result.len(), cycle_length as usize);
|
|
let expected_result = get_range_of_hashes(7, 15);
|
|
assert_eq!(result, expected_result);
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_signed_hashes_scenario_2() {
|
|
/*
|
|
* Block 1, attestation 0.
|
|
*/
|
|
let cycle_length: u8 = 8;
|
|
let block_slot: u64 = 1;
|
|
let attestation_slot: u64 = 0;
|
|
let current_hashes = get_range_of_hashes(0, 16);
|
|
let oblique_hashes = vec![];
|
|
let result = attestation_parent_hashes(
|
|
cycle_length,
|
|
block_slot,
|
|
attestation_slot,
|
|
¤t_hashes,
|
|
&oblique_hashes);
|
|
let result = result.unwrap();
|
|
assert_eq!(result.len(), cycle_length as usize);
|
|
let expected_result = get_range_of_hashes(7, 15);
|
|
assert_eq!(result, expected_result);
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_signed_hashes_scenario_3() {
|
|
/*
|
|
* attestation_slot too large
|
|
*/
|
|
let cycle_length: u8 = 8;
|
|
let block_slot: u64 = 100;
|
|
let attestation_slot: u64 = 100;
|
|
let current_hashes = get_range_of_hashes(0, 16);
|
|
let oblique_hashes = vec![];
|
|
let result = attestation_parent_hashes(
|
|
cycle_length,
|
|
block_slot,
|
|
attestation_slot,
|
|
¤t_hashes,
|
|
&oblique_hashes);
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_signed_hashes_scenario_4() {
|
|
/*
|
|
* Current hashes too small
|
|
*/
|
|
let cycle_length: u8 = 8;
|
|
let block_slot: u64 = 100;
|
|
let attestation_slot: u64 = 99;
|
|
let current_hashes = get_range_of_hashes(0, 15);
|
|
let oblique_hashes = vec![];
|
|
let result = attestation_parent_hashes(
|
|
cycle_length,
|
|
block_slot,
|
|
attestation_slot,
|
|
¤t_hashes,
|
|
&oblique_hashes);
|
|
assert!(result.is_err());
|
|
}
|
|
}
|