# BLK-004: Get Record by Hash (`get_record`)

## Summary

Retrieve a `BlockRecord` by block hash. The record cache is checked first. On a cache miss, the header is read from `CF_HEADERS`, and a `BlockRecord` is derived via `from_header()`. `BlockRecord` values are never persisted to disk; they exist only in the in-memory cache.

## Specification

```rust
impl BlockStore {
    /// Retrieve a BlockRecord by hash. Returns `Ok(None)` if no header exists.
    /// BlockRecords are derived from headers and cached in-memory only.
    pub fn get_record(&self, hash: &Bytes32) -> Result<Option<BlockRecord>> {
        // Cache hit path
        if let Some(record) = self.record_cache.get(hash) {
            return Ok(Some(record.clone()));
        }

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

        let header: L2BlockHeader = bincode::deserialize(&raw)?;

        // Derive BlockRecord from header (never persisted)
        let record = BlockRecord::from_header(&header);

        // Populate record cache
        self.record_cache.insert(*hash, record.clone());

        Ok(Some(record))
    }
}
```

## Acceptance Criteria

1. A `BlockRecord` is returned for any block whose header exists in `CF_HEADERS`.
2. Cache hits return the record without RocksDB I/O.
3. Cache misses read the header from `CF_HEADERS`, derive the `BlockRecord` via `from_header()`, and populate the cache.
4. `BlockRecord` values are NEVER written to any RocksDB column family.
5. Returns `Ok(None)` when no header exists for the given hash.

## Implementation Notes

- `BlockRecord::from_header()` extracts lightweight metadata (hash, height, parent hash, timestamp, status) from the full header.
- The record cache is separate from the header cache; both may contain entries for the same hash.
- The `put` method also populates the record cache, so most records will already be cached after block ingestion.

## Test Plan

- **Unit: cache hit** -- Pre-populate the record cache, call `get_record`, verify no RocksDB read.
- **Unit: cache miss derivation** -- Store a block, evict the record cache, call `get_record`, verify the record matches `BlockRecord::from_header()`.
- **Unit: never persisted** -- Scan all RocksDB column families and confirm no `BlockRecord` data is stored on disk.
- **Unit: missing hash** -- Call `get_record` with an unknown hash, verify `Ok(None)`.

## Expected Test Files

- `tests/blk_004_tests.rs`
