Repository navigation
fix(scanner): point-in-time PoS reads and typed replay errors - #2152
Merged
nullPointerEnjoyer merged 6 commits intoOct 31, 2026
Merged
nullPointerEnjoyer merged 6 commits into
nullPointerEnjoyer merged 6 commits into
Conversation
Fee calculation and pool decommission now read pool state as of the connected height, and the point-in-time delegation shares lookup uses a lateral join over the latest-state cache with primary-key history lookups instead of scanning the delegation history of the pool.
nullPointerEnjoyer
requested review from
ImplOfAnImpl,
anyxem and
erubboli
as code owners
October 31, 2026 07:36
erubboli
approved these changes
Oct 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Re-creation of #2149 (head was re-created on top of current master after the dependency and CI fixes landed via #2148; the old PR could not be reopened).
Problem
The scanner's consensus-state reads are not point-in-time.
update_tables_from_consensus_datareads pool data and delegation shares as the latest rows in storage, not the state as of the block being connected. When a replay (rescan, partial restore, crash-loop restart) reconnects blocks below surviving future rows, PoS rewards are credited on top of future state. On mainnet this produced a permanent silent drift for the affected pool: an 18x block-subsidy pile-up at a single height, then stretches of +0 staker credits, leaving the scanner ~1600 ML ahead of chain truth. The same latest-row reads in the delegation spend path then let a legitimate on-chain spend underflow the drifted delegation balance, andDelegation::spend_sharepanicked (not underflow), crash-looping the scanner.Fix
get_pool_data_at_or_below_height,get_delegation_at_or_below_height,get_pool_delegations_at_or_below_height(postgres reads history tables bounded by block height, deliberately bypassing the listing caches; in-memory reads the per-height maps bounded the same way). The consensus, delegation-spend, and delegate-transactions paths now read state as of the block being connected. A replay below future rows can no longer observe them, and catches up instead of piling rewards onto the future.Delegation::spend_shareandPoolDataWithExtraInfo::decrease_delegation_balancereturnResultinstead of panicking on underflow; failures propagate asBlockchainStateError::PosAccountingError(scanners stay up and surface an error instead of crash-looping).Test plan
cargo test -p api-server-common -p api-blockchain-scanner-libgreen, including new tests; postgres containerized backend suite green (8/8, including new point-in-time read tests)../do_checks.shsteps (fmt, cargo deny, cargo vet, both clippy passes, codecheck.py, wasm-doc check) pass locally.cargo testgreen; wasm job flow (wasm-pack build, tsc, node tests, knip) green locally.