use bitvec::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use near_schema_checker_lib::ProtocolSchema;
#[derive(
BorshSerialize,
BorshDeserialize,
serde::Serialize,
serde::Deserialize,
Debug,
Clone,
Eq,
PartialEq,
Default,
ProtocolSchema,
)]
pub struct ChunkEndorsementsBitmap {
inner: Vec<Vec<u8>>,
}
type BitVecType = BitVec<u8, Lsb0>;
impl ChunkEndorsementsBitmap {
pub fn new(num_shards: usize) -> Self {
Self { inner: vec![Default::default(); num_shards] }
}
pub fn genesis() -> Self {
Self::new(0)
}
pub fn from_bytes(bytes: Vec<Vec<u8>>) -> Self {
Self { inner: bytes }
}
pub fn bytes(&self) -> Vec<Vec<u8>> {
self.inner.clone()
}
pub fn from_endorsements(shards_to_endorsements: Vec<Vec<bool>>) -> Self {
let mut bitmap = ChunkEndorsementsBitmap::new(shards_to_endorsements.len());
for (shard_index, endorsements) in shards_to_endorsements.into_iter().enumerate() {
bitmap.add_endorsements(shard_index, endorsements);
}
bitmap
}
pub fn add_endorsements(&mut self, shard_index: usize, endorsements: Vec<bool>) {
let bitvec: BitVecType = endorsements.iter().collect();
self.inner[shard_index] = bitvec.into();
}
pub fn iter(&self, shard_index: usize) -> Box<dyn Iterator<Item = bool>> {
let bitvec = BitVecType::from_vec(self.inner[shard_index].clone());
Box::new(bitvec.into_iter())
}
pub fn num_shards(&self) -> usize {
self.inner.len()
}
pub fn len(&self, shard_index: usize) -> Option<usize> {
self.inner.get(shard_index).map(|v| v.len() * 8)
}
}
#[cfg(test)]
mod tests {
use super::ChunkEndorsementsBitmap;
use itertools::Itertools;
use rand::Rng;
const NUM_SHARDS: usize = 4;
fn assert_bitmap(
bitmap: &ChunkEndorsementsBitmap,
num_assignments: usize,
expected_endorsements: &Vec<Vec<bool>>,
) {
for (shard_index, endorsements) in expected_endorsements.iter().enumerate() {
let num_bits = bitmap.len(shard_index).unwrap();
let bits = bitmap.iter(shard_index).collect_vec();
assert_eq!(num_bits, bits.len());
assert_eq!(num_bits, num_assignments.div_ceil(8) * 8);
let mut expected_bits = endorsements.clone();
expected_bits.extend(std::iter::repeat(false).take(num_bits - num_assignments));
assert_eq!(bits, expected_bits);
}
}
fn run_bitmap_test(num_assignments: usize, num_produced: usize) {
let mut rng = rand::thread_rng();
let mut bitmap = ChunkEndorsementsBitmap::new(NUM_SHARDS);
let mut expected_endorsements = vec![];
for shard_index in 0..NUM_SHARDS {
let mut endorsements = vec![false; num_assignments];
for _ in 0..num_produced {
endorsements[rng.gen_range(0..num_assignments)] = true;
}
expected_endorsements.push(endorsements.clone());
bitmap.add_endorsements(shard_index, endorsements);
}
assert_bitmap(&bitmap, num_assignments, &expected_endorsements);
let serialized = borsh::to_vec(&bitmap).unwrap();
let bitmap = borsh::from_slice::<ChunkEndorsementsBitmap>(&serialized).unwrap();
assert_bitmap(&bitmap, num_assignments, &expected_endorsements);
}
#[test]
fn test_chunk_endorsements_bitmap() {
run_bitmap_test(0, 0);
run_bitmap_test(60, 20);
run_bitmap_test(68, 20);
run_bitmap_test(125, 30);
run_bitmap_test(130, 50);
run_bitmap_test(300, 100);
}
}