Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inclusions struct unit tests #1518

Merged
merged 6 commits into from
Sep 13, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 274 additions & 1 deletion polkadot/node/core/dispute-coordinator/src/scraping/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use polkadot_primitives::{
GroupIndex, Hash, HashT, HeadData, Id as ParaId,
};

use crate::LOG_TARGET;
use crate::{scraping::Inclusions, LOG_TARGET};

use super::ChainScraper;

Expand Down Expand Up @@ -662,3 +662,276 @@ fn inclusions_per_candidate_properly_adds_and_prunes() {
assert!(scraper.get_blocks_including_candidate(&candidate.hash()).len() == 0);
});
}

// ----- Inclusions tests -----

#[test]
fn inclusions_initialization() {
let inclusions = Inclusions::new();

assert!(inclusions.inclusions_inner.is_empty(), "Expected inclusions_inner to be empty");
assert!(
inclusions.candidates_by_block_number.is_empty(),
"Expected candidates_by_block_number to be empty"
);
}
tdimitrov marked this conversation as resolved.
Show resolved Hide resolved
#[test]
fn inclusions_insertion() {
let mut inclusions = Inclusions::new();
let candidate_receipt = make_candidate_receipt(get_magic_candidate_hash());
let candidate_hash = candidate_receipt.hash();
let block_number = 0;
let block_hash = get_block_number_hash(block_number);

inclusions.insert(candidate_hash, block_number, block_hash);

// Check inclusions_inner
assert!(inclusions.inclusions_inner.len() == 1, "Expected inclusions_inner to have length 1");
assert!(
inclusions.inclusions_inner.contains_key(&candidate_hash),
"Expected candidate_hash to be present in inclusions_inner"
);
let inner_map = inclusions.inclusions_inner.get(&candidate_hash).unwrap();
assert!(inner_map.len() == 1, "Expected inner_map to have length 1");
assert!(
inner_map.contains_key(&block_number),
Overkillus marked this conversation as resolved.
Show resolved Hide resolved
"Expected block_number to be present for the candidate_hash in inclusions_inner"
);
let hash_set = inner_map.get(&block_number).unwrap();
Overkillus marked this conversation as resolved.
Show resolved Hide resolved
assert!(hash_set.len() == 1, "Expected hash_map to have length 1");
assert!(
hash_set.contains(&block_hash),
"Expected block_hash to be present for the block_number in inclusions_inner"
);

// Check candidates_by_block_number
assert!(
inclusions.candidates_by_block_number.len() == 1,
"Expected candidates_by_block_number to have length 1"
);
assert!(
inclusions.candidates_by_block_number.contains_key(&block_number),
"Expected block_number to be present in candidates_by_block_number"
);
let candidate_set = inclusions.candidates_by_block_number.get(&block_number).unwrap();
Overkillus marked this conversation as resolved.
Show resolved Hide resolved
assert!(
candidate_set.len() == 1,
"Expected candidate_set to have length 1 for the block_number in candidates_by_block_number"
);
assert!(
candidate_set.contains(&candidate_hash),
"Expected candidate_hash to be present for the block_number in candidates_by_block_number"
);
}

#[test]
fn inclusions_get() {
let mut inclusions = Inclusions::new();
let candidate_receipt = make_candidate_receipt(get_magic_candidate_hash());
let candidate_hash = candidate_receipt.hash();

// Insert the candidate with multiple block numbers and block hashes
let block_numbers = [0, 1, 2];
let block_hashes: Vec<_> =
block_numbers.iter().map(|&num| get_block_number_hash(num)).collect();

for (&block_number, &block_hash) in block_numbers.iter().zip(&block_hashes) {
inclusions.insert(candidate_hash, block_number, block_hash);
}

// Call the get method for that candidate
let result = inclusions.get(&candidate_hash);

// Verify that the method returns the correct list of block numbers and hashes associated with
// that candidate
assert_eq!(
result.len(),
block_numbers.len(),
"Expected the same number of results as inserted block numbers"
);

for (&block_number, &block_hash) in block_numbers.iter().zip(&block_hashes) {
assert!(
result.contains(&(block_number, block_hash)),
"Expected to find ({}, {}) in the result",
block_number,
block_hash
);
}
}

#[test]
fn inclusions_iterative_removal() {
let mut inclusions = Inclusions::new();

// Insert 3 candidates in the specified blocks
let candidate1 = make_candidate_receipt(BlakeTwo256::hash(&"c1".encode())).hash();
let candidate2 = make_candidate_receipt(BlakeTwo256::hash(&"c2".encode())).hash();
let candidate3 = make_candidate_receipt(BlakeTwo256::hash(&"c3".encode())).hash();
let candidate4 = make_candidate_receipt(BlakeTwo256::hash(&"c4".encode())).hash();
let candidate5 = make_candidate_receipt(BlakeTwo256::hash(&"c5".encode())).hash();

// B 0 1 2 3
// C1 x
// C2 x
// C3 x x
// C4 x
// C5 x
inclusions.insert(candidate1, 0, get_block_number_hash(0));
inclusions.insert(candidate2, 1, get_block_number_hash(1));
inclusions.insert(candidate3, 0, get_block_number_hash(0));
inclusions.insert(candidate3, 2, get_block_number_hash(2));
inclusions.insert(candidate4, 2, get_block_number_hash(2));
inclusions.insert(candidate5, 3, get_block_number_hash(3));

inclusions.remove_up_to_height(&0);
assert!(inclusions.contains(&candidate1), "Expected candidate1 to remain");
assert!(inclusions.contains(&candidate2), "Expected candidate2 to remain");
assert!(inclusions.contains(&candidate3), "Expected candidate3 to remain");
assert!(inclusions.contains(&candidate4), "Expected candidate4 to remain");
assert!(inclusions.contains(&candidate5), "Expected candidate5 to remain");

inclusions.remove_up_to_height(&1);
assert!(!inclusions.contains(&candidate1), "Expected candidate1 to be removed");
assert!(inclusions.contains(&candidate2), "Expected candidate2 to remain");
assert!(inclusions.contains(&candidate3), "Expected candidate3 to remain");
assert!(inclusions.contains(&candidate4), "Expected candidate4 to remain");
assert!(inclusions.contains(&candidate5), "Expected candidate5 to remain");

inclusions.remove_up_to_height(&2);
assert!(!inclusions.contains(&candidate1), "Expected candidate1 to be removed");
assert!(!inclusions.contains(&candidate2), "Expected candidate2 to be removed");
assert!(inclusions.contains(&candidate3), "Expected candidate3 to remain");
assert!(inclusions.contains(&candidate4), "Expected candidate4 to remain");
assert!(inclusions.contains(&candidate5), "Expected candidate5 to remain");

inclusions.remove_up_to_height(&10);
assert!(!inclusions.contains(&candidate1), "Expected candidate1 to be removed");
assert!(!inclusions.contains(&candidate2), "Expected candidate2 to be removed");
assert!(!inclusions.contains(&candidate3), "Expected candidate3 to be removed");
assert!(!inclusions.contains(&candidate4), "Expected candidate4 to be removed");
assert!(!inclusions.contains(&candidate5), "Expected candidate5 to be removed");
}
Overkillus marked this conversation as resolved.
Show resolved Hide resolved

