def test_gap_continuity(changes): MAX_BLOCK_NUM = 21 # method to get all the block numbers that are in a gap right now _all_missing = compose( set, partial(_all_gap_numbers, highest_block_number=MAX_BLOCK_NUM)) @to_set def _all_inserted(chain_gaps): """List all the inserted headers, the block numbers not in gaps""" missing = _all_missing(chain_gaps) for block_num in range(MAX_BLOCK_NUM + 1): if block_num not in missing: yield block_num chain_gaps = GENESIS_CHAIN_GAPS for do_insert, block_num in changes: starts_inserted = _all_inserted(chain_gaps) starts_missing = _all_missing(chain_gaps) if do_insert: to_insert = block_num _, chain_gaps = fill_gap(to_insert, chain_gaps) assert not is_block_number_in_gap(to_insert, chain_gaps) # Make sure that at most this one block number was filled finished_inserted = _all_inserted(chain_gaps) assert to_insert in finished_inserted new_inserts = finished_inserted - starts_inserted if block_num in starts_inserted: assert new_inserts == set() else: assert new_inserts == {block_num} # Make sure that no new gaps were created finished_missing = _all_missing(chain_gaps) assert to_insert not in finished_missing new_missing = finished_missing - starts_missing assert new_missing == set() else: to_remove = block_num # Note that removing a header is inserting a gap chain_gaps = reopen_gap(to_remove, chain_gaps) assert is_block_number_in_gap(to_remove, chain_gaps) # Make sure that no gaps were filled finished_inserted = _all_inserted(chain_gaps) new_inserts = finished_inserted - starts_inserted assert new_inserts == set() # Make sure that at most this one block number gap was reopened finished_missing = _all_missing(chain_gaps) new_missing = finished_missing - starts_missing if block_num in starts_missing: assert new_missing == set() else: assert new_missing == {block_num} _validate_gap_invariants(chain_gaps)
def _update_chain_gaps(cls, db: DatabaseAPI, persisted_block: BlockAPI, base_gaps: ChainGaps = None) -> GapInfo: # If we make many updates in a row, we can avoid reloading the integrity info by # continuously caching it and providing it as a parameter to this API if base_gaps is None: base_gaps = cls._get_chain_gaps(db) gap_change, gaps = fill_gap(persisted_block.number, base_gaps) if gap_change is not GapChange.NoChange: db.set(SchemaV1.make_chain_gaps_lookup_key(), rlp.encode(gaps, sedes=chain_gaps)) return gap_change, gaps
def test_corrupt_gaps(): with pytest.raises(GapTrackingCorrupted, match="(1, 3)"): fill_gap(2, (((1, 3), (2, 4)), 6)) with pytest.raises(GapTrackingCorrupted, match="(2, 4)"): fill_gap(2, (((1, 3), (2, 4)), 6))