Fixes bug with ssz encoding of BooleanBitfield

This commit is contained in:
Alex Stokes 2018-11-20 12:27:44 -08:00 committed by mjkeating
parent 38d9d7ac92
commit 8c78dde43b
2 changed files with 64 additions and 2 deletions

View File

@ -31,7 +31,7 @@ impl Encodable for AttestationRecord {
s.append(&self.shard_id); s.append(&self.shard_id);
s.append_vec(&self.oblique_parent_hashes); s.append_vec(&self.oblique_parent_hashes);
s.append(&self.shard_block_hash); s.append(&self.shard_block_hash);
s.append_vec(&self.attester_bitfield.to_bytes()); s.append(&self.attester_bitfield);
s.append(&self.justified_slot); s.append(&self.justified_slot);
s.append(&self.justified_block_hash); s.append(&self.justified_block_hash);
s.append_vec(&self.aggregate_sig.as_bytes()); s.append_vec(&self.aggregate_sig.as_bytes());

View File

@ -86,6 +86,7 @@ impl BooleanBitfield {
} }
/// Returns a vector of bytes representing the bitfield /// Returns a vector of bytes representing the bitfield
/// Note that this returns the bit layout of the underlying implementation in the `bit-vec` crate.
pub fn to_bytes(&self) -> Vec<u8> { pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes() self.0.to_bytes()
} }
@ -99,6 +100,28 @@ impl default::Default for BooleanBitfield {
} }
} }
// borrowed from bit_vec crate
fn reverse_bits(byte: u8) -> u8 {
let mut result = 0;
for i in 0..8 {
result = result | ((byte >> i) & 1) << (7 - i);
}
result
}
impl ssz::Encodable for BooleanBitfield {
// ssz_append encodes Self according to the `ssz` spec.
// Note that we have to flip the endianness of the encoding with `reverse_bits` to account for an implementation detail of `bit-vec` crate.
fn ssz_append(&self, s: &mut ssz::SszStream) {
let bytes: Vec<u8> = self
.to_bytes()
.iter()
.map(|&byte| reverse_bits(byte))
.collect();
s.append_vec(&bytes);
}
}
impl ssz::Decodable for BooleanBitfield { impl ssz::Decodable for BooleanBitfield {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), ssz::DecodeError> { fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), ssz::DecodeError> {
let len = ssz::decode::decode_length(bytes, index, ssz::LENGTH_BYTES)?; let len = ssz::decode::decode_length(bytes, index, ssz::LENGTH_BYTES)?;
@ -109,7 +132,18 @@ impl ssz::Decodable for BooleanBitfield {
if len == 0 { if len == 0 {
Ok((BooleanBitfield::new(), index + ssz::LENGTH_BYTES)) Ok((BooleanBitfield::new(), index + ssz::LENGTH_BYTES))
} else { } else {
let field = BooleanBitfield::from_bytes(&bytes[(index + 4)..(index + len + 4)]); let bytes = &bytes[(index + 4)..(index + len + 4)];
let mut field = BooleanBitfield::from_elem(0, false);
for (byte_index, byte) in bytes.iter().enumerate() {
for i in 0..8 {
let bit = byte & (1 << i);
if bit != 0 {
field.set(8 * byte_index + i, true);
}
}
}
let index = index + ssz::LENGTH_BYTES + len; let index = index + ssz::LENGTH_BYTES + len;
Ok((field, index)) Ok((field, index))
} }
@ -119,6 +153,7 @@ impl ssz::Decodable for BooleanBitfield {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use ssz::SszStream;
#[test] #[test]
fn test_empty_bitfield() { fn test_empty_bitfield() {
@ -231,4 +266,31 @@ mod tests {
let field = BooleanBitfield::from_elem(13, true); let field = BooleanBitfield::from_elem(13, true);
assert_eq!(field.num_bytes(), 2); assert_eq!(field.num_bytes(), 2);
} }
#[test]
fn test_ssz_encode() {
let field = BooleanBitfield::from_elem(5, true);
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!(stream.drain(), vec![0, 0, 0, 1, 31]);
let field = BooleanBitfield::from_elem(18, true);
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!(stream.drain(), vec![0, 0, 0, 3, 255, 255, 3]);
}
#[test]
fn test_ssz_decode() {
let encoded = vec![0, 0, 0, 1, 31];
let (field, _): (BooleanBitfield, usize) = ssz::decode_ssz(&encoded, 0).unwrap();
let expected = BooleanBitfield::from_elem(5, true);
assert_eq!(field, expected);
let encoded = vec![0, 0, 0, 3, 255, 255, 3];
let (field, _): (BooleanBitfield, usize) = ssz::decode_ssz(&encoded, 0).unwrap();
let expected = BooleanBitfield::from_elem(18, true);
assert_eq!(field, expected);
}
} }