mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-22 22:34:45 +00:00
163 lines
6.1 KiB
Rust
163 lines
6.1 KiB
Rust
use super::bls::BLS_AGG_SIG_BYTE_SIZE;
|
|
use super::ssz::decode::decode_length;
|
|
use super::ssz::LENGTH_BYTES;
|
|
use super::types::attestation_data::SSZ_ATTESTION_DATA_LENGTH;
|
|
use super::types::attestation_record::MIN_SSZ_ATTESTION_RECORD_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_attestations<'a>(
|
|
full_ssz: &'a [u8],
|
|
index: usize,
|
|
) -> Result<Vec<&'a [u8]>, AttestationSplitError> {
|
|
let mut v = vec![];
|
|
let mut index = index;
|
|
while index < full_ssz.len() - 1 {
|
|
let (slice, i) = split_one_attestation(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_attestation(
|
|
full_ssz: &[u8],
|
|
index: usize,
|
|
) -> Result<(&[u8], usize), AttestationSplitError> {
|
|
let length = determine_ssz_attestation_len(full_ssz, index)?;
|
|
let end = index + length;
|
|
|
|
// The check to ensure that the slice exists _should_ be redundant as it is already checked in
|
|
// `determine_ssz_attestation_len`, however it is checked here again for additional safety
|
|
// against panics.
|
|
match full_ssz.get(index..end) {
|
|
None => Err(AttestationSplitError::TooShort),
|
|
Some(slice) => Ok((slice, end)),
|
|
}
|
|
}
|
|
|
|
/// Given some SSZ, assume that a serialized `AttestationRecord` begins at the `index` position and
|
|
/// attempt to find the length (in bytes) of that serialized `AttestationRecord`.
|
|
///
|
|
/// This function does not perform validation on the `AttestationRecord`. It is very likely that
|
|
/// given some sufficiently long non-`AttestationRecord` bytes it will not raise an error.
|
|
fn determine_ssz_attestation_len(
|
|
full_ssz: &[u8],
|
|
index: usize,
|
|
) -> Result<usize, AttestationSplitError> {
|
|
if full_ssz.len() < MIN_SSZ_ATTESTION_RECORD_LENGTH {
|
|
return Err(AttestationSplitError::TooShort);
|
|
}
|
|
|
|
let data_struct_end = index + SSZ_ATTESTION_DATA_LENGTH;
|
|
|
|
// Determine the end of the first bitfield.
|
|
let participation_bitfield_len = decode_length(full_ssz, data_struct_end, LENGTH_BYTES)
|
|
.map_err(|_| AttestationSplitError::TooShort)?;
|
|
let participation_bitfield_end = data_struct_end + LENGTH_BYTES + participation_bitfield_len;
|
|
|
|
// Determine the end of the second bitfield.
|
|
let custody_bitfield_len = decode_length(full_ssz, participation_bitfield_end, LENGTH_BYTES)
|
|
.map_err(|_| AttestationSplitError::TooShort)?;
|
|
let custody_bitfield_end = participation_bitfield_end + LENGTH_BYTES + custody_bitfield_len;
|
|
|
|
// Determine the very end of the AttestationRecord.
|
|
let agg_sig_end = custody_bitfield_end + LENGTH_BYTES + BLS_AGG_SIG_BYTE_SIZE;
|
|
|
|
if agg_sig_end > full_ssz.len() {
|
|
Err(AttestationSplitError::TooShort)
|
|
} else {
|
|
Ok(agg_sig_end - index)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::super::bls::AggregateSignature;
|
|
use super::super::ssz::{Decodable, SszStream};
|
|
use super::super::types::{AttestationData, AttestationRecord, Bitfield, Hash256};
|
|
use super::*;
|
|
|
|
fn get_two_records() -> Vec<AttestationRecord> {
|
|
let a = AttestationRecord {
|
|
data: AttestationData {
|
|
slot: 7,
|
|
shard: 9,
|
|
beacon_block_hash: Hash256::from("a_beacon".as_bytes()),
|
|
epoch_boundary_hash: Hash256::from("a_epoch".as_bytes()),
|
|
shard_block_hash: Hash256::from("a_shard".as_bytes()),
|
|
latest_crosslink_hash: Hash256::from("a_xlink".as_bytes()),
|
|
justified_slot: 19,
|
|
justified_block_hash: Hash256::from("a_justified".as_bytes()),
|
|
},
|
|
participation_bitfield: Bitfield::from_bytes(&vec![17; 42][..]),
|
|
custody_bitfield: Bitfield::from_bytes(&vec![255; 12][..]),
|
|
aggregate_sig: AggregateSignature::new(),
|
|
};
|
|
let b = AttestationRecord {
|
|
data: AttestationData {
|
|
slot: 9,
|
|
shard: 7,
|
|
beacon_block_hash: Hash256::from("b_beacon".as_bytes()),
|
|
epoch_boundary_hash: Hash256::from("b_epoch".as_bytes()),
|
|
shard_block_hash: Hash256::from("b_shard".as_bytes()),
|
|
latest_crosslink_hash: Hash256::from("b_xlink".as_bytes()),
|
|
justified_slot: 15,
|
|
justified_block_hash: Hash256::from("b_justified".as_bytes()),
|
|
},
|
|
participation_bitfield: Bitfield::from_bytes(&vec![1; 42][..]),
|
|
custody_bitfield: Bitfield::from_bytes(&vec![11; 3][..]),
|
|
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_attestation(&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_attestations(&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_attestations(&ssz, 0).is_err());
|
|
}
|
|
}
|