# BLK-003: Get Header by Hash (`get_header`)

## Summary

Retrieve an L2 block header by its hash. The header cache is checked first. On a cache miss, the header is read from `CF_HEADERS` and deserialized with bincode. Headers are stored uncompressed, so no decompression step is needed.

## Specification

```rust
impl BlockStore {
    /// Retrieve a block header by hash. Returns `Ok(None)` if not found.
    pub fn get_header(&self, hash: &Bytes32) -> Result<Option<L2BlockHeader>> {
        // Cache hit path
        if let Some(header) = self.header_cache.get(hash) {
            return Ok(Some(header.clone()));
        }

        // Cache miss: read from RocksDB (no decompression)
        let raw = match self.db.get_cf(&self.cf_headers, hash.as_ref())? {
            Some(bytes) => bytes,
            None => return Ok(None),
        };

        // Deserialize directly (headers stored uncompressed)
        let header: L2BlockHeader = bincode::deserialize(&raw)?;

        // Populate cache
        self.header_cache.insert(*hash, header.clone());

        Ok(Some(header))
    }
}
```

## Acceptance Criteria

1. A header stored via `put` is returned correctly by `get_header`.
2. Cache hits return the header without RocksDB I/O.
3. Cache misses read raw bytes from `CF_HEADERS` and deserialize with bincode only (no zstd decompression).
4. The header cache is populated on a successful read-through.
5. Returns `Ok(None)` for a hash that does not exist.

## Implementation Notes

- Headers are intentionally stored uncompressed because they are small and frequently accessed; the overhead of decompression would outweigh space savings.
- The header cache is separate from the block cache and uses its own sharded LRU.

## Test Plan

- **Unit: cache hit** -- Pre-populate the header cache, call `get_header`, verify no RocksDB read.
- **Unit: cache miss read-through** -- Store a block (which writes the header), evict the header cache, call `get_header`, verify correct return and cache population.
- **Unit: no decompression** -- Read raw bytes from `CF_HEADERS`, confirm they deserialize directly with bincode without any decompression.
- **Unit: missing hash** -- Call `get_header` with an unknown hash, verify `Ok(None)`.

## Expected Test Files

- `tests/blk_003_tests.rs`
