# SER-002: Header Serialization

## Summary

Block headers are serialized with bincode only (no compression) and stored in `CF_HEADERS`. The write path is `L2BlockHeader` -> `bincode::serialize()` -> `CF_HEADERS`. The read path is `CF_HEADERS` -> `bincode::deserialize()` -> `L2BlockHeader`. Headers are small (~200 bytes) and frequently read for chain traversal, making compression overhead not worthwhile.

## Specification

```rust
impl BlockStore {
    /// Serialize a block header for storage in CF_HEADERS.
    /// Uses bincode only -- no compression is applied.
    pub fn serialize_header(header: &L2BlockHeader) -> Result<Vec<u8>> {
        bincode::serialize(header)
            .map_err(|e| BlockStoreError::Serialization(e.to_string()))
    }

    /// Deserialize a block header from CF_HEADERS bytes.
    /// Expects raw bincode -- no decompression is performed.
    pub fn deserialize_header(bytes: &[u8]) -> Result<L2BlockHeader> {
        bincode::deserialize(bytes)
            .map_err(|e| BlockStoreError::Serialization(e.to_string()))
    }
}
```

## Acceptance Criteria

1. `serialize_header` produces valid bincode bytes with no zstd framing.
2. `deserialize_header(serialize_header(header))` returns an `L2BlockHeader` equal to the original.
3. Raw bytes stored in `CF_HEADERS` MUST be directly deserializable by `bincode::deserialize` without any decompression step.
4. No zstd magic bytes (`0x28 0xB5 0x2F 0xFD`) appear at the start of serialized header bytes.
5. Serialized header size is proportional to the header struct size (no compression overhead or expansion).

## Implementation Notes

- Headers are read far more frequently than full blocks (chain traversal, record derivation, validation). Avoiding compression eliminates decode latency on the hot path.
- The bincode format is deterministic for the same struct layout, ensuring consistent key derivation if headers are ever hashed from their serialized form.
- Header size is typically ~200 bytes; zstd compression would add framing overhead that nearly offsets any savings.

## Test Plan

- **Unit: round-trip** -- Serialize a header, deserialize, verify all fields match.
- **Unit: no zstd framing** -- Serialize a header, verify first 4 bytes do not match zstd magic number `0xFD2FB528`.
- **Unit: raw bincode** -- Serialize with `BlockStore::serialize_header`, deserialize directly with `bincode::deserialize` (no BlockStore method), verify equality.
- **Unit: size proportionality** -- Serialize headers of varying content, verify output size is within expected range (~150-300 bytes) with no anomalous expansion.
- **Unit: corrupted input** -- Pass truncated bytes to `deserialize_header`, verify `BlockStoreError::Serialization` returned.

## Expected Test Files

- `tests/ser_002_tests.rs`
