Skip to content

Multi-block PSE - Final Features and TODO Cleanup#111

Merged
metalarm10 merged 15 commits into
masterfrom
john/pse-final-features
Mar 26, 2026
Merged

Multi-block PSE - Final Features and TODO Cleanup#111
metalarm10 merged 15 commits into
masterfrom
john/pse-final-features

Conversation

@metalarm10

@metalarm10 metalarm10 commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Description

This PR Closes Following Tasks:

  1. https://app.clickup.com/t/868hj3nw6
  2. https://app.clickup.com/t/868hu7kuc
  3. https://app.clickup.com/t/868hxtvez
  4. https://app.clickup.com/t/868hxtqb3

Summary

  1. ExcludedAddressScore store: New Map[AccAddr -> Int] collection. Added addScoreForAddress helper that routes score writes based on exclusion status: excluded addresses accumulate to ExcludedAddressScore instead of AccountScoreSnapshot and TotalScore. Score is restored to AccountScoreSnapshot on re-inclusion. Preserved across genesis export/import.
  2. Fairness bonus: Normalizes batch-ordering bias. After each delegator receives tokens in distribution phase, bonus score credited to next distribution: bonusScore = distributedAmount × elapsed_seconds.
  3. pse_community_buffer: New tampon module account. At distribution start, only the current round's community allocation moves pse_community -> pse_community_buffer. Phase 2 distributes from the buffer; leftover sent to community pool. Limits blast radius on failure.
  4. DistributionBatchSize param: Replaces hardcoded defaultBatchSize = 100. Configurable via MsgUpdateDistributionBatchSize governance message. Initialized to 100 in v7 upgrade.
  5. v7 upgrade modifications: Registers pse_community_buffer account and initializes DistributionBatchSize.
  6. Deterministic gas modifications: Registered MsgUpdateDistributionBatchSize as non-deterministic.
  7. Closed remaining sections marked as TODOs in the current multiblock pse code.
  8. README (x/pse/spec/README.md): Updated to document multi-block distribution flow.

Testing

  1. Test_ExcludedAddress_FullLifecycle / TestExcludedAddress_ScoreLifecycle: excluded score preserved, restored, and genesis-safe.
  2. TestDistribution_FairnessBonus / _SkippedWhenStartedAtZero: bonus score normalization and backward compat guard.
  3. TestMsgUpdateAllocationSchedule_RejectedDuringOngoingDistribution: schedule update blocked during active distribution.
  4. TestMsgUpdateDistributionBatchSize: valid update, zero rejection, wrong authority rejection.
  5. TestCommunityBuffer_AccountInitialized / TestCommunityBuffer_FundIsolation: buffer account existence and fund isolation.
  6. TestPSEDistribution (integration): extended to assert pse_community_buffer drains to zero after each of 3 distributions.
  7. Upgrade integration test: asserts DistributionBatchSize initialized to 100 by v7.
  8. Added explicit LastProcessedDistributionID assertions in multiple-distributions tests.

The final state of all multiblock PSE tests is listed in this document: https://docs.google.com/document/d/15Xi_35mS4zIk4f48oSA4iDojiSvJAVxpXqKqCDhdIu0/edit?usp=sharing

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 changed the title [draft-DO NOT MERGE]: PSE edge cases + todo cleanup Multi-block PSE - Final Features and TODO Cleanups Mar 23, 2026
@metalarm10 metalarm10 changed the title Multi-block PSE - Final Features and TODO Cleanups Multi-block PSE - Final Features and TODO Cleanup Mar 23, 2026
@metalarm10 metalarm10 marked this pull request as ready for review March 23, 2026 11:27
@metalarm10 metalarm10 requested a review from a team as a code owner March 23, 2026 11:27
@metalarm10 metalarm10 requested review from TxCorpi0x, akhlopiachyi, masihyeganeh, miladz68 and ysv and removed request for a team March 23, 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 made 1 comment.
Reviewable status: 0 of 35 files reviewed, 1 unresolved discussion (waiting on akhlopiachyi, masihyeganeh, metalarm10, miladz68, and ysv).


x/pse/keeper/distribute_test.go line 354 at r1 (raw file):

		}},
	}
	err = pseKeeper.BeginCommunityDistribution(ctx, scheduledDistribution, bondDenom)

This issue is not clear to me:
UpdateDistributionScheduleprevents updates during ongoing distribution, but UpdateExcludedAddresses does not have this check. So the exclusion may happen during phase 2, which modifies TotalScore in the middle of the round.

  • Adding exclusion: reduces total denom → increases payouts for remaining delegators in later batches
  • Removing exclusion: increases total denom → decreases payouts for remaining delegators in later batches

It is worth having test coverage for that

@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 35 files reviewed, 1 unresolved discussion (waiting on akhlopiachyi, masihyeganeh, miladz68, TxCorpi0x, and ysv).


x/pse/keeper/distribute_test.go line 354 at r1 (raw file):

Previously, TxCorpi0x wrote…

This issue is not clear to me:
UpdateDistributionScheduleprevents updates during ongoing distribution, but UpdateExcludedAddresses does not have this check. So the exclusion may happen during phase 2, which modifies TotalScore in the middle of the round.

  • Adding exclusion: reduces total denom → increases payouts for remaining delegators in later batches
  • Removing exclusion: increases total denom → decreases payouts for remaining delegators in later batches

