# VAL-010: Ephemeral Coin Support

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

---

## Summary

An ephemeral coin is a coin that is created and spent within the same block, never appearing in the persistent UTXO set. This is a common pattern in multi-bundle block construction: an earlier bundle creates a coin (via `CREATE_COIN`), and a later bundle in the same block spends that coin. Since the coin has not yet been committed to the UTXO database when the later bundle is validated, it would fail the coin existence check (VAL-004) unless the validator is aware of its ephemeral status.

`dig-clvm` supports ephemeral coins through the `context.ephemeral_coins` field, a `HashSet<Bytes32>` containing the coin IDs of coins created by earlier bundles in the current block. When a coin being spent is not found in `context.coin_records`, the validator checks `context.ephemeral_coins` before declaring the coin missing. If found in the ephemeral set, the coin is treated as a valid spend target.

On Chia L1, ephemeral coin handling occurs in the block body validator at [`validate_block_body()`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/block_body_validation.py#L191) which tracks additions and removals within a block and allows intra-block spend chains. The `ASSERT_EPHEMERAL` condition opcode allows puzzles to explicitly require that they are being spent ephemerally. This condition validation is handled by `chia-consensus` (per VAL-009).

The caller (block builder or validator) is responsible for populating `ephemeral_coins` correctly. As each bundle in a block is validated, the caller adds the `additions` from the result to the ephemeral set before validating the next bundle. `dig-clvm` trusts this input and does not maintain its own intra-block state.

---

## Specification

### ephemeral_coins Field

```rust
pub struct ValidationContext {
    // ... other fields ...
    /// Coins created by earlier bundles in the same block (ephemeral).
    /// Allows later bundles to spend coins that don't yet exist in the
    /// persistent UTXO set, matching Chia L1's ASSERT_EPHEMERAL behavior.
    pub ephemeral_coins: HashSet<Bytes32>,
}
```

### How Ephemeral Coins Are Used

During the coin existence check (VAL-004):

```rust
// For each coin being spent:
let coin_id = coin_spend.coin.coin_id();

// Check persistent UTXO set first
if context.coin_records.contains_key(&coin_id) {
    // Found in persistent state — proceed to already-spent check (VAL-005)
    continue;
}

// Check ephemeral set
if context.ephemeral_coins.contains(&coin_id) {
    // Ephemeral coin — valid spend target, skip already-spent check
    continue;
}

// Not found anywhere
return Err(ValidationError::CoinNotFound(coin_id));
```

### Caller Workflow for Block Construction

```rust
let mut ephemeral = HashSet::new();

for bundle in block_bundles {
    let context = ValidationContext {
        height,
        timestamp,
        constants: DIG_MAINNET,
        coin_records: load_coin_records(&bundle),
        ephemeral_coins: ephemeral.clone(),
    };

    let result = validate_spend_bundle(&bundle, &context, &config, Some(&mut cache))?;

    // Add new coins to ephemeral set for subsequent bundles
    for coin in &result.additions {
        ephemeral.insert(coin.coin_id());
    }
}
```

### Ephemeral Coin Properties

- Ephemeral coins have no `CoinRecord` and therefore:
  - The already-spent check (VAL-005) does not apply to them.
  - The puzzle hash for VAL-006 is taken from the `CoinSpend.coin` field.
  - They have no `confirmed_block_index`, `timestamp`, or `spent_block_index`.
- Ephemeral coins are only valid within the block they are created in. The `ephemeral_coins` set is scoped to a single block's validation.
- An ephemeral coin can itself create more coins that are also ephemeral (chained ephemerals).

---

## Acceptance Criteria

- [ ] A coin listed in `context.ephemeral_coins` but not in `context.coin_records` can be successfully spent.
- [ ] A coin not in `context.ephemeral_coins` and not in `context.coin_records` is rejected with `CoinNotFound`.
- [ ] Ephemeral coins are not subjected to the already-spent check (VAL-005).
- [ ] The `ephemeral_coins` field is a `HashSet<Bytes32>` in `ValidationContext`.
- [ ] Multiple bundles in sequence can create and spend ephemeral coins when the caller properly populates `ephemeral_coins`.
- [ ] Chained ephemeral spending works: bundle A creates coin X, bundle B spends X and creates coin Y, bundle C spends Y.

---

## Implementation Notes

- **Primary file:** `src/consensus/validate.rs` (existence check), `src/consensus/context.rs` (field definition).
- **Caller responsibility:** `dig-clvm` does not automatically track ephemeral coins across bundles. The caller must add additions from each validated bundle to the ephemeral set before validating the next bundle.
- **No CoinRecord for ephemerals:** Since there is no `CoinRecord` for ephemeral coins, any check that reads from `CoinRecord` fields (like VAL-005's `spent` check) must be skipped or handled differently for ephemeral coins.
- **Conservation check:** Ephemeral coins participate in the conservation check (VAL-011) normally. Their amounts are included in the removal sum when spent.
- **Edge case:** An ephemeral coin that is never spent in the block simply becomes a regular addition. It will be committed to the UTXO set when the block is finalized.
- **Edge case:** If the same coin ID appears in both `coin_records` and `ephemeral_coins`, the `coin_records` entry takes precedence (the coin exists in persistent state).
- **Edge case:** The `CoinSpend.coin` field for an ephemeral spend must accurately reflect the coin created by the earlier bundle (correct `parent_coin_info`, `puzzle_hash`, and `amount`). If these are wrong, the spend will fail at the CLVM level or puzzle hash check.

---

## Verification

### Test Plan

| Test | Type | Description | Expected Result |
|------|------|-------------|-----------------|
| Ephemeral spend succeeds | Simulator test | Bundle A creates coin X; validate bundle B spending X with X in `ephemeral_coins` | Bundle B validation succeeds |
| Missing non-ephemeral rejected | Simulator test | Coin not in `coin_records` or `ephemeral_coins` | `Err(ValidationError::CoinNotFound)` |
| Chained ephemeral | Simulator test | Three bundles: A creates X, B spends X creates Y, C spends Y | All three validations succeed |
| Ephemeral skips already-spent | Simulator test | Coin in `ephemeral_coins` only, validate spend | Not rejected by already-spent check |
| Empty ephemeral set | Unit test | Default `ValidationContext` with empty `ephemeral_coins` | Non-recorded coins produce `CoinNotFound` |
| Both sets contain coin | Unit test | Coin in both `coin_records` (unspent) and `ephemeral_coins` | Passes (coin_records entry used) |

---

## Source Citations

- **SPEC.md Section 5.1** (lines 472-475): `ephemeral_coins` field definition: "Coins created by earlier bundles in the same block (ephemeral). Allows later bundles to spend coins that don't yet exist in the persistent UTXO set."
- **SPEC.md Section 6.1** (line 664): Step 4 condition validation includes "Ephemeral coin assertions."
- **SPEC.md Section 3.1** (line 221): `ValidationContext` owns "ephemeral tracking."
- **L1 reference — validate_block_body:** [`block_body_validation.py:191`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/block_body_validation.py#L191) — tracks intra-block additions and removals.
- **L1 reference — ASSERT_EPHEMERAL:** Condition opcode for ephemeral assertion, handled by `chia-consensus`.

---

## References

- [VAL-004](VAL-004.md) — Missing coin check (ephemeral coins bypass CoinNotFound)
- [VAL-005](VAL-005.md) — Already-spent check (ephemeral coins skip this)
- [VAL-009](VAL-009.md) — Condition validation (ASSERT_EPHEMERAL handled by chia-consensus)
- [VAL-011](VAL-011.md) — Conservation check (ephemeral coins participate normally)
- [VAL-015](VAL-015.md) — coin_records is minimal (ephemeral_coins supplements it)
