[PIBD_IMPL] BitmapAccumulator Serialization Fix (#3705)

* fix for writing / calculating incorrect length for negative indices

* update capabilities with new version of PIBD hist

* remove incorrect comment

* fix capabilities flag, trace output

* test fix
This commit is contained in:
Yeastplume 2022-04-20 13:44:53 +01:00 committed by GitHub
parent aa2a2a98df
commit 5efd70a17b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 19 deletions

View file

@ -418,7 +418,11 @@ impl Writeable for BitmapBlock {
writer.write_u8((length / BitmapChunk::LEN_BITS) as u8)?; writer.write_u8((length / BitmapChunk::LEN_BITS) as u8)?;
let count_pos = self.inner.iter().filter(|&v| v).count() as u32; let count_pos = self.inner.iter().filter(|&v| v).count() as u32;
let count_neg = Self::NBITS - count_pos;
// Negative count needs to be adjusted if the block is not full,
// which affects the choice of serialization mode and size written
let count_neg = length as u32 - count_pos;
let threshold = Self::NBITS / 16; let threshold = Self::NBITS / 16;
if count_pos < threshold { if count_pos < threshold {
// Write positive indices // Write positive indices
@ -518,17 +522,19 @@ mod tests {
use rand::thread_rng; use rand::thread_rng;
use std::io::Cursor; use std::io::Cursor;
fn test_roundtrip(entries: usize, inverse: bool, encoding: u8, length: usize) { fn test_roundtrip(entries: usize, inverse: bool, encoding: u8, length: usize, n_blocks: usize) {
let mut rng = thread_rng(); let mut rng = thread_rng();
let mut block = BitmapBlock::new(64); let mut block = BitmapBlock::new(n_blocks);
if inverse { if inverse {
block.inner.negate(); block.inner.negate();
} }
let range_size = n_blocks * BitmapChunk::LEN_BITS as usize;
// Flip `entries` bits in random spots // Flip `entries` bits in random spots
let mut count = 0; let mut count = 0;
while count < entries { while count < entries {
let idx = rng.gen_range(0, BitmapBlock::NBITS as usize); let idx = rng.gen_range(0, range_size);
if block.inner.get(idx).unwrap() == inverse { if block.inner.get(idx).unwrap() == inverse {
count += 1; count += 1;
block.inner.set(idx, !inverse); block.inner.set(idx, !inverse);
@ -562,21 +568,35 @@ mod tests {
fn block_ser_roundtrip() { fn block_ser_roundtrip() {
let threshold = BitmapBlock::NBITS as usize / 16; let threshold = BitmapBlock::NBITS as usize / 16;
let entries = thread_rng().gen_range(threshold, 4 * threshold); let entries = thread_rng().gen_range(threshold, 4 * threshold);
test_roundtrip(entries, false, 0, 2 + BitmapBlock::NBITS as usize / 8); test_roundtrip(entries, false, 0, 2 + BitmapBlock::NBITS as usize / 8, 64);
test_roundtrip(entries, true, 0, 2 + BitmapBlock::NBITS as usize / 8); test_roundtrip(entries, true, 0, 2 + BitmapBlock::NBITS as usize / 8, 64);
} }
#[test] #[test]
fn sparse_block_ser_roundtrip() { fn sparse_block_ser_roundtrip() {
let entries = let entries =
thread_rng().gen_range(BitmapChunk::LEN_BITS, BitmapBlock::NBITS as usize / 16); thread_rng().gen_range(BitmapChunk::LEN_BITS, BitmapBlock::NBITS as usize / 16);
test_roundtrip(entries, false, 1, 4 + 2 * entries); test_roundtrip(entries, false, 1, 4 + 2 * entries, 64);
}
#[test]
fn sparse_unfull_block_ser_roundtrip() {
let entries =
thread_rng().gen_range(BitmapChunk::LEN_BITS, BitmapBlock::NBITS as usize / 16);
test_roundtrip(entries, false, 1, 4 + 2 * entries, 61);
} }
#[test] #[test]
fn abdundant_block_ser_roundtrip() { fn abdundant_block_ser_roundtrip() {
let entries = let entries =
thread_rng().gen_range(BitmapChunk::LEN_BITS, BitmapBlock::NBITS as usize / 16); thread_rng().gen_range(BitmapChunk::LEN_BITS, BitmapBlock::NBITS as usize / 16);
test_roundtrip(entries, true, 2, 4 + 2 * entries); test_roundtrip(entries, true, 2, 4 + 2 * entries, 64);
}
#[test]
fn abdundant_unfull_block_ser_roundtrip() {
let entries =
thread_rng().gen_range(BitmapChunk::LEN_BITS, BitmapBlock::NBITS as usize / 16);
test_roundtrip(entries, true, 2, 4 + 2 * entries, 61);
} }
} }

View file

@ -391,6 +391,8 @@ bitflags! {
const PIBD_HIST = 0b0001_0000; const PIBD_HIST = 0b0001_0000;
/// Can provide historical blocks for archival sync. /// Can provide historical blocks for archival sync.
const BLOCK_HIST = 0b0010_0000; const BLOCK_HIST = 0b0010_0000;
/// As above, with crucial serialization fix #3705 applied
const PIBD_HIST_1 = 0b0100_0000;
} }
} }
@ -402,6 +404,7 @@ impl Default for Capabilities {
| Capabilities::PEER_LIST | Capabilities::PEER_LIST
| Capabilities::TX_KERNEL_HASH | Capabilities::TX_KERNEL_HASH
| Capabilities::PIBD_HIST | Capabilities::PIBD_HIST
| Capabilities::PIBD_HIST_1
} }
} }

View file

@ -42,6 +42,7 @@ fn default_capabilities() {
assert!(x.contains(Capabilities::PEER_LIST)); assert!(x.contains(Capabilities::PEER_LIST));
assert!(x.contains(Capabilities::TX_KERNEL_HASH)); assert!(x.contains(Capabilities::TX_KERNEL_HASH));
assert!(x.contains(Capabilities::PIBD_HIST)); assert!(x.contains(Capabilities::PIBD_HIST));
assert!(x.contains(Capabilities::PIBD_HIST_1));
assert_eq!( assert_eq!(
x, x,
@ -50,5 +51,6 @@ fn default_capabilities() {
| Capabilities::PEER_LIST | Capabilities::PEER_LIST
| Capabilities::TX_KERNEL_HASH | Capabilities::TX_KERNEL_HASH
| Capabilities::PIBD_HIST | Capabilities::PIBD_HIST
| Capabilities::PIBD_HIST_1
); );
} }

View file

@ -55,11 +55,7 @@ fn test_capabilities() {
assert_eq!( assert_eq!(
expected, expected,
p2p::types::Capabilities::from_bits_truncate(0b11111 as u32), p2p::types::Capabilities::from_bits_truncate(0b1011111 as u32),
);
assert_eq!(
expected,
p2p::types::Capabilities::from_bits_truncate(0b00011111 as u32),
); );
assert_eq!( assert_eq!(

View file

@ -305,10 +305,10 @@ impl StateSync {
let max_diff = peers_iter().max_difficulty().unwrap_or(Difficulty::zero()); let max_diff = peers_iter().max_difficulty().unwrap_or(Difficulty::zero());
let peers_iter_max = || peers_iter().with_difficulty(|x| x >= max_diff); let peers_iter_max = || peers_iter().with_difficulty(|x| x >= max_diff);
// Then, further filter by PIBD capabilities // Then, further filter by PIBD capabilities v1
let peers_iter_pibd = || { let peers_iter_pibd = || {
peers_iter_max() peers_iter_max()
.with_capabilities(Capabilities::PIBD_HIST) .with_capabilities(Capabilities::PIBD_HIST_1)
.connected() .connected()
}; };
@ -335,10 +335,10 @@ impl StateSync {
} }
// Choose a random "most work" peer, preferring outbound if at all possible. // Choose a random "most work" peer, preferring outbound if at all possible.
let peer = peers_iter_pibd().outbound().choose_random().or_else(|| { let peer = peers_iter_pibd()
warn!("no suitable outbound peer for pibd message, considering inbound"); .outbound()
peers_iter_pibd().inbound().choose_random() .choose_random()
}); .or_else(|| peers_iter_pibd().inbound().choose_random());
trace!("Chosen peer is {:?}", peer); trace!("Chosen peer is {:?}", peer);
if let Some(p) = peer { if let Some(p) = peer {