It is worth having test coverage for that

Done.

@metalarm10 metalarm10 requested a review from TxCorpi0x March 24, 2026 07:59
TxCorpi0x
TxCorpi0x previously approved these changes Mar 24, 2026

@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 35 files and all commit messages, and resolved 1 discussion.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on akhlopiachyi, 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 29 files and all commit messages, and made 5 comments.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on akhlopiachyi, masihyeganeh, metalarm10, and miladz68).


a discussion (no related file):

ExcludedAddressScore store: New Map[AccAddr -> Int] collection. Added addScoreForAddress helper that routes score writes based on exclusion status: excluded addresses accumulate to ExcludedAddressScore instead of AccountScoreSnapshot and TotalScore. Score is restored to AccountScoreSnapshot on re-inclusion. Preserved across genesis export/import.

But we still keep DelegationTimeEntries for excluded addresses, right ?


a discussion (no related file):
TODO myself: check deeper

  • Fairness bonus: Normalizes batch-ordering bias. After each delegator receives tokens in distribution phase, bonus score credited to next distribution: bonusScore = distributedAmount × elapsed_seconds.

x/pse/keeper/delegation.go line 75 at r3 (raw file):

// addToExcludedScore atomically adds a score value to an excluded address's accumulated score.
// Unlike addToScore, this does NOT update TotalScore - excluded addresses don't participate in distribution.
func (k Keeper) addToExcludedScore(ctx context.Context, addr sdk.AccAddress, score sdkmath.Int) error {

do we resent this score together with 'main score' in the end of PSE distribution ?


x/pse/keeper/delegation.go line 129 at r3 (raw file):

// moveExcludedScoreToSnapshot moves accumulated ExcludedAddressScore back into AccountScoreSnapshot+TotalScore.
// Called when an address is re-included (removed from the exclusion list).
func (k Keeper) moveExcludedScoreToSnapshot(ctx context.Context, distributionID uint64, addr sdk.AccAddress) error {

nit: as idea to differentiate Excluded score and 'other one' maybe you can name it something like 'main'

so it will be:

  • moveExcludedScoreToMain & moveMainScoreToExcluded
  • addToExcludedScore & addToMainScore
    etc.

Renaming of store isn't critical


x/pse/keeper/distribution.go line 92 at r3 (raw file):

	communityCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, communityAmount))
	return k.bankKeeper.SendCoinsFromModuleToModule(
		ctx, types.ClearingAccountCommunity, types.ClearingAccountCommunityBuffer, communityCoins,

nit; name suggestion Buffer -> intermediary (similar to name in banking)

If not painful I would rename

Code quote:

ClearingAccountCommunityBuffer

@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 2 comments.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on akhlopiachyi, masihyeganeh, miladz68, and ysv).


a discussion (no related file):

Previously, ysv (Yaroslav Savchuk) wrote…

ExcludedAddressScore store: New Map[AccAddr -> Int] collection. Added addScoreForAddress helper that routes score writes based on exclusion status: excluded addresses accumulate to ExcludedAddressScore instead of AccountScoreSnapshot and TotalScore. Score is restored to AccountScoreSnapshot on re-inclusion. Preserved across genesis export/import.

But we still keep DelegationTimeEntries for excluded addresses, right ?

Correct, hooks still fire and record delegation time entries for excluded addresses, with proper shares + timestamp.


x/pse/keeper/delegation.go line 75 at r3 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

do we resent this score together with 'main score' in the end of PSE distribution ?

Done. Added pruning of ExcludedAddressScore in cleanup at the end of each distribution round. Now score accumulation will start fresh for each period.

ysv
ysv previously approved these changes Mar 25, 2026

@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 8 files and all commit messages, made 1 comment, and resolved 3 discussions.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on akhlopiachyi, masihyeganeh, metalarm10, and miladz68).


x/pse/types/params.go line 13 at r4 (raw file):

	ClearingAccountCommunity = "pse_community"
	// ClearingAccountCommunityBuffer is a short-lived buffer account used during community distribution.
	ClearingAccountCommunityBuffer = "pse_community_buffer"

nit: does it makes sense to put this account separately ?
Either in the beginning or end of these contstants

Code quote:

ClearingAccountCommunityBuffer = "pse_community_buffer"

@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 3 comments and resolved 3 discussions.
Reviewable status: 21 of 35 files reviewed, all discussions resolved (waiting on akhlopiachyi, masihyeganeh, miladz68, TxCorpi0x, and ysv).


x/pse/keeper/delegation.go line 129 at r3 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

nit: as idea to differentiate Excluded score and 'other one' maybe you can name it something like 'main'

so it will be:

  • moveExcludedScoreToMain & moveMainScoreToExcluded
  • addToExcludedScore & addToMainScore
    etc.

Renaming of store isn't critical

Done


x/pse/keeper/distribution.go line 92 at r3 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

nit; name suggestion Buffer -> intermediary (similar to name in banking)

If not painful I would rename

Done


x/pse/types/params.go line 13 at r4 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

nit: does it makes sense to put this account separately ?
Either in the beginning or end of these contstants

Done

@metalarm10 metalarm10 requested review from TxCorpi0x and ysv March 25, 2026 15:45

@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 14 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on akhlopiachyi, 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 14 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on akhlopiachyi, masihyeganeh, and miladz68).

@metalarm10 metalarm10 merged commit 31c6929 into master Mar 26, 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