diff --git a/src/state/active_state.rs b/src/state/active_state.rs index db93ef9bd..b43be44c7 100644 --- a/src/state/active_state.rs +++ b/src/state/active_state.rs @@ -20,7 +20,7 @@ impl ActiveState { Self { height: 0, randao: Sha256Digest::zero(), - ffg_voter_bitfield: Vec::new(), + ffg_voter_bitfield: Bitfield::new(), recent_attesters: Vec::new(), partial_crosslinks: Vec::new(), total_skip_count: 0, @@ -96,11 +96,11 @@ mod tests { } #[test] - fn test_serialization() { + fn test_rlp_serialization() { let a = ActiveState { height: 100, randao: Sha256Digest::zero(), - ffg_voter_bitfield: Vec::new(), + ffg_voter_bitfield: Bitfield::new(), recent_attesters: Vec::new(), partial_crosslinks: Vec::new(), total_skip_count: 99, diff --git a/src/state/aggregate_vote.rs b/src/state/aggregate_vote.rs index 690fa907e..c855d6aed 100644 --- a/src/state/aggregate_vote.rs +++ b/src/state/aggregate_vote.rs @@ -14,7 +14,7 @@ impl AggregateVote { Self { shard_id: 0, shard_block_hash: Sha256Digest::zero(), - notary_bitfield: Vec::new(), + notary_bitfield: Bitfield::new(), aggregate_sig: AggregateSignature::new() } } @@ -46,11 +46,11 @@ mod tests { } #[test] - fn test_serialization() { + fn test_rlp_serialization() { let a = AggregateVote { shard_id: 100, shard_block_hash: Sha256Digest::zero(), - notary_bitfield: Vec::new(), + notary_bitfield: Bitfield::new(), aggregate_sig: AggregateSignature::new() }; let e = rlp::encode(&a); diff --git a/src/state/block.rs b/src/state/block.rs index 58c069d93..cb6e1fb02 100644 --- a/src/state/block.rs +++ b/src/state/block.rs @@ -23,7 +23,7 @@ impl Block { parent_hash: parent_hash, skip_count: 0, randao_reveal: randao_reveal, - attestation_bitfield: Vec::new(), + attestation_bitfield: Bitfield::new(), attestation_aggregate_sig: AggregateSignature::new(), shard_aggregate_votes: Vec::new(), main_chain_ref: main_chain_ref, @@ -147,12 +147,12 @@ mod tests { } #[test] - fn test_serialization() { + fn test_rlp_serialization() { let b = Block { parent_hash: Sha256Digest::zero(), skip_count: 100, randao_reveal: Sha256Digest::zero(), - attestation_bitfield: Vec::new(), + attestation_bitfield: Bitfield::new(), attestation_aggregate_sig: AggregateSignature::new(), shard_aggregate_votes: Vec::new(), main_chain_ref: Sha256Digest::zero(), diff --git a/src/state/crosslink_record.rs b/src/state/crosslink_record.rs index 85913a67b..217f9e8f8 100644 --- a/src/state/crosslink_record.rs +++ b/src/state/crosslink_record.rs @@ -40,7 +40,7 @@ mod tests { } #[test] - fn test_serialization() { + fn test_rlp_serialization() { let c = CrosslinkRecord { epoch: 100, hash: Sha256Digest::zero() diff --git a/src/state/crystallized_state.rs b/src/state/crystallized_state.rs index 935be53cf..b2acb7da6 100644 --- a/src/state/crystallized_state.rs +++ b/src/state/crystallized_state.rs @@ -92,7 +92,7 @@ mod tests { use super::*; #[test] - fn test_serialization() { + fn test_rlp_serialization() { let a = CrystallizedState { active_validators: Vec::new(), queued_validators: Vec::new(), diff --git a/src/state/partial_crosslink_record.rs b/src/state/partial_crosslink_record.rs index 79b4d8147..536e61d17 100644 --- a/src/state/partial_crosslink_record.rs +++ b/src/state/partial_crosslink_record.rs @@ -13,7 +13,7 @@ impl PartialCrosslinkRecord { PartialCrosslinkRecord { shard_id: shard_id, shard_block_hash: shard_block_hash, - voter_bitfield: Vec::new() + voter_bitfield: Bitfield::new() } } } @@ -45,11 +45,11 @@ mod tests { } #[test] - fn test_serialization() { + fn test_rlp_serialization() { let p = PartialCrosslinkRecord { shard_id: 1, shard_block_hash: Sha256Digest::zero(), - voter_bitfield: Vec::new() + voter_bitfield: Bitfield::new() }; let e = rlp::encode(&p); assert_eq!(e.len(), 35); diff --git a/src/state/recent_proposer_record.rs b/src/state/recent_proposer_record.rs index d08369586..4db492fad 100644 --- a/src/state/recent_proposer_record.rs +++ b/src/state/recent_proposer_record.rs @@ -37,7 +37,7 @@ mod tests { use super::*; #[test] - fn test_serialization() { + fn test_rlp_serialization() { let index = 1; let randao_commitment = Sha256Digest::zero(); let balance_delta = 99; diff --git a/src/state/validator_record.rs b/src/state/validator_record.rs index ce9f9992f..87146073a 100644 --- a/src/state/validator_record.rs +++ b/src/state/validator_record.rs @@ -81,7 +81,7 @@ mod tests { } #[test] - fn test_serialization() { + fn test_rlp_serialization() { let keypair = get_dangerous_test_keypair(); let v = ValidatorRecord { pubkey: keypair.public, diff --git a/src/utils/boolean_bitfield.rs b/src/utils/boolean_bitfield.rs new file mode 100644 index 000000000..f83d57b89 --- /dev/null +++ b/src/utils/boolean_bitfield.rs @@ -0,0 +1,142 @@ +/* + * Implemenation of a bitfield as a vec. Only + * supports bytes (Vec) as the underlying + * storage. + * + * A future implementation should be more efficient, + * this is just to get the job done for now. + */ +extern crate rlp; +use self::rlp::{ RlpStream, Encodable }; + +pub struct BooleanBitfield{ + vec: Vec +} + +impl BooleanBitfield { + pub fn new() -> Self { + Self { + vec: vec![] + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + vec: Vec::with_capacity(capacity) + } + } + + // Output the bitfield as a big-endian vec of u8 + pub fn to_be_vec(&self) -> Vec { + let mut o = self.vec.clone(); + o.reverse(); + o + } + + pub fn get_bit(&self, i: &usize) -> bool { + self.get_bit_on_byte(*i % 8, *i / 8) + } + + fn get_bit_on_byte(&self, bit: usize, byte: usize) -> bool { + assert!(bit < 8); + if byte >= self.vec.len() { + false + } else { + self.vec[byte] & (1 << (bit as u8)) != 0 + } + } + + pub fn set_bit(&mut self, bit: &usize, to: &bool) { + self.set_bit_on_byte(*bit % 8, *bit / 8, to); + } + + fn set_bit_on_byte(&mut self, bit: usize, byte: usize, val: &bool) { + assert!(bit < 8); + if byte >= self.vec.len() { + self.vec.resize(byte + 1, 0); + } + match val { + true => self.vec[byte] = self.vec[byte] | (1 << (bit as u8)), + false => self.vec[byte] = self.vec[byte] & !(1 << (bit as u8)) + } + } +} + +impl Encodable for BooleanBitfield { + // TODO: ensure this is a sensible method of encoding + // the bitfield. Currently, it is treated as a list of + // bytes not as a string. I do not have any guidance as + // to which method is correct -- don't follow my lead + // without seeking authoritative advice. + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.to_be_vec()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::rlp; + + #[test] + fn test_bitfield_set() { + let mut b = BooleanBitfield::new(); + b.set_bit(&0, &false); + assert_eq!(b.to_be_vec(), [0]); + + b = BooleanBitfield::new(); + b.set_bit(&7, &true); + assert_eq!(b.to_be_vec(), [128]); + b.set_bit(&7, &false); + assert_eq!(b.to_be_vec(), [0]); + + b = BooleanBitfield::new(); + b.set_bit(&7, &true); + b.set_bit(&0, &true); + assert_eq!(b.to_be_vec(), [129]); + b.set_bit(&7, &false); + assert_eq!(b.to_be_vec(), [1]); + + b = BooleanBitfield::new(); + b.set_bit(&8, &true); + assert_eq!(b.to_be_vec(), [1, 0]); + b.set_bit(&8, &false); + assert_eq!(b.to_be_vec(), [0, 0]); + + b = BooleanBitfield::new(); + b.set_bit(&15, &true); + assert_eq!(b.to_be_vec(), [128, 0]); + b.set_bit(&15, &false); + assert_eq!(b.to_be_vec(), [0, 0]); + + b = BooleanBitfield::new(); + b.set_bit(&8, &true); + b.set_bit(&15, &true); + assert_eq!(b.to_be_vec(), [129, 0]); + b.set_bit(&15, &false); + assert_eq!(b.to_be_vec(), [1, 0]); + } + + #[test] + fn test_bitfield_get() { + let mut b = BooleanBitfield::new(); + let test_nums = vec![0, 8, 15, 42, 1337]; + for i in test_nums { + b = BooleanBitfield::new(); + assert_eq!(b.get_bit(&0), false); + b.set_bit(&0, &true); + assert_eq!(b.get_bit(&0), true); + b.set_bit(&0, &true); + } + } + + #[test] + fn test_bitfield_rlp_serialization() { + let mut b = BooleanBitfield::new(); + b.set_bit(&15, &true); + let e = rlp::encode(&b); + assert_eq!(e[0], 130); + assert_eq!(e[1], 128); + assert_eq!(e[2], 0); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e2428fe36..683e80d0a 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -8,3 +8,4 @@ use super::state::crystallized_state; pub mod types; pub mod bls; pub mod test_helpers; +pub mod boolean_bitfield; diff --git a/src/utils/types.rs b/src/utils/types.rs index f7fed2a90..6bc672600 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -1,6 +1,7 @@ use super::ethereum_types::{ H256, H160 }; use super::active_state::ActiveState; use super::crystallized_state::CrystallizedState; +use super::boolean_bitfield::BooleanBitfield; pub use super::blake2::Blake2s; @@ -33,4 +34,4 @@ impl StateHash { } } -pub type Bitfield = Vec; +pub type Bitfield = BooleanBitfield;