Skip to content

fix(ledger): complete live stake-pool retirement - #107

Merged
satran004 merged 16 commits into
mainfrom
fix/issue_62
Aug 31, 2026
Merged

fix(ledger): complete live stake-pool retirement#107
satran004 merged 16 commits into
mainfrom
fix/issue_62

Conversation

@satran004

Copy link
Copy Markdown
Member

Summary

  • preserve exact transaction/certificate ordering for stake-pool registration and retirement, including both same-block orderings
  • implement the complete Shelley/Conway POOLREAP transition after SNAP and before governance: exact deposit refund, live pool/retirement/registration-slot cleanup, and bounded credential-major delegation cleanup
  • make POOLREAP crash-safe and exactly-once with rollback-v1 chunk journaling, a durable progress cursor, startup recovery, and readiness gating
  • add the fail-closed pool-lifecycle-state-v1 readiness marker; preview chainstates created before this change require a clean resync
  • correct Java and Scalus value-conservation handling so active pool updates do not pay a second pool deposit
  • add deterministic store, integration, running-devnet, kill/recovery, rollback/replay, native, and clean-preprod validation

Closes #62.

Design: ADR-050

Correctness and recovery

  • Ordered block-local overlay respects transaction index -> certificate index.
  • Re-registering an active pool cancels a pending retirement without losing its original deposit or live delegations.
  • Effective retirement removes only live pool state and pool delegation; DRep delegation and history remain intact.
  • Refund uses the deposit stored for that lifecycle and is credited exactly once, or is accounted as unclaimed when the reward credential is not registered.
  • Every large cleanup is bounded and resumable; a progress marker prevents a partial phase from being classified as complete or served externally.
  • Manual rollback uses the existing rollback-v1 journal and restores pool, retirement, delegation, and reward state; deterministic replay reproduces the same digest.
  • The in-memory implementation remains explicitly test-only and mirrors lifecycle semantics without being presented as durable consensus state.

Validation

  • Focused unit/store/recovery suites and full Scalus bridge tests passed.
  • Fresh isolated :app:integrationTest: 12 tests in five suites, 0 failures/errors/skips, 2m12s.
  • Running signed RocksDB devnet A–F matrix: all six lifecycle/order scenarios passed.
  • Real SIGKILL after the durable POOLREAP cursor resumed from the same stage/sequence before readiness; refund remained exactly 500 ADA once; DRep delegation survived.
  • Manual rollback and two independent replays produced the identical digest 500c13f516641be864cd3d39525209588b0f73f1e7388aebe23fadf5f6b9bef2.
  • Clean native G1 preprod replay reached tip at epoch 310 with fail-fast epoch validation: 305/305 exact Koios AdaPot matches for epochs 5–310.
  • Preprod processed 85 retirement epochs, 137 pools, and 1,586 delegations; POOLREAP averaged 13.05 ms (41 ms max), while all boundaries averaged 1.742 s (5.719 s max).
  • Peak reported native heap was about 901 MiB; observed RSS was about 1.31–1.48 GiB on a contended host.
  • Current-head Oracle GraalVM 25.3 G1 image accepted 4,121/4,121 real submitted transactions at 63.1 tx/s, with no native linkage, reflection, class-loading, or spurious value-conservation errors.

Evidence:

Rollout consequence

This intentionally does not migrate previously created preview account state. Existing mainnet chainstate and retained checkpoints do not carry pool-lifecycle-state-v1 and will fail closed with backup-and-resync guidance. After merge, rollout requires a clean mainnet resync and checkpoint-ladder rebuild as documented in UPGRADING.md. No clean mainnet replay was started as part of this pre-merge test gate.

Related follow-ups

Records ADR-050 Phase 0 evidence and preserves the red lifecycle and POOLREAP gates as phase-disabled tests so the module suite remains green.
ADR-050 Phase 1 gate: make same-block pool certificates observe prior writes, preserve active lifecycle deposits, use registration-epoch parameters for fresh deposits, and prove byte-identical block rollback.
ADR-050 Phase 2 gate: apply exact refunds and live pool cleanup from one validated plan, scan pool delegations cache-cold in bounded resumable chunks, and classify an active pool-reap marker as incomplete.
ADR-050 Phase 3 hygiene gate: PoolReapProcessor is now the sole pool-refund owner, eliminating the dead second path and its stale boundary comment.
@beanbot-gh

Copy link
Copy Markdown

Reviewer verdict: APPROVED (ADR-050 campaign reviewer; merge is the maintainer's call.)

Every one of the 16 commits was reviewed pre- or at-commit against ADR-050's phase gates, with independent test runs at each step. Verified at head e67ed5e7:

  • Semantics: both POOLREAP leaves stale live pool/delegation state and same-block re-registration can miss retirement #62 defects fixed and Haskell-verified (Pool.hs/PoolReap.hs at the pinned commit) — ordered same-block certificates via a block-local overlay; complete POOLREAP (exact per-lifecycle deposit refund, live pool/retirement/reg-slot/delegation cleanup, DRep + history preserved); re-registration preserves the original deposit.
  • Crash-safety: rollback-v1 chunk journaling + durable progress cursor; ±commit fault-injection matrix; a real SIGKILL drill mid-POOLREAP resumed at the identical cursor with exactly-once refund, and rollback + two replays produced identical live-state digests.
  • Compatibility: pool-lifecycle-state-v1 fails closed with typed startup termination and zero writes to rejected stores (digest-proven); no v2 formats. Note the rollout consequence: all pre-existing chainstates (including mainnet) require a clean resync per UPGRADING.md.
  • Validation: devnet A–F matrix with real transactions (6/6); clean native preprod replay to tip — 305/305 Koios AdaPot exact (epochs 5–310), 137 pools/1,586 delegations reaped, POOLREAP 13 ms avg/41 ms max, memory in the ADR-048 envelope; current-head native G1 admission load — 4,121/4,121 real transactions accepted at 63.1 tx/s with zero linkage/validation failures; :app:integrationTest green in fresh isolated worktrees (two independent runs).
  • Bonus fixes: two latent main defects found by the new E2E matrix and fixed here — pool-deposit overcharge in ccl-ledger-rules and (via a guard-tested, TODO-linked bridge override) in Scalus 0.18.2 (upstream report drafted; also verified unfixed in Scalus 1.1.1 — upgrade tracked in Upgrade Scalus 0.18.2 → 1.x (keep pool-deposit override until fixed upstream) #106); DuckDB driver classloader registration (Native image drops DuckLake ProjectionSinkProvider — devnet-native startup fails with projection enabled #105-adjacent).

@satran004
satran004 merged commit e2566ad into main Aug 31, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

POOLREAP leaves stale live pool/delegation state and same-block re-registration can miss retirement

2 participants