diff --git a/lighthouse/state/attestation_record.rs b/lighthouse/state/attestation_record/attestation_record.rs similarity index 99% rename from lighthouse/state/attestation_record.rs rename to lighthouse/state/attestation_record/attestation_record.rs index a1997613fc..3fb1c8b0d9 100644 --- a/lighthouse/state/attestation_record.rs +++ b/lighthouse/state/attestation_record/attestation_record.rs @@ -22,7 +22,7 @@ pub const MIN_SSZ_ATTESTION_RECORD_LENGTH: usize = { 4 + BLS_AGG_SIG_BYTE_SIZE // aggregate sig (two 256 bit points) }; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq)] pub struct AttestationRecord { pub slot: u64, pub shard_id: u16, diff --git a/lighthouse/state/attestation_record/mod.rs b/lighthouse/state/attestation_record/mod.rs new file mode 100644 index 0000000000..74a5d8c347 --- /dev/null +++ b/lighthouse/state/attestation_record/mod.rs @@ -0,0 +1,17 @@ +use super::bls; +use super::ssz; +use super::utils; + + +mod attestation_record; +mod ssz_splitter; + +pub use self::attestation_record::{ + AttestationRecord, + MIN_SSZ_ATTESTION_RECORD_LENGTH, +}; +pub use self::ssz_splitter::{ + split_all, + split_one, + AttestationSplitError, +}; diff --git a/lighthouse/state/attestation_record/ssz_splitter.rs b/lighthouse/state/attestation_record/ssz_splitter.rs new file mode 100644 index 0000000000..4562b1fb33 --- /dev/null +++ b/lighthouse/state/attestation_record/ssz_splitter.rs @@ -0,0 +1,139 @@ +use super::MIN_SSZ_ATTESTION_RECORD_LENGTH as MIN_LENGTH; +use super::ssz::LENGTH_BYTES; +use super::ssz::decode::decode_length; + +#[derive(Debug, PartialEq)] +pub enum AttestationSplitError { + TooShort, +} + +/// Given some ssz slice, find the bounds of each serialized AttestationRecord and return a vec of +/// slices point to each. +pub fn split_all<'a>(full_ssz: &'a [u8], index: usize) + -> Result, AttestationSplitError> +{ + let mut v = vec![]; + let mut index = index; + while index < full_ssz.len() - 1 { + let (slice, i) = split_one(full_ssz, index)?; + v.push(slice); + index = i; + } + Ok(v) +} + +/// Given some ssz slice, find the bounds of one serialized AttestationRecord +/// and return a slice pointing to that. +pub fn split_one<'a>(full_ssz: &'a [u8], index: usize) + -> Result<(&'a [u8], usize), AttestationSplitError> +{ + if full_ssz.len() < MIN_LENGTH { + return Err(AttestationSplitError::TooShort); + } + + let hashes_len = decode_length(full_ssz, 10, LENGTH_BYTES) + .map_err(|_| AttestationSplitError::TooShort)?; + + let bitfield_len = decode_length( + full_ssz, hashes_len + 46, + LENGTH_BYTES) + .map_err(|_| AttestationSplitError::TooShort)?; + + // Subtract one because the min length assume 1 byte of bitfield + let len = MIN_LENGTH + + hashes_len + + bitfield_len.saturating_sub(1); + + if full_ssz.len() < len { + return Err(AttestationSplitError::TooShort); + } + + Ok((&full_ssz[index..(index + len)], index + len)) +} + +#[cfg(test)] +mod tests { + use super::*; + use super::super::AttestationRecord; + use super::super::utils::types::{ + Hash256, + Bitfield, + }; + use super::super::bls::AggregateSignature; + use super::super::ssz::{ + SszStream, + Decodable, + }; + + fn get_two_records() -> Vec { + let a = AttestationRecord { + slot: 7, + shard_id: 9, + oblique_parent_hashes: vec![Hash256::from(&vec![14; 32][..])], + shard_block_hash: Hash256::from(&vec![15; 32][..]), + attester_bitfield: Bitfield::from(&vec![17; 42][..]), + justified_slot: 19, + justified_block_hash: Hash256::from(&vec![15; 32][..]), + aggregate_sig: AggregateSignature::new(), + }; + let b = AttestationRecord { + slot: 9, + shard_id: 7, + oblique_parent_hashes: vec![Hash256::from(&vec![15; 32][..])], + shard_block_hash: Hash256::from(&vec![14; 32][..]), + attester_bitfield: Bitfield::from(&vec![19; 42][..]), + justified_slot: 15, + justified_block_hash: Hash256::from(&vec![17; 32][..]), + aggregate_sig: AggregateSignature::new(), + }; + vec![a, b] + } + + #[test] + fn test_attestation_ssz_split() { + let ars = get_two_records(); + let a = ars[0].clone(); + let b = ars[1].clone(); + + + /* + * Test split one + */ + let mut ssz_stream = SszStream::new(); + ssz_stream.append(&a); + let ssz = ssz_stream.drain(); + let (a_ssz, i) = split_one(&ssz, 0).unwrap(); + assert_eq!(i, ssz.len()); + let (decoded_a, _) = AttestationRecord::ssz_decode(a_ssz, 0) + .unwrap(); + assert_eq!(a, decoded_a); + + /* + * Test split two + */ + let mut ssz_stream = SszStream::new(); + ssz_stream.append(&a); + ssz_stream.append(&b); + let ssz = ssz_stream.drain(); + let ssz_vec = split_all(&ssz, 0).unwrap(); + let (decoded_a, _) = + AttestationRecord::ssz_decode(ssz_vec[0], 0) + .unwrap(); + let (decoded_b, _) = + AttestationRecord::ssz_decode(ssz_vec[1], 0) + .unwrap(); + assert_eq!(a, decoded_a); + assert_eq!(b, decoded_b); + + /* + * Test split two with shortened ssz + */ + let mut ssz_stream = SszStream::new(); + ssz_stream.append(&a); + ssz_stream.append(&b); + let ssz = ssz_stream.drain(); + let ssz = &ssz[0..ssz.len() - 1]; + assert!(split_all(&ssz, 0).is_err()); + } +} + diff --git a/lighthouse/state/block/ssz_block.rs b/lighthouse/state/block/ssz_block.rs index 3c430c251a..a90ff13510 100644 --- a/lighthouse/state/block/ssz_block.rs +++ b/lighthouse/state/block/ssz_block.rs @@ -13,9 +13,6 @@ use super::attestation_record::MIN_SSZ_ATTESTION_RECORD_LENGTH; pub enum BlockValidatorError { TooShort, TooLong, - BadPowHash, - SlotTooLow, - SlotTooHigh, } const LENGTH_BYTES: usize = 4; @@ -23,7 +20,7 @@ const LENGTH_BYTES: usize = 4; /// Allows for reading of block values directly from serialized ssz bytes. /// /// The purpose of this struct is to provide the functionality to read block fields directly from -/// some serialized SSZ slice, effectively allowing us to read the block without fully +/// some serialized SSZ slice allowing us to read the block without fully /// de-serializing it. /// /// This struct should be as "zero-copy" as possible. The `ssz` field is a reference to some slice diff --git a/lighthouse/state/validation/attestation_validation.rs b/lighthouse/state/validation/attestation/attestation_validation.rs similarity index 100% rename from lighthouse/state/validation/attestation_validation.rs rename to lighthouse/state/validation/attestation/attestation_validation.rs index aa94a91bca..db19db0bca 100644 --- a/lighthouse/state/validation/attestation_validation.rs +++ b/lighthouse/state/validation/attestation/attestation_validation.rs @@ -52,6 +52,11 @@ fn bytes_for_bits(bits: usize) -> usize { (bits.saturating_sub(1) / 8) + 1 } +fn any_of_last_n_bits_are_set(byte: &u8, n: usize) -> bool { + let shift = 8_u8.saturating_sub(n as u8); + ((!0 >> shift) & byte) > 0 +} + pub fn validate_attestation(a: &AttestationRecord, block_slot: u64, cycle_length: u8, @@ -162,11 +167,6 @@ pub fn validate_attestation(a: &AttestationRecord, Ok((signature_valid, voted_hashmap)) } -fn any_of_last_n_bits_are_set(byte: &u8, n: usize) -> bool { - let shift = 8_u8.saturating_sub(n as u8); - ((!0 >> shift) & byte) > 0 -} - /// Generates the message used to validate the signature provided with an AttestationRecord. /// /// Ensures that the signer of the message has a view of the chain that is compatible with ours. diff --git a/lighthouse/state/validation/attestation/mod.rs b/lighthouse/state/validation/attestation/mod.rs new file mode 100644 index 0000000000..b4b782a0a7 --- /dev/null +++ b/lighthouse/state/validation/attestation/mod.rs @@ -0,0 +1,11 @@ +use super::db; +use super::bls; +use super::AttestationRecord; +use super::ssz; +use super::attestation_parent_hashes; +use super::utils; + +mod attestation_validation; +mod signatures; + +pub use self::attestation_validation::validate_attestation; diff --git a/lighthouse/state/validation/signatures.rs b/lighthouse/state/validation/attestation/signatures.rs similarity index 100% rename from lighthouse/state/validation/signatures.rs rename to lighthouse/state/validation/attestation/signatures.rs diff --git a/lighthouse/state/validation/mod.rs b/lighthouse/state/validation/mod.rs index c11d2c3c22..b09805d6c9 100644 --- a/lighthouse/state/validation/mod.rs +++ b/lighthouse/state/validation/mod.rs @@ -14,6 +14,5 @@ use super::ssz; use super::transition::attestation_parent_hashes; use super::utils; -mod attestation_validation; -mod signatures; +mod attestation; mod ssz_block; diff --git a/lighthouse/state/validator_record.rs b/lighthouse/state/validator_record.rs index 9961447da6..323c9e88d4 100644 --- a/lighthouse/state/validator_record.rs +++ b/lighthouse/state/validator_record.rs @@ -3,8 +3,6 @@ extern crate rand; use super::utils::types::{ Hash256, Address, U256 }; use super::bls::{ PublicKey, Keypair }; -use self::rand::thread_rng; - pub struct ValidatorRecord { pub pubkey: PublicKey, pub withdrawal_shard: u16,