# CON-004: Fork Heights at Zero

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

---

## Summary

DIG L2 sets all consensus fork heights to zero (`hard_fork_height = 0`, `hard_fork2_height = 0`), meaning that all Chia consensus features are active from the very first block. Unlike Chia L1, which introduced new consensus rules at specific block heights through phased fork activations, DIG L2 has no historical blocks that predate any fork. Since DIG is a new network starting from scratch, there is no need to maintain backward compatibility with pre-fork block formats.

On Chia L1, the hard fork (enabling new CLVM opcodes, tighter cost accounting, and other changes) activated at a specific height, and the `hard_fork2` (enabling CHIP-0029 message conditions, SEND_MESSAGE/RECEIVE_MESSAGE opcodes) activated at a later height. The `chia-consensus` code checks these heights to determine which rules to apply: before the fork height, old rules are used; at or after the fork height, new rules are used. By setting both fork heights to 0, DIG L2 tells `chia-consensus` to always use the newest rules.

This is the correct approach for a new L2 chain: there is no genesis that predates the hard fork, so there is no block that should be validated under old rules. Setting fork heights to 0 is the cleanest way to achieve this within the existing `chia-consensus` framework, which is designed to support configurable fork heights.

---

## Specification

### Fork Height Values

```rust
const DIG_BASE_CONSTANTS: ConsensusConstants = ConsensusConstants {
    // All fork heights at zero: all features active from block 0.
    hard_fork_height: 0,       // Chia hard fork (new opcodes, stricter cost)
    hard_fork2_height: 0,      // CHIP-0029 (SEND_MESSAGE, RECEIVE_MESSAGE)

    // ... other fields
};
```

### What Fork Heights Control in chia-consensus

```rust
// In chia-consensus, fork heights gate feature activation:

// hard_fork_height gates:
// - New CLVM opcodes (SOFTFORK, etc.)
// - Tighter CLVM cost accounting
// - Updated condition validation rules
// - BLS signature scheme changes
if block_height >= constants.hard_fork_height {
    // Use new rules
} else {
    // Use old rules (pre-fork compatibility)
}

// hard_fork2_height gates:
// - SEND_MESSAGE (opcode 66) and RECEIVE_MESSAGE (opcode 67)
// - Cross-coin message passing within blocks
if block_height >= constants.hard_fork2_height {
    // Enable message conditions
} else {
    // Reject message conditions as unknown
}
```

### Effect on DIG L2

With both fork heights at 0:

```rust
// For any DIG L2 block at any height:
assert!(block_height >= 0);  // Always true

// Therefore:
// - All new CLVM opcodes are always available
// - Stricter cost accounting is always active
// - SEND_MESSAGE and RECEIVE_MESSAGE always work
// - No "legacy mode" code paths are ever taken
// - No conditional fork activation logic is needed in DIG code
```

### No Phased Fork Activations

```rust
// DIG L2 DOES NOT do this:
const WRONG: ConsensusConstants = ConsensusConstants {
    hard_fork_height: 100_000,   // WRONG: No reason to delay
    hard_fork2_height: 200_000,  // WRONG: No reason to delay
    // ...
};

// DIG L2 DOES this:
const CORRECT: ConsensusConstants = ConsensusConstants {
    hard_fork_height: 0,     // All features from genesis
    hard_fork2_height: 0,    // All features from genesis
    // ...
};
```

### Implications for Testing

```rust
// Tests never need to test pre-fork behavior on DIG:
#[test]
fn message_conditions_always_work() {
    let context = ValidationContext {
        height: 0,  // Even at genesis height
        constants: DIG_MAINNET,
        // ...
    };

    // SEND_MESSAGE should work at any height, including 0
    let bundle = create_bundle_with_send_message();
    let result = validate_spend_bundle(&bundle, &context, &config, None);
    assert!(result.is_ok());
}
```

### Comparison with Chia L1

| Parameter | Chia L1 | DIG L2 | Rationale |
|-----------|---------|--------|-----------|
| `hard_fork_height` | 5,496,000 | 0 | DIG has no pre-fork history |
| `hard_fork2_height` | 5,716,000 | 0 | DIG has no pre-fork2 history |
| Pre-fork block validation | Required (historical blocks) | N/A (no pre-fork blocks) | New chain |
| Legacy opcode handling | Required before fork height | Never needed | All features from genesis |

---

## Acceptance Criteria

- [ ] `hard_fork_height` is set to 0 in both `DIG_MAINNET` and `DIG_TESTNET`.
- [ ] `hard_fork2_height` is set to 0 in both `DIG_MAINNET` and `DIG_TESTNET`.
- [ ] All Chia consensus features (new opcodes, SEND_MESSAGE, RECEIVE_MESSAGE) are available at block height 0.
- [ ] No conditional fork activation logic is present in `dig-clvm` or `dig-constants`.
- [ ] Tests validate that consensus features work at height 0.

---

## Implementation Notes

- **Primary file:** `dig-constants/src/constants.rs` (fork height values in DIG_BASE_CONSTANTS).
- **chia-consensus compatibility:** Setting fork heights to 0 is a supported configuration in `chia-consensus`. The code uses `>=` comparisons, so height 0 satisfies `block_height >= 0` trivially.
- **No negative heights:** Block heights are unsigned (`u32`), so 0 is the minimum possible value. There is no way to set a fork height "before genesis."
- **Future forks:** If `chia-consensus` adds a `hard_fork3_height` in the future, DIG L2 should set it to 0 as well, for the same reasons.
- **No feature flags:** DIG L2 does not need compile-time feature flags to enable/disable fork features. Everything is always enabled.
- **Consensus divergence risk:** If `chia-consensus` introduces a fork that DIG L2 does NOT want to adopt, DIG would need to pin the `chia-consensus` version rather than setting the fork height to 0. This is a future concern, not a current one.

---

## Verification

### Test Plan

| Test | Type | Description | Expected Result |
|------|------|-------------|-----------------|
| hard_fork_height is zero | Unit test | Assert DIG_MAINNET fork height | == 0 |
| hard_fork2_height is zero | Unit test | Assert DIG_MAINNET fork2 height | == 0 |
| New opcodes at height 0 | Simulator test | Use SOFTFORK condition at height 0 | Accepted |
| SEND_MESSAGE at height 0 | Simulator test | Use opcode 66 at height 0 | Accepted |
| RECEIVE_MESSAGE at height 0 | Simulator test | Use opcode 67 at height 0 | Accepted |
| Same for testnet | Unit test | Assert DIG_TESTNET fork heights | Both == 0 |

---

## Source Citations

- **SPEC.md Section 4.3**: "hard_fork_height=0, hard_fork2_height=0. All Chia consensus features from block 0. No phased fork activations on DIG L2."
- **Chia L1 reference:** Hard fork at height 5,496,000, hard fork 2 at height 5,716,000.
- **chia-consensus source:** Fork height comparison logic using `>=` operator.

---

## References

- [CON-002](CON-002.md) — NetworkConstants type (contains fork height fields)
- [CON-003](CON-003.md) — DIG_MAINNET and DIG_TESTNET (instances where these are set)
- [PAR-009](../../parity/specs/PAR-009.md) — Condition type re-exports (SEND_MESSAGE/RECEIVE_MESSAGE enabled by fork2)
- [VAL-009](../../validation/specs/VAL-009.md) — Condition validation (uses fork heights internally)
