Problem
Yano receives rollback targets as a ChainSync Point containing both slot and hash, but parts of the rollback path currently reduce that point to a slot:
DirectRocksDBChainState.rollbackTo(Long slot) resolves the canonical entry using slot-based mappings.
DefaultUtxoStore.rollbackTo(RollbackEvent) retains the first delta satisfying delta.slot() <= targetSlot and does not compare the target hash.
This is ambiguous at a Byron epoch boundary:
preceding main: block N, slot < S
EBB: block N, slot S
successor main: block N+1, slot S
The EBB shares its block number with its predecessor and its slot with its successor. After the successor main block has been applied, rollback to the EBB at slot S must remove successor state at S. Slot-only rollback can instead retain the successor or resolve its hash.
This can also affect post-store compensation: if application of the successor main block fails, tipBeforeStore can be the same-slot EBB and compensation must restore that exact point.
Ouroboros Consensus models non-genesis points as (slot, headerHash) and matches the complete point during header and ledger rewind. Yano should preserve the same identity through its rollback pipeline.
Proposed scope
-
Add a point-aware ChainState rollback API, for example rollbackTo(Point), and propagate the ChainSync target hash instead of discarding it.
-
Distinguish an EBB from the regular block at the same slot using the existing EBB and main-block indexes.
-
Update DefaultUtxoStore.rollbackTo so a delta is retained only when:
delta.slot < target.slot
or
delta.slot == target.slot && delta.blockHash == target.hash
-
Reverse a same-slot delta whose hash differs from the target hash.
-
Require a hash for non-origin canonical rollback points. Fail closed when an equal-slot delta lacks sufficient hash identity.
-
Audit other rollback-capable stores and compensation/reconciliation callers to ensure none silently reduce an exact point to slot-only identity.
-
Preserve or deliberately deprecate the slot-only ChainState API after callers migrate.
No RocksDB schema or index migration is expected: UTXO deltas already store the block hash, and ChainState already stores EBB hashes separately from main-block slot mappings.
Acceptance criteria
- Rollback to
main N retains its matching delta and point.
- Given
main N -> EBB N -> main N+1, rollback to the EBB removes state from main N+1 and restores the exact EBB ChainState tip.
- Rollback to
main N+1 at the same slot retains its matching-hash delta.
- A same-slot hash mismatch is never accepted as the target point.
- Post-store compensation from the successor main block to the preceding EBB completes at the exact expected hash.
- Origin rollback remains correct.
- Existing current-era rollback behavior remains unchanged for ordinary distinct-slot points.
- Direct RocksDB and in-memory ChainState implementations have equivalent point-aware behavior.
- Tests cover missing target hashes and legacy/malformed deltas without hashes and verify fail-closed behavior.
Context
Proposed ADR-041 now includes exact point-aware rollback as part of the Byron main-block UTXO application decision. This issue tracks the ChainState, UTXO, compensation, and rollback-audit portion of that ADR so the same-slot EBB recovery behavior is implemented and verified with the feature.
Problem
Yano receives rollback targets as a ChainSync
Pointcontaining both slot and hash, but parts of the rollback path currently reduce that point to a slot:DirectRocksDBChainState.rollbackTo(Long slot)resolves the canonical entry using slot-based mappings.DefaultUtxoStore.rollbackTo(RollbackEvent)retains the first delta satisfyingdelta.slot() <= targetSlotand does not compare the target hash.This is ambiguous at a Byron epoch boundary:
The EBB shares its block number with its predecessor and its slot with its successor. After the successor main block has been applied, rollback to the EBB at slot
Smust remove successor state atS. Slot-only rollback can instead retain the successor or resolve its hash.This can also affect post-store compensation: if application of the successor main block fails,
tipBeforeStorecan be the same-slot EBB and compensation must restore that exact point.Ouroboros Consensus models non-genesis points as
(slot, headerHash)and matches the complete point during header and ledger rewind. Yano should preserve the same identity through its rollback pipeline.Proposed scope
Add a point-aware ChainState rollback API, for example
rollbackTo(Point), and propagate the ChainSync target hash instead of discarding it.Distinguish an EBB from the regular block at the same slot using the existing EBB and main-block indexes.
Update
DefaultUtxoStore.rollbackToso a delta is retained only when:Reverse a same-slot delta whose hash differs from the target hash.
Require a hash for non-origin canonical rollback points. Fail closed when an equal-slot delta lacks sufficient hash identity.
Audit other rollback-capable stores and compensation/reconciliation callers to ensure none silently reduce an exact point to slot-only identity.
Preserve or deliberately deprecate the slot-only ChainState API after callers migrate.
No RocksDB schema or index migration is expected: UTXO deltas already store the block hash, and ChainState already stores EBB hashes separately from main-block slot mappings.
Acceptance criteria
main Nretains its matching delta and point.main N -> EBB N -> main N+1, rollback to the EBB removes state frommain N+1and restores the exact EBB ChainState tip.main N+1at the same slot retains its matching-hash delta.Context
Proposed ADR-041 now includes exact point-aware rollback as part of the Byron main-block UTXO application decision. This issue tracks the ChainState, UTXO, compensation, and rollback-audit portion of that ADR so the same-slot EBB recovery behavior is implemented and verified with the feature.