Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions KIPs/kip-286.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ The `User tx` is a transaction initiated by the user, while `System tx` is a sys

| From | To | Timing | Condition | Trigger |
| ----------- | ----------- | ------- | --------------------- | --------- |
| ValActive | ValInactive | Epoch | Below `MinStake` | System tx |
| ValActive | ValInactive | Epoch | Below Top 50 by stake | System tx |
| ValActive | ValInactive | Epoch | Below `MinStake` AND at least one `ValActive` remains after the epoch transition | System tx |
| ValActive | ValInactive | Epoch | Below Top 50 by stake AND at least one `ValActive` remains after the epoch transition | System tx |
| ValPaused | ValInactive | Epoch | Below `MinStake` | System tx |
| ValInactive | ValReady | Anytime | Over `MinStake` | User tx |
| ValReady | ValInactive | Anytime | - | User tx |
Expand Down Expand Up @@ -215,6 +215,8 @@ def process_epoch_transition():
# over all entities. T1 and T4 do not contribute competitors.
# T3b (MinStake) and pool building are handled inline via compete_or_demote.
competitors = []
# Captured before the pass mutates states, for the liveness floor below.
previously_active = [e for e in get_all_entities() if e.state == ValActive]

def compete_or_demote(entity):
# T3b (below MinStake): demote directly to ValInactive, no slot check.
Expand Down Expand Up @@ -261,6 +263,13 @@ def process_epoch_transition():
else:
# T3b: Demote to ValInactive (entities below top 50 by slot competition)
transition(entity, ValInactive)

# Liveness floor: retain the entities that entered as ValActive when the
# demotions above leave none. A ValPaused entity in the top 50 stays paused,
# so it does not satisfy this floor.
if count_by_state(ValActive) == 0:
for entity in previously_active:
transition(entity, ValActive)
```

**Ordering Rationale:**
Expand All @@ -279,6 +288,8 @@ T1, T4, T2, and the eligible-pool collection (T3b MinStake check) are processed

6. **T3b (competition demotion)**: Entities in the eligible pool but outside top 50 are demoted to `ValInactive`.

7. **Liveness floor**: an empty `ValActive` set has no in-band recovery, since no block can be produced to restore stake or ready a replacement. The entities that entered the epoch as `ValActive` are therefore retained when the demotions above would leave none. Only that set is retained, and it competes normally at the next boundary.

### Violation Transition

Violation transitions run at every block (after epoch transition if on an epoch block). They handle MinStake and PFS violations with slot-limited demotions. Validators are processed in deterministic address order to ensure consistent results across nodes.
Expand Down
Loading