# BLS-002: None Cache Works Correctly Without Errors

> **Authoritative requirement:** [BLS-002](../NORMATIVE.md#BLS-002)
> **Verification:** [VERIFICATION.md](../VERIFICATION.md)
> **Tracking:** [TRACKING.yaml](../TRACKING.yaml)
> **Spec reference:** [SPEC.md](../../../../resources/SPEC.md) — Section 5.1

---

## Summary

When the `bls_cache` parameter is `None`, both `validate_spend_bundle()` and `validate_block()` must perform full BLS12-381 signature verification from scratch with no errors, no performance degradation beyond the expected cost of uncached verification, and no behavioral change other than the absence of caching. The `None` path is the baseline correctness path: every valid bundle must pass, every invalid bundle must fail, and the results must be identical to what the `Some` path produces (as formalized in [BLS-005](BLS-005.md)).

This requirement ensures that callers who do not need caching -- such as one-off validation tools, test harnesses, or stateless validators -- can use the API without constructing a `BlsCache` instance. On Chia L1, the equivalent uncached path occurs when `validate_clvm_and_signature()` at [`mempool_manager.py:445`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/mempool_manager.py#L445) is called without a pre-existing cache. The underlying `chia-bls::aggregate_verify()` function operates identically regardless of whether a cache is present.

The `None` path must not introduce any code branches that could cause panics, unwrap failures, or null pointer dereferences. The implementation must cleanly handle the `Option::None` case by simply skipping cache lookups and insertions while executing the same verification logic. This is a critical safety property for a consensus-grade library where unexpected panics could halt a validator node.

---

## Specification

### Behavioral Requirements

When `bls_cache` is `None`:

```rust
// This call must succeed for any valid SpendBundle:
let result = validate_spend_bundle(
    &bundle,
    &context,
    &config,
    None,  // no cache
)?;

// This call must succeed for any valid block generator:
let result = validate_block(
    &generator,
    &generator_refs,
    &context,
    &config,
    None,  // no cache
)?;
```

1. **No errors from cache absence**: The `None` value must not cause `unwrap()` failures, panics, or `NullPointerException`-equivalent errors. The code must use safe pattern matching (`if let Some(cache) = bls_cache` or `match`).

2. **Full verification**: Every BLS pairing `(pk, msg, sig)` must be verified via `chia-bls::aggregate_verify()` with no shortcuts.

3. **Identical results**: For any given input, the `SpendResult` (additions, removals, fee) or `ValidationError` must be bit-for-bit identical whether `bls_cache` is `Some` or `None`.

4. **No state leakage**: No thread-local, global, or static cache state may be created or consulted when `None` is passed. Verification is purely functional.

### Implementation Pattern

```rust
fn validate_spend_bundle(
    bundle: &SpendBundle,
    context: &ValidationContext,
    config: &ValidationConfig,
    bls_cache: Option<&mut BlsCache>,
) -> Result<SpendResult, ValidationError> {
    // ... CLVM execution ...

    // BLS verification -- cache is optional
    match bls_cache {
        Some(cache) => {
            // Cached verification path
            verify_signatures_cached(bundle, &conditions, cache)?;
        }
        None => {
            // Uncached verification path -- same logic, no cache ops
            verify_signatures_uncached(bundle, &conditions)?;
        }
    }

    // ... result construction (identical for both paths) ...
}
```

### Error Handling

- Valid bundle + `None` cache -> `Ok(SpendResult { .. })`
- Invalid signature + `None` cache -> `Err(ValidationError::InvalidSignature(..))`
- Overspent cost + `None` cache -> `Err(ValidationError::CostExceeded(..))`
- All error variants must match those produced by the `Some` path for the same input.

---

## Acceptance Criteria

- [ ] `validate_spend_bundle(&bundle, &ctx, &cfg, None)` returns `Ok` for a valid signed spend bundle
- [ ] `validate_block(&gen, &refs, &ctx, &cfg, None)` returns `Ok` for a valid block generator
- [ ] An invalid signature produces the same `ValidationError` variant whether cache is `Some` or `None`
- [ ] No panics or unwrap failures occur when `None` is passed under any input condition
- [ ] The `SpendResult` (additions, removals, fee) is identical for `Some` vs `None` on the same input

---

## Implementation Notes

- Use `if let Some(cache) = bls_cache` or `match` rather than `.unwrap()` or `.expect()` to handle the `None` case.
- The `None` path should ideally share the same verification function as the `Some` path, with the cache parameter threaded through. This minimizes code duplication and divergence risk.
- If `chia-consensus` APIs accept `Option<&mut BlsCache>` natively, delegate directly. If they require a concrete cache, create a temporary `BlsCache::default()` and discard it after the call (acceptable since no reuse is expected).
- Performance: the `None` path is expected to be slower than a warm `Some` cache path. This is acceptable and not a bug.

---

## Verification

### Test Plan

1. **Valid bundle, None cache**:
   - Use `Simulator` to create a funded coin and build a valid `SpendBundle`.
   - Call `validate_spend_bundle(&bundle, &ctx, &cfg, None)`.
   - Assert `Ok(result)` with correct additions and removals.

2. **Valid block generator, None cache**:
   - Build a block generator from the validated bundle.
   - Call `validate_block(&gen, &refs, &ctx, &cfg, None)`.
   - Assert `Ok(result)`.

3. **Invalid signature, None cache**:
   - Construct a `SpendBundle` with a tampered aggregated signature.
   - Call `validate_spend_bundle(&bundle, &ctx, &cfg, None)`.
   - Assert `Err(ValidationError::InvalidSignature(..))` or equivalent.

4. **Result parity (None vs Some)**:
   - Validate the same bundle with `None` and with `Some(&mut BlsCache::default())`.
   - Assert that `result_none.additions == result_some.additions`.
   - Assert that `result_none.removals == result_some.removals`.
   - Assert that `result_none.fee == result_some.fee`.

5. **Stress test -- many bundles, None cache**:
   - Validate 100 distinct spend bundles with `None` cache.
   - Assert all valid bundles pass, all invalid bundles fail.
   - Assert no panics or unexpected errors.

6. **Error variant parity**:
   - For each `ValidationError` variant (cost exceeded, invalid puzzle, double spend, etc.), verify the same variant is returned regardless of cache presence.

### Expected Test Files

- `tests/cache_tests.rs` -- BLS-002 None-path validation tests
- `tests/validation_tests.rs` -- cross-reference with general validation tests using `None`

---

## Source Citations

- Chia L1 uncached verification path: [`mempool_manager.py:445`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/mempool_manager.py#L445)
- `chia-bls::aggregate_verify()`: [`chia-bls` crate](https://crates.io/crates/chia-bls)
- dig-clvm public API: [SPEC.md Section 5.1](../../../../resources/SPEC.md)

---

## References

- [BLS-001](BLS-001.md) -- Optional BlsCache parameter (defines the `Option` interface)
- [BLS-005](BLS-005.md) -- Cache does not affect correctness (formalizes result equivalence)
- [PAR-005](../../parity/specs/PAR-005.md) -- `chia-bls::aggregate_verify()` parity (underlying verification)
