Skip to content

EPMB/unsigned: fixed multi-page winner computation#8987

Merged
sigurpol merged 11 commits intomasterfrom
sigurpol/epmb_fix_max_winner_total
Jun 30, 2025
Merged

EPMB/unsigned: fixed multi-page winner computation#8987
sigurpol merged 11 commits intomasterfrom
sigurpol/epmb_fix_max_winner_total

Conversation

@sigurpol
Copy link
Copy Markdown
Contributor

The issue

In FullSupportsOfMiner, we initially considered MaxWinnersPerPage as the overall maximum number of winners across pages. However, it should be calculated as Pages * MaxWinnersPerPage to prevent the computed solution from having a low overall total of winners, which could result in a WrongWinnerCount error.

This bug was identified in
#staking-miner-1074 while testing the staking miner in a setup with aggressive trimming to evaluate backer bounding.

How to test

A test has been added to replicate this scenario:

Test configuration:

  • Pages=2
  • MaxWinnersPerPage=2
  • desired_targets=3 (3 or 4 doesn't matter here, the key point is that is strictly > MaxWinnersPerPage)
  • MaxBackersPerWinner=1

Before the fix in this PR:

  • FullSupportsOfMiner could only hold 2 winners in total (bounded by MaxWinnersPerPage)
  • But the mining algorithm needed to hold 3 winners across multiple pages
  • This would cause a WrongWinnerCount error during verification

With the fix:

  • FullSupportsOfMiner can now hold Pages * MaxWinnersPerPage = 2 * 2 = 4 winners
  • The test passes with 3 winners across 2 pages (proving it can hold more than MaxWinnersPerPage=2)
  • No WrongWinnerCount errors occur

In `FullSupportsOfMiner`, we initially considered `MaxWinnersPerPage` as
the overall maximum total winners across pages. However, it should be
calculated as `Pages * MaxWinnersPerPage`.

This bug was identified in
[#staking-miner-1074](paritytech/polkadot-staking-miner#1074)
while testing the staking miner in a setup with aggressive trimming to
evaluate backer bounding. Consequently, the computed solution had a low
overall total of winners and was rejected with a `WrongWinnerCount`
error. A test has been added to replicate this scenario.
@sigurpol sigurpol requested a review from a team as a code owner June 25, 2025 21:20
@sigurpol sigurpol added the T2-pallets This PR/Issue is related to a particular pallet. label Jun 25, 2025
@sigurpol
Copy link
Copy Markdown
Contributor Author

/cmd prdoc --audience runtime_dev --bump patch

Comment on lines +5 to +30
## The issue

In `FullSupportsOfMiner`, we initially considered `MaxWinnersPerPage` as the overall maximum number of winners across pages. However, it should be calculated as `Pages * MaxWinnersPerPage` to prevent the computed solution from having a low overall total of winners, which could result in a `WrongWinnerCount` error.

This bug was identified in
[#staking-miner-1074](https://github.com/paritytech/polkadot-staking-miner/issues/1074) while testing the staking miner in a setup with aggressive trimming to evaluate backer bounding.

## How to test

A test has been added to replicate this scenario:

Test configuration:
- `Pages=2`
- `MaxWinnersPerPage=2`
- `desired_targets=3` (3 or 4 doesn't matter here, the key point is that is strictly > `MaxWinnersPerPage`)
- `MaxBackersPerWinner=1`

Before the fix in this PR:
- `FullSupportsOfMiner` could only hold 2 winners in total (bounded by `MaxWinnersPerPage`)
- But the mining algorithm needed to hold 3 winners across multiple pages
- This would cause a `WrongWinnerCount` error during verification

With the fix:
- `FullSupportsOfMiner` can now hold `Pages * MaxWinnersPerPage = 2 * 2 = 4` winners
- The test passes with 3 winners across 2 pages (proving it can hold more than `MaxWinnersPerPage=2`)
- No `WrongWinnerCount` errors occur
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.

Suggested change
## The issue
In `FullSupportsOfMiner`, we initially considered `MaxWinnersPerPage` as the overall maximum number of winners across pages. However, it should be calculated as `Pages * MaxWinnersPerPage` to prevent the computed solution from having a low overall total of winners, which could result in a `WrongWinnerCount` error.
This bug was identified in
[#staking-miner-1074](https://github.com/paritytech/polkadot-staking-miner/issues/1074) while testing the staking miner in a setup with aggressive trimming to evaluate backer bounding.
## How to test
A test has been added to replicate this scenario:
Test configuration:
- `Pages=2`
- `MaxWinnersPerPage=2`
- `desired_targets=3` (3 or 4 doesn't matter here, the key point is that is strictly > `MaxWinnersPerPage`)
- `MaxBackersPerWinner=1`
Before the fix in this PR:
- `FullSupportsOfMiner` could only hold 2 winners in total (bounded by `MaxWinnersPerPage`)
- But the mining algorithm needed to hold 3 winners across multiple pages
- This would cause a `WrongWinnerCount` error during verification
With the fix:
- `FullSupportsOfMiner` can now hold `Pages * MaxWinnersPerPage = 2 * 2 = 4` winners
- The test passes with 3 winners across 2 pages (proving it can hold more than `MaxWinnersPerPage=2`)
- No `WrongWinnerCount` errors occur
Change the calculation of `MaxWinnersPerPage` in `FullSupportsOfMiner` to `Pages * MaxWinnersPerPage` (instead of the overall maximum number of winners across pages) to prevent the computed solution from having a low overall total of winners, which could result in a `WrongWinnerCount` error.

@sigurpol sigurpol enabled auto-merge June 30, 2025 08:50
>;

/// Helper type that computes the maximum total winners across all pages.
pub struct MaxWinnersTotal<T: MinerConfig>(core::marker::PhantomData<T>);
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.

Elsewhere in the code, we have a similar naming dilemma:

  • MaxBackersPerWinner: page-local bound
  • MaxBackersPerWinnerFinal: bound across all pages

I am happy with either naming schemes (or even a new one) but I do suggest them to be consistent.

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.

very fair point, let me use Final here as well

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.

done

<T as MinerConfig>::AccountId,
<T as MinerConfig>::MaxWinnersPerPage,
MaxWinnersTotal<T>,
<T as MinerConfig>::MaxBackersPerWinnerFinal,
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.

You can actually see the naming convention I mentioned here, noting the Final postfix.

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.

renamed to use Final for consistency, ty

@sigurpol sigurpol disabled auto-merge June 30, 2025 09:26
Copy link
Copy Markdown
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

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

This is a very useful finding! would have been hard to catch later in prod.

The unfortunate note is that we will 99% use configurations in prod that will not trigger any trimming, but in the case of the 1%, it is hard to catch :)

I have a todo in the code about this, which you can now maybe remove and convert to an issue for us as well:

// TODO: we should have a fuzzer for miner that ensures no matter the parameters, it generates a
// valid solution. Esp. for the trimming.

What we did here was a small step towards this! But we can later it push it to the extreme: No matter what is in the snapshot, and what configurations are set in MinerConfig, it MUST produce something that is valid, or fail to produce a solution.

// The solution should still be valid despite aggressive trimming
assert!(solution.solution_pages.len() > 0);

let winner_count = solution
Copy link
Copy Markdown
Contributor

@kianenigma kianenigma Jun 30, 2025

Choose a reason for hiding this comment

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

It is not a great name, but I found winner_count_single_page_target_snapshot.

Copy link
Copy Markdown
Contributor Author

@sigurpol sigurpol Jun 30, 2025

Choose a reason for hiding this comment

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

not completely sold but also I don't mind to change the name either, I was hoping that the comments were enough to explain what we are doing in the test

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.

yeah it is fine either way. Just noted that we have a helper fn that does the same.

@sigurpol
Copy link
Copy Markdown
Contributor Author

This is a very useful finding! would have been hard to catch later in prod.

The unfortunate note is that we will 99% use configurations in prod that will not trigger any trimming, but in the case of the 1%, it is hard to catch :)

I have a todo in the code about this, which you can now maybe remove and convert to an issue for us as well:

// TODO: we should have a fuzzer for miner that ensures no matter the parameters, it generates a
// valid solution. Esp. for the trimming.

What we did here was a small step towards this! But we can later it push it to the extreme: No matter what is in the snapshot, and what configurations are set in MinerConfig, it MUST produce something that is valid, or fail to produce a solution.

Kept the TODO for the time being, and added #9038

@sigurpol sigurpol added this pull request to the merge queue Jun 30, 2025
Merged via the queue into master with commit 75deaab Jun 30, 2025
246 checks passed
@sigurpol sigurpol deleted the sigurpol/epmb_fix_max_winner_total branch June 30, 2025 15:13
alvicsam pushed a commit that referenced this pull request Jun 30, 2025
## The issue 

In `FullSupportsOfMiner`, we initially considered `MaxWinnersPerPage` as
the overall maximum number of winners across pages. However, it should
be calculated as `Pages * MaxWinnersPerPage` to prevent the computed
solution from having a low overall total of winners, which could result
in a `WrongWinnerCount` error.

This bug was identified in

[#staking-miner-1074](paritytech/polkadot-staking-miner#1074)
while testing the staking miner in a setup with aggressive trimming to
evaluate backer bounding.

## How to test 

A test has been added to replicate this scenario:

Test configuration:
- `Pages=2`
- `MaxWinnersPerPage=2`
- `desired_targets=3` (3 or 4 doesn't matter here, the key point is that
is strictly > `MaxWinnersPerPage`)
- `MaxBackersPerWinner=1`

Before the fix in this PR: 
- `FullSupportsOfMiner` could only hold 2 winners in total (bounded by
`MaxWinnersPerPage`)
- But the mining algorithm needed to hold 3 winners across multiple
pages
- This would cause a `WrongWinnerCount` error during verification

With the fix:
- `FullSupportsOfMiner` can now hold `Pages * MaxWinnersPerPage = 2 * 2
= 4` winners
- The test passes with 3 winners across 2 pages (proving it can hold
more than `MaxWinnersPerPage=2`)
- No `WrongWinnerCount` errors occur

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
ordian added a commit that referenced this pull request Jul 1, 2025
* master:
  EPMB/unsigned: fixed multi-page winner computation (#8987)
  Always send full parent header, not only hash, part of collation response (#8939)
ordian added a commit that referenced this pull request Jul 24, 2025
* master: (91 commits)
  Add extra information to the harmless error logs during validate_transaction (#9047)
  `sp-tracing`: Remove `test-utils` feature (#9063)
  add try-state check for staking roles -- staker cannot be nominator a… (#9034)
  net/discovery: File persistence for `AddrCache` (#8839)
  dispute-coordinator: handle race with offchain disabling (#9050)
  Align parameters for `EventEmitter::emit_sent_event` (#9057)
  Fetch parent block `api_version` (#9059)
  [XCM Precompile] Rename functions and improve docs in the Solidity interface (#9023)
  Cleanup and improvements for `ControlledValidatorIndices` (#8896)
  reenable 0001-parachains-pvf (#9046)
  Add optional auto-rebag within on-idle (#8684)
  Fix flaxy 0003-block-building-warp-sync test - one more approach (#8974)
  [Staking] [AHM] Fixes insufficient slashing of nominators (and some other small issues). (#8937)
  chore: Bump bounded-collections dep (#9004)
  XCMP and DMP improvements (#8860)
  EPMB/unsigned: fixed multi-page winner computation (#8987)
  Always send full parent header, not only hash, part of collation response (#8939)
  revive: Precompiles should return dummy code when queried (#9001)
  Fix confusing log messages in network protocol behaviour (#8819)
  Fix pallet_migrations benchmark when FailedMigrationHandler emits events (#8694)
  ...
alvicsam pushed a commit that referenced this pull request Oct 17, 2025
## The issue 

In `FullSupportsOfMiner`, we initially considered `MaxWinnersPerPage` as
the overall maximum number of winners across pages. However, it should
be calculated as `Pages * MaxWinnersPerPage` to prevent the computed
solution from having a low overall total of winners, which could result
in a `WrongWinnerCount` error.

This bug was identified in

[#staking-miner-1074](paritytech/polkadot-staking-miner#1074)
while testing the staking miner in a setup with aggressive trimming to
evaluate backer bounding.

## How to test 

A test has been added to replicate this scenario:

Test configuration:
- `Pages=2`
- `MaxWinnersPerPage=2`
- `desired_targets=3` (3 or 4 doesn't matter here, the key point is that
is strictly > `MaxWinnersPerPage`)
- `MaxBackersPerWinner=1`

Before the fix in this PR: 
- `FullSupportsOfMiner` could only hold 2 winners in total (bounded by
`MaxWinnersPerPage`)
- But the mining algorithm needed to hold 3 winners across multiple
pages
- This would cause a `WrongWinnerCount` error during verification

With the fix:
- `FullSupportsOfMiner` can now hold `Pages * MaxWinnersPerPage = 2 * 2
= 4` winners
- The test passes with 3 winners across 2 pages (proving it can hold
more than `MaxWinnersPerPage=2`)
- No `WrongWinnerCount` errors occur

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T2-pallets This PR/Issue is related to a particular pallet.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants