Skip to content

[multiblock-PSE] Fix possible failures in delegation phase#119

Merged
metalarm10 merged 3 commits into
masterfrom
john/pse-fix-slashed-validator-redelegations
Apr 13, 2026
Merged

[multiblock-PSE] Fix possible failures in delegation phase#119
metalarm10 merged 3 commits into
masterfrom
john/pse-fix-slashed-validator-redelegations

Conversation

@metalarm10

@metalarm10 metalarm10 commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Closes: https://app.clickup.com/t/868j5ydyz

Description

Problem

In PSE reward distribution phase, distributeToDelegator function calls stakingKeeper.Delegate(...) once per validator the delegator has stake on. If one validator is 100%-slashed (Tokens=0, Shares>0), SDK returns ErrDelegatorShareExRateInvalid from the x/staking module, which propagates to EndBlock and disables PSE chain-wide.

The existingtotalDelegationAmount.IsZero()check only fires when ALL of the delegator's validators are fully slashed (so the sum of balances is zero). It does NOT fire in the mixed case, where the delegator has at least one healthy validator alongside slashed ones - totalDelegationAmount stays positive, the early-return is skipped, and the per-validator loop reaches the slashed one and triggers ErrDelegatorShareExRateInvalid.

The exact scenario described above is produced with unit tests, which resulted in PSE being disabled due to returned error from the Staking module.

Fix

Skip zero-balance delegations in the per-validator loop. The proportional split formula already excludes them from the denominator, so the slashed slice is naturally redirected to healthy validators with no leftover. The delegator receives their full reward; but it just lands entirely on healthy delegations.

Why only this error is handled

Staking module keeper method usages in multi-block PSE reviewed: stakingKeeper.Delegate, GetValidator, DelegatorDelegations. Possible returned error cases are:

  • ErrDelegatorShareExRateInvalid: fully-slashed validator; fixed by this PR.
  • ErrInsufficientFunds: unreachable; PSE sends funds to the wallet right before Delegate in the same atomic call.
  • ErrInvalidCoins: unreachable; sdk.NewCoins filters zero coins, Delegate(val, 0) is a silent no-op.
  • ErrNoValidatorFound: unreachable; validator removal also removes the delegation row.
  • KV / hook / panic errors: genuinely critical; existing disable behavior is preserved.

Based on this assessment, only the fully-slashed validator case requires handling at the PSE layer. All other reachable failures are either unreachable in practice or genuinely critical and should keep the existing disable behavior.

Tests

TestDistribution_SlashedValidator_RedirectsRewardToHealthy: full EndBlocker flow, asserts PSE not disabled, distribution finalized, slashed slice fully redirected to healthy validator (wallet leftover ≤ 1 subunit). Fails without the fix.

Reviewers checklist:

  • Try to write more meaningful comments with clear actions to be taken.
  • Nit-picking should be unblocking. Focus on core issues.

Authors checklist

  • Provide a concise and meaningful description
  • Review the code yourself first, before making the PR.
  • Annotate your PR in places that require explanation.
  • Think and try to split the PR to smaller PR if it is big.

This change is Reviewable

@metalarm10 metalarm10 marked this pull request as ready for review April 8, 2026 10:19
@metalarm10 metalarm10 requested a review from a team as a code owner April 8, 2026 10:19
@metalarm10 metalarm10 requested review from TxCorpi0x, masihyeganeh, miladz68 and ysv and removed request for a team April 8, 2026 10:19

@TxCorpi0x TxCorpi0x left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TxCorpi0x made 1 comment.
Reviewable status: 0 of 2 files reviewed, 1 unresolved discussion (waiting on masihyeganeh, metalarm10, miladz68, and ysv).


x/pse/keeper/distribute.go line 381 at r1 (raw file):

	for _, delegation := range delegations {
		// Skip fully-slashed validators (Balance=0) and auto-delegate only to healthy ones.
		// Otherwise SDK Delegate returns ErrDelegatorShareExRateInvalid and disables PSE.

So the only error that may lead to delegation failure and PSE disablement is ErrDelegatorShareExRateInvalid
Can we have the list of all possible errors in a comment, or having TODO for each version to make sure there is no new logic has been defined in the upgrade version of SDK

@metalarm10 metalarm10 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@metalarm10 made 1 comment.
Reviewable status: 0 of 2 files reviewed, 1 unresolved discussion (waiting on masihyeganeh, miladz68, TxCorpi0x, and ysv).


x/pse/keeper/distribute.go line 381 at r1 (raw file):

Previously, TxCorpi0x wrote…

So the only error that may lead to delegation failure and PSE disablement is ErrDelegatorShareExRateInvalid
Can we have the list of all possible errors in a comment, or having TODO for each version to make sure there is no new logic has been defined in the upgrade version of SDK

PR description includes all possible errors based on used Staking module functions:

  • ErrDelegatorShareExRateInvalid: fully-slashed validator; fixed by this PR.
  • ErrInsufficientFunds: unreachable; PSE sends funds to the wallet right before Delegate in the same atomic call.
  • ErrInvalidCoins: unreachable; sdk.NewCoins filters zero coins, Delegate(val, 0) is a silent no-op.
  • ErrNoValidatorFound: unreachable; validator removal also removes the delegation row.
  • KV / hook / panic errors: genuinely critical; existing disable behavior is preserved.
    If you have some other possible error concerns that we need to handle, please feel free to specify.

@metalarm10 metalarm10 requested a review from TxCorpi0x April 8, 2026 11:27

@TxCorpi0x TxCorpi0x left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TxCorpi0x reviewed 2 files and all commit messages, and resolved 1 discussion.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on masihyeganeh, miladz68, and ysv).

@ysv ysv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysv reviewed 2 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on masihyeganeh and miladz68).

@metalarm10 metalarm10 merged commit 6f4e1ea into master Apr 13, 2026
14 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.

3 participants