# ROR-006: Blocks to Revert

| Link | Path |
|------|------|
| **Normative** | [NORMATIVE.md](../NORMATIVE.md) |
| **Verification** | [VERIFICATION.md](../VERIFICATION.md) |
| **Tracking** | [TRACKING.yaml](../TRACKING.yaml) |
| **Spec** | [SPEC.md](../../../../resources/SPEC.md) Section 8.2, 15.4 |

---

## Summary

`blocks_to_revert(&self, target_height: u64) -> Result<Vec<Bytes32>>` is a read-only query that returns the canonical block hashes that would be reverted if the chain rolled back to the given height. Hashes are returned in descending height order (tip first). No state mutations occur.

---

## Specification

### Function Signature

```rust
pub fn blocks_to_revert(
    &self,
    target_height: u64,
) -> Result<Vec<Bytes32>>
```

### Behavior

1. If no chain tip is set, return an empty `Vec`.
2. If `target_height >= current_tip.height`, return an empty `Vec`.
3. Iterate canonical heights from `current_tip.height` down to `target_height + 1`.
4. For each height, look up the canonical hash via `get_hash_by_height()`.
5. Collect all hashes into the result vector (descending height order).

```rust
pub fn blocks_to_revert(&self, target_height: u64) -> Result<Vec<Bytes32>> {
    let current_tip = match self.tip() {
        Some(t) => t,
        None => return Ok(Vec::new()),
    };
    if target_height >= current_tip.height {
        return Ok(Vec::new());
    }

    let mut reverted = Vec::new();
    for h in (target_height + 1..=current_tip.height).rev() {
        if let Some(hash) = self.get_hash_by_height(h)? {
            reverted.push(hash);
        }
    }
    Ok(reverted)
}
```

### Read-Only Guarantee

This method MUST NOT modify any state: no CF_CANONICAL deletions, no mmap truncation, no tip updates, no record cache modifications. It is a pure query for the consensus layer to evaluate potential rollbacks.

---

## Acceptance Criteria

- [ ] Returns canonical hashes for heights `(target_height, tip.height]`
- [ ] Hashes are in descending height order (tip first)
- [ ] Returns empty vec when no tip is set
- [ ] Returns empty vec when `target_height >= tip.height`
- [ ] No state mutations occur (CF_CANONICAL, mmap, tip, caches unchanged)
- [ ] Handles gaps in canonical index gracefully (skips missing heights)

---

## Implementation Notes

- This is essentially the "read" portion of `rollback_to_height` (ROR-001) without the write side.
- Used by the consensus layer to preview the cost of a rollback before deciding to execute it.
- Can also be used for diagnostic/monitoring purposes.

---

## Test Plan

| Test Name | Type | Description |
|-----------|------|-------------|
| `test_blocks_to_revert_basic` | unit | Build chain of 10 blocks, call blocks_to_revert(5), verify 5 hashes returned in descending order |
| `test_blocks_to_revert_no_tip` | unit | Empty store, call blocks_to_revert(0), verify empty vec |
| `test_blocks_to_revert_at_tip` | unit | Build chain to height 10, call blocks_to_revert(10), verify empty vec |
| `test_blocks_to_revert_above_tip` | unit | Build chain to height 10, call blocks_to_revert(15), verify empty vec |
| `test_blocks_to_revert_no_mutation` | unit | Call blocks_to_revert, verify CF_CANONICAL, mmap, and tip unchanged |
| `test_blocks_to_revert_to_zero` | unit | Build chain of 5 blocks, call blocks_to_revert(0), verify all 5 hashes returned |

---

## Expected Test Files

- `tests/ror_006_tests.rs`
