# PAR-010: Block Generator Parity

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

---

## Summary

`dig-clvm` must achieve full parity with Chia L1's block generator system by using the same underlying functions for both construction and validation of block generators. On the construction side, `solution_generator_backrefs()` serializes a set of spend bundles into a compressed CLVM generator program using back-references to deduplicate shared puzzle reveals. On the validation side, `run_block_generator2()` executes a generator program and extracts the coin spends it produces. These two functions form the round-trip pair that connects mempool transactions to block bodies.

On Chia L1, `solution_generator_backrefs()` is called from [`bundle_tools.py:15`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/bundle_tools.py#L15) during block construction by the farmer/timelord. The function takes a list of serialized coin spend solutions and produces a single generator program that, when executed with `run_block_generator2()`, reproduces the original coin spends. The `run_block_generator2()` function is called during block validation in [`multiprocess_validation.py:69`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/multiprocess_validation.py#L69) and in the mempool at [`mempool.py:529`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/mempool.py#L529).

`dig-clvm` wraps these functions with ergonomic Rust APIs (`build_block_generator` and `validate_block`) while ensuring that the underlying serialization format, back-reference encoding, and execution semantics are identical to L1. This parity is critical because blocks produced by DIG L2 nodes must be validatable by any implementation using the same consensus rules.

---

## Specification

### Construction Path: solution_generator_backrefs()

```rust
use chia_consensus::gen::solution_generator::solution_generator_backrefs;

/// Build a block generator from a set of spend bundles.
/// Uses solution_generator_backrefs() from chia-consensus for
/// byte-compatible output with Chia L1.
pub fn build_block_generator(
    spend_bundles: &[SpendBundle],
) -> Result<Vec<u8>, BlockGeneratorError> {
    // Extract serialized coin spends from each bundle
    let serialized_spends: Vec<Vec<u8>> = spend_bundles
        .iter()
        .flat_map(|bundle| &bundle.coin_spends)
        .map(|cs| serialize_coin_spend(cs))
        .collect::<Result<Vec<_>, _>>()?;

    // Delegate to chia-consensus for generator construction
    // with back-reference compression
    let generator = solution_generator_backrefs(&serialized_spends)?;

    Ok(generator)
}
```

### Validation Path: run_block_generator2()

```rust
use chia_consensus::gen::run_block_generator::run_block_generator2;

/// Execute a block generator program and validate the resulting spends.
/// Uses run_block_generator2() from chia-consensus for L1-compatible
/// generator execution.
pub fn validate_block(
    generator: &[u8],
    context: &ValidationContext,
    config: &ValidationConfig,
    cache: Option<&mut BlsCache>,
) -> Result<BlockResult, ValidationError> {
    // Execute the generator program using chia-consensus
    let block_runner_result = run_block_generator2(
        &mut allocator,
        generator,
        &[],                              // block_refs (empty for DIG L2)
        context.constants.consensus(),
        config.flags,
        cache,
    )?;

    // The result contains all coin spends produced by the generator.
    // Apply the same validation checks as validate_spend_bundle (BLK-008).
    // ...
}
```

### Back-Reference Compression

`solution_generator_backrefs()` uses CLVM back-references to compress repeated sub-trees in the generator program:

```
// Without back-references:
// Bundle 1: (puzzle_reveal_A, solution_1)
// Bundle 2: (puzzle_reveal_A, solution_2)  <- puzzle_A repeated
// Bundle 3: (puzzle_reveal_A, solution_3)  <- puzzle_A repeated again

// With back-references:
// Bundle 1: (puzzle_reveal_A, solution_1)
// Bundle 2: (backref_to_puzzle_A, solution_2)  <- compressed
// Bundle 3: (backref_to_puzzle_A, solution_3)  <- compressed
```

This compression is transparent to the validation side — `run_block_generator2()` resolves back-references automatically during execution.

### L1 Function Mapping

| dig-clvm API | chia-consensus Function | L1 Python Equivalent |
|-------------|------------------------|---------------------|
| `build_block_generator()` | `solution_generator_backrefs()` | `bundle_tools.py:15` |
| `validate_block()` | `run_block_generator2()` | `multiprocess_validation.py:69` |

### Generator Format

The generator program is a valid CLVM program that, when run:
1. Takes no arguments (or the block height as an argument)
2. Returns a list of `(parent_coin_info, puzzle_reveal, amount, solution)` tuples
3. Each tuple corresponds to one coin spend in the block

```clvm
; Conceptual generator structure (simplified):
(mod ()
  (list
    (0x<parent_id_1> 0x<puzzle_1> <amount_1> 0x<solution_1>)
    (0x<parent_id_2> (backref 0x<puzzle_1>) <amount_2> 0x<solution_2>)
    ; ... more spends
  )
)
```

### block_refs Parameter

On Chia L1, `run_block_generator2()` accepts a `block_refs` parameter — a list of previous block generators that can be referenced for additional compression. On DIG L2, this parameter is empty (`&[]`) because DIG blocks do not reference previous block generators. This may change in future versions.

---

## Acceptance Criteria

- [ ] `build_block_generator` uses `solution_generator_backrefs()` from `chia-consensus` for generator construction.
- [ ] `validate_block` uses `run_block_generator2()` from `chia-consensus` for generator execution.
- [ ] Back-reference compression is applied during construction and correctly resolved during validation.
- [ ] The generator output from `build_block_generator` is byte-compatible with the format expected by `run_block_generator2`.
- [ ] The round-trip invariant (BLK-009) holds for the construction/validation pair.
- [ ] `block_refs` is empty for DIG L2 block validation.

---

## Implementation Notes

- **Primary files:** `src/block/generator.rs` (build_block_generator), `src/consensus/validate_block.rs` (validate_block using run_block_generator2).
- **Allocator management:** `run_block_generator2()` requires a CLVM `Allocator`. The `validate_block` function should create a fresh allocator for each call to avoid memory accumulation.
- **Cost budget:** The generator execution itself consumes CLVM cost. This cost is included in the block's total cost and must not exceed `max_block_cost_clvm`. The cost of running the generator is additional to the cost of running individual puzzles.
- **Error mapping:** Errors from `solution_generator_backrefs()` and `run_block_generator2()` should be wrapped in `dig-clvm` error types (e.g., `BlockGeneratorError`, `ValidationError::GeneratorError`).
- **Serialization format:** `serialize_coin_spend()` must produce the exact byte format expected by `solution_generator_backrefs()`. This is the Chia Streamable serialization of `(parent_coin_info, puzzle_reveal, amount, solution)`.
- **No custom generator logic:** `dig-clvm` does NOT implement its own generator construction or execution. It delegates entirely to `chia-consensus` to ensure parity.
- **Future: block_refs:** If DIG L2 later supports cross-block generator references, the `block_refs` parameter would be populated with previous generators. The API should be designed to accommodate this (e.g., accepting an optional `&[Vec<u8>]`).

---

## Verification

### Test Plan

| Test | Type | Description | Expected Result |
|------|------|-------------|-----------------|
| Build and validate round-trip | Integration test | build_block_generator then validate_block | Spends recovered correctly |
| Back-reference compression | Integration test | Multiple bundles with shared puzzles | Generator smaller than uncompressed |
| Back-reference transparency | Integration test | Validate compressed generator | Same spends as uncompressed |
| Empty block | Unit test | build_block_generator with no bundles | Valid empty generator |
| Single bundle | Integration test | One bundle through build/validate | Matches validate_spend_bundle output |
| block_refs empty | Unit test | validate_block passes &[] for block_refs | No errors from empty refs |
| Cost includes generator | Integration test | Check that generator execution cost is counted | Total cost includes generator overhead |

---

## Source Citations

- **SPEC.md Section 7**: "solution_generator_backrefs() for construction, run_block_generator2() for validation."
- **L1 reference — bundle_tools.py:15:** [`bundle_tools.py`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/bundle_tools.py#L15) — `solution_generator_backrefs()` usage.
- **L1 reference — multiprocess_validation.py:69:** [`multiprocess_validation.py`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/multiprocess_validation.py#L69) — `run_block_generator2()` in block validation.
- **L1 reference — mempool.py:529:** [`mempool.py`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/mempool.py#L529) — generator execution in mempool.

---

## References

- [BLK-008](../../block_generator/specs/BLK-008.md) — Same checks as validate_spend_bundle (validation parity)
- [BLK-009](../../block_generator/specs/BLK-009.md) — Round-trip consistency (invariant for this pair)
- [VAL-007](../../validation/specs/VAL-007.md) — Cost enforcement (generator execution cost)
- [PAR-011](PAR-011.md) — Mempool flag parity (flags passed to run_block_generator2)
