diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs index ae722fcb1b47..0a971ad5d1df 100644 --- a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs @@ -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; @@ -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" + ); +} +#[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), + "Expected block_number to be present for the candidate_hash in inclusions_inner" + ); + let hash_set = inner_map.get(&block_number).unwrap(); + 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(); + 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"); +} + +#[test] +fn inclusions_duplicate_insertion_same_height_and_block() { + 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()); +}