# PAR-002: Condition Parsing via chia-consensus SpendVisitor

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

---

## Summary

Condition parsing in `dig-clvm` must use the `chia-consensus` condition processing engine with the `SpendVisitor` trait, covering all 40+ condition opcodes defined in Chia L1's [`condition_opcodes.py:7-73`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L7). This includes the newer messaging opcodes `SEND_MESSAGE(66)` and `RECEIVE_MESSAGE(67)` added in Chia 2.3 at [`:37-38`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L37). By delegating to `chia-consensus` for condition processing, `dig-clvm` inherits identical parsing logic, argument validation, and cost accounting for every opcode.

The `SpendVisitor` trait from `chia-consensus` provides a hook into the condition processing pipeline. On L1, the mempool uses `MempoolVisitor` (an implementation of `SpendVisitor`) to apply additional mempool-specific rules during condition parsing. The `dig-clvm` crate may either use the default visitor or provide a custom `SpendVisitor` implementation for L2-specific rules, but the underlying condition parsing must always be performed by `chia-consensus`.

Custom condition parsing is explicitly prohibited. A hand-rolled parser would need to correctly handle all 40+ opcodes, each with its own argument format, cost, and validation rules. Any discrepancy -- even a single opcode handled differently -- would create a consensus divergence with L1. The `chia-consensus` crate is the single source of truth for condition semantics, updated by the Chia team whenever new opcodes are added or existing opcodes are modified.

---

## Specification

### Condition Opcodes (complete list from L1)

The following opcodes must be supported via `chia-consensus`:

| Category | Opcodes | Reference |
|----------|---------|-----------|
| Output | `CREATE_COIN(51)` | [`condition_opcodes.py:12`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L12) |
| Signatures | `AGG_SIG_UNSAFE(49)`, `AGG_SIG_ME(50)` | [`:10-11`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L10) |
| Scoped signatures | `AGG_SIG_PARENT(48)`, `AGG_SIG_PUZZLE(47)`, `AGG_SIG_AMOUNT(46)`, `AGG_SIG_PUZZLE_AMOUNT(45)`, `AGG_SIG_PARENT_AMOUNT(44)`, `AGG_SIG_PARENT_PUZZLE(43)` | [`:22-27`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L22) |
| Timelocks | `ASSERT_SECONDS_RELATIVE(80)`, `ASSERT_SECONDS_ABSOLUTE(81)`, `ASSERT_HEIGHT_RELATIVE(82)`, `ASSERT_HEIGHT_ABSOLUTE(83)` | [`:14-17`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L14) |
| Negative timelocks | `ASSERT_BEFORE_SECONDS_RELATIVE(84)`, `ASSERT_BEFORE_SECONDS_ABSOLUTE(85)`, `ASSERT_BEFORE_HEIGHT_RELATIVE(86)`, `ASSERT_BEFORE_HEIGHT_ABSOLUTE(87)` | [`:28-31`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L28) |
| Announcements | `CREATE_COIN_ANNOUNCEMENT(60)`, `ASSERT_COIN_ANNOUNCEMENT(61)`, `CREATE_PUZZLE_ANNOUNCEMENT(62)`, `ASSERT_PUZZLE_ANNOUNCEMENT(63)` | [`:18-21`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L18) |
| Identity | `ASSERT_MY_COIN_ID(70)`, `ASSERT_MY_PARENT_ID(71)`, `ASSERT_MY_PUZZLEHASH(72)`, `ASSERT_MY_AMOUNT(73)`, `ASSERT_MY_BIRTH_SECONDS(74)`, `ASSERT_MY_BIRTH_HEIGHT(75)` | [`:32-36`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L32) |
| Messaging | `SEND_MESSAGE(66)`, `RECEIVE_MESSAGE(67)` | [`:37-38`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L37) |
| Fee | `RESERVE_FEE(52)` | [`:13`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L13) |
| Softfork | `SOFTFORK(90)` | [`:39`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L39) |
| Remark | `REMARK(1)` | [`:9`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L9) |

### Required Integration

```rust
use chia_consensus::spendbundle_conditions::run_spendbundle;
use chia_consensus::conditions::{
    SpendBundleConditions, OwnedSpendBundleConditions,
    SpendConditions, OwnedSpendConditions,
};

// Production validation delegates to chia-consensus:
let conditions: OwnedSpendBundleConditions = run_spendbundle(
    &mut allocator,
    bundle,
    max_cost,
    height,
    constants,
    flags,
)?;

// Conditions are fully parsed by chia-consensus into typed structs:
// - conditions.agg_sig_unsafe: Vec<(PublicKey, Bytes)>
// - conditions.cost: u64
// - conditions.spends: Vec<OwnedSpendConditions>
//   - each spend has: create_coin, agg_sig_me, agg_sig_parent, etc.
```

### SpendVisitor Integration

```rust
use chia_consensus::conditions::SpendVisitor;

// Default visitor (block validation):
// chia-consensus provides a default implementation that collects
// conditions into SpendBundleConditions.

// Mempool visitor (stricter rules):
// chia-consensus provides MempoolVisitor which adds mempool-specific
// checks (e.g., no relative timelocks > threshold, announcement limits).
```

### Prohibited Patterns

```rust
// PROHIBITED: Custom condition parser
fn parse_conditions(output: NodePtr, allocator: &Allocator) -> Vec<Condition> {
    // hand-rolled S-expression traversal
}

// PROHIBITED: Partial opcode support
match opcode {
    51 => handle_create_coin(..),
    50 => handle_agg_sig_me(..),
    _ => { /* ignore unknown opcodes */ }  // WRONG: misses 30+ opcodes
}

// PROHIBITED: Custom opcode constants
const CREATE_COIN: u8 = 51;  // Use chia-consensus opcode constants
```

---

## Acceptance Criteria

- [ ] All condition parsing flows through `chia-consensus` functions (`run_spendbundle`, `get_conditions_from_spendbundle`, or equivalent)
- [ ] `SEND_MESSAGE(66)` and `RECEIVE_MESSAGE(67)` are supported (verified by test)
- [ ] No custom condition parsing code exists (`grep -r "parse_condition\|parse_output" src/` returns zero matches)
- [ ] `SpendBundleConditions` and `SpendConditions` types are from `chia-consensus`, not custom-defined
- [ ] All 40+ opcodes from `condition_opcodes.py` are handled (inherited from `chia-consensus`)

---

## Implementation Notes

- The `chia-consensus` crate handles all opcode parsing internally. The `dig-clvm` crate does not need to enumerate opcodes.
- The `OwnedSpendBundleConditions` struct provides owned versions of all condition data, suitable for returning from functions without lifetime constraints.
- For mempool validation, the `MempoolVisitor` from `chia-consensus` can be used to apply stricter rules (see [PAR-011](PAR-011.md)).
- Future opcodes added to `chia-consensus` will be automatically supported by updating the dependency version.

---

## Verification

### Test Plan

1. **Code review -- no custom parser**:
   - Run `grep -rn "parse_condition\|parse_output\|fn extract_conditions" src/`.
   - Assert zero matches for custom condition parsing.

2. **CREATE_COIN test**:
   - Build a spend that emits `CREATE_COIN(puzzle_hash, amount, [memos])`.
   - Validate via `dig-clvm`. Assert the `SpendResult.additions` contains the expected coin.

3. **AGG_SIG_ME test**:
   - Build a spend that emits `AGG_SIG_ME(pubkey, message)`.
   - Validate with correct signature. Assert success.
   - Validate with incorrect signature. Assert `ValidationError`.

4. **SEND_MESSAGE / RECEIVE_MESSAGE test**:
   - Build two spends: one emitting `SEND_MESSAGE(mode, message, ...)` and one emitting `RECEIVE_MESSAGE(mode, message, ...)`.
   - Validate as a bundle. Assert success when messages match.
   - Assert failure when messages do not match.

5. **Timelock test**:
   - Build a spend with `ASSERT_HEIGHT_ABSOLUTE(100)`.
   - Validate at height 50. Assert failure.
   - Validate at height 100. Assert success.

6. **Full opcode coverage (spot check)**:
   - Test at least one opcode from each category in the table above.
   - Verify conditions are correctly extracted into `SpendBundleConditions`.

### Expected Test Files

- `tests/condition_tests.rs` -- PAR-002 condition parsing tests
- `tests/parity_tests.rs` -- cross-validation with L1 expected behavior

---

## Source Citations

- Chia L1 condition opcodes: [`condition_opcodes.py:7-73`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L7)
- Chia L1 condition processing: [`multiprocess_validation.py:62`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/multiprocess_validation.py#L62)
- `SpendVisitor` trait: [`chia-consensus` crate](https://crates.io/crates/chia-consensus)
- SEND_MESSAGE / RECEIVE_MESSAGE: [`condition_opcodes.py:37-38`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/condition_opcodes.py#L37)

---

## References

- [PAR-001](PAR-001.md) -- CLVM execution via `clvmr` (conditions are output of execution)
- [PAR-006](PAR-006.md) -- Per-condition costs (each opcode has an associated cost)
- [PAR-009](PAR-009.md) -- Condition types from `chia-sdk-types` (type representation)
- [PAR-011](PAR-011.md) -- Mempool flags (affect condition validation strictness)