#[test]
fn inclusions_duplicate_insertion_same_height_and_block() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be more interested if pruning works as expected for same height/same block/different block. As this was precisely what went wrong in the Kusama incident.

Ideally we would also test whether pruning still works as desired if the same candidate was included at different heights.

let mut inclusions = Inclusions::new();

// Insert a candidate
let candidate1 = make_candidate_receipt(get_magic_candidate_hash()).hash();
let block_number = 0;
let block_hash = get_block_number_hash(block_number);

// Insert the candidate once
inclusions.insert(candidate1, block_number, block_hash);

// Insert the same candidate again at the same height and block
inclusions.insert(candidate1, block_number, block_hash);

// Check inclusions_inner
assert!(
inclusions.inclusions_inner.contains_key(&candidate1),
"Expected candidate1 to be present in inclusions_inner"
);
let inner_map = inclusions.inclusions_inner.get(&candidate1).unwrap();
assert!(
inner_map.contains_key(&block_number),
"Expected block_number to be present for the candidate1 in inclusions_inner"
);
let hash_set = inner_map.get(&block_number).unwrap();
assert_eq!(
hash_set.len(),
1,
"Expected only one block_hash for the block_number in inclusions_inner"
);
assert!(
hash_set.contains(&block_hash),
"Expected block_hash to be present for the block_number in inclusions_inner"
);

// Check candidates_by_block_number
assert!(
inclusions.candidates_by_block_number.contains_key(&block_number),
"Expected block_number to be present in candidates_by_block_number"
);
let candidate_set = inclusions.candidates_by_block_number.get(&block_number).unwrap();
assert_eq!(
candidate_set.len(),
1,
"Expected only one candidate for the block_number in candidates_by_block_number"
);
assert!(
candidate_set.contains(&candidate1),
"Expected candidate1 to be present for the block_number in candidates_by_block_number"
);
}

#[test]
fn test_duplicate_insertion_same_height_different_blocks() {
let mut inclusions = Inclusions::new();

// Insert a candidate
let candidate1 = make_candidate_receipt(get_magic_candidate_hash()).hash();
let block_number = 0;
let block_hash1 = BlakeTwo256::hash(&"b1".encode());
let block_hash2 = BlakeTwo256::hash(&"b2".encode()); // Different block hash for the same height
inclusions.insert(candidate1, block_number, block_hash1);
inclusions.insert(candidate1, block_number, block_hash2);

// Check inclusions_inner
assert!(
inclusions.inclusions_inner.contains_key(&candidate1),
"Expected candidate1 to be present in inclusions_inner"
);
let inner_map = inclusions.inclusions_inner.get(&candidate1).unwrap();
assert!(
inner_map.contains_key(&block_number),
"Expected block_number to be present for the candidate1 in inclusions_inner"
);
let hash_set = inner_map.get(&block_number).unwrap();
assert_eq!(
hash_set.len(),
2,
"Expected two block_hashes for the block_number in inclusions_inner"
);
assert!(
hash_set.contains(&block_hash1),
"Expected block_hash1 to be present for the block_number in inclusions_inner"
);
assert!(
hash_set.contains(&block_hash2),
"Expected block_hash2 to be present for the block_number in inclusions_inner"
);

// Check candidates_by_block_number
assert!(
inclusions.candidates_by_block_number.contains_key(&block_number),
"Expected block_number to be present in candidates_by_block_number"
);
let candidate_set = inclusions.candidates_by_block_number.get(&block_number).unwrap();
assert_eq!(
candidate_set.len(),
1,
"Expected only one candidate for the block_number in candidates_by_block_number"
);
assert!(
candidate_set.contains(&candidate1),
"Expected candidate1 to be present for the block_number in candidates_by_block_number"
);
}

#[test]
fn inclusions_remove_with_empty_maps() {
let mut inclusions = Inclusions::new();
let height = 5;

// Ensure both maps are empty before the operation
assert!(inclusions.candidates_by_block_number.is_empty());
assert!(inclusions.inclusions_inner.is_empty());

inclusions.remove_up_to_height(&height);

// Ensure both maps remain empty after the operation
assert!(inclusions.candidates_by_block_number.is_empty());
assert!(inclusions.inclusions_inner.is_empty());
}