# Block Storage - Verification Matrix

| ID | Status | Summary | Verification Approach |
|----|--------|---------|----------------------|
| BLK-001 | done | Store block with zstd compression, header, record cache, canonical mapping; idempotent | Integration: `tests/blk_001_tests.rs` — round-trip hash, idempotent CF_BYTES stable, zstd/bincode CF layout, get_record vs from_header, canonical on/off |
| BLK-002 | done | Get block by hash with cache, zstd dictionary fallback, bincode deserialization | Integration: `tests/blk_002_tests.rs` — physical get counter, read-through, dict + plain fallback reopen, unknown hash |
| BLK-003 | done | Get header by hash with cache, no decompression | Integration: `tests/blk_003_tests.rs` — physical get counter, read-through, raw CF_HEADERS bincode (no zstd), unknown hash |
| BLK-004 | done | Get record by hash, derived from header, never persisted | Integration: `tests/blk_004_tests.rs` — cache hit skips physical CF_HEADERS; header-warm miss; full miss read-through; reopen derives record; CF layout (header bincode / zstd blocks); unknown hash |
| BLK-005 | done | Batch retrieval with multi_get for cache misses | Integration: `tests/blk_005_tests.rs` — all hits skip batch counter; all misses one batch; mixed order; ordering permutation; missing slot None; read-through warms `get_block`; empty input |
| BLK-006 | done | Prefetch sequential blocks via readahead iterator on CF_CANONICAL | Integration: `tests/blk_006_tests.rs` — height order slice 10..50; readahead_size wiring; second pass cache-only; missing body BlockNotFound; inverted range empty; optional ignored throughput smoke |
| BLK-007 | done | Async wrappers: cache hits non-blocking, misses via spawn_blocking | Integration: `tests/blk_007_tests.rs` — first-poll Ready on cache hit; Pending then await on miss; sync/async parity; height path; join prefix; `Arc` clone shares counters |
| BLK-008 | done | Write pipeline batches puts into WriteBatch via bounded mpsc channel | Integration: `tests/blk_008_tests.rs` — batched enqueue (4 jobs → one WriteBatch counter); partial flush via timer; duplicate hash idempotency; bulk 32 round-trip; read-only rejection; bounded-channel backpressure; graceful shutdown flush after drop (yield + short sleep before reopen) |
| BLK-009 | done | Attestation put/get via bincode in CF_ATTESTED | Integration: `tests/blk_009_tests.rs` — round-trip (bincode bytes equality); raw CF read + manual deserialize; missing `Ok(None)`; overwrite latest; read-only `put_attestation` rejected |
| BLK-010 | done | Update BlockRecord status in cache only, no disk write | Integration: `tests/blk_010_tests.rs` — `get_record` reflects status + `in_canonical_chain`; raw `CF_HEADERS`/`CF_BLOCKS` bytes unchanged; miss + post-invalidate errors (`ERR_UPDATE_STATUS_RECORD_NOT_CACHED_PREFIX`); sequential updates |
| BLK-011 | done | Has block existence check by hash | Integration: `tests/blk_011_tests.rs` — true after put; false unknown; cache hit keeps `cf_blocks_physical_get_count` at 0; block-only LRU evict with header warm; full evict cold path still true without get_block counter |
| BLK-012 | done | Storage statistics via stats() | Integration: `tests/blk_012_tests.rs` — empty store zeros + None heights; genesis counts + tip_height Some(0); fork vs canonical counts; attested_count; META_MIN_HEIGHT via raw RocksDB then stats; total_size_bytes > 0 after flush |
| BLK-013 | done | Flush WAL and trigger manual compaction | Integration: `tests/blk_013_tests.rs` — flush/compact Ok after writes; data survives drop+reopen after flush; get_record + CF key counts unchanged after flush+compact (size estimate excluded) |
| BLK-014 | done | Get canonical blocks in height range (inclusive) | Integration: `tests/blk_014_tests.rs` — basic 3..=7; full 0..=9; inverted empty; single height; beyond tip truncated; CF_CANONICAL gap skipped with ordered survivors |
| BLK-015 | done | Get canonical records in height range (inclusive) | Integration: `tests/blk_015_tests.rs` — basic order; empty inverted; beyond tip; CF_CANONICAL gap; fields vs chain; cold-cache counters prove no CF_BLOCKS reads for records then CF_BLOCKS for `get_blocks_in_range` |
