IntersectMBO / IntersectMBO/cardano-ledger

Inconsistent voting stake calculation when compared to leader election

Open
#5,014 3 comments 1 reaction 2 assignees Claimed by @aniketd View on GitHub
bug ConwayEra intra-era-hardfork
Dominant language
Haskell
Stars
295
Forks
179
Avg merge
4d 7h
Merged PRs (30d)
29

Description

It has been reported by @KtorZ that the way stake distribution calculation works for voting is incorrect because it is not consistent with the way it works for leader election. Moreover, there is an inconsistency between calculating DRep stake distribution when compared to SPO stake distribution for voting.

After some discussion we have agreed that it would be desired to fix this issue for the next hard fork, whether it will be a new era or an intra-era hard fork.

We need to ensure that we use consistent ledger state for voting stake distribution calculation.

Originally when @KtorZ and I where discussing this issue, here is what we thought that we should do:
* For SPOs it should match stake distribution calculation for leader election, except for in-flight proposals' deposits
* For DReps the process of calculation should match SPOs
* state used for computation must ~not~ be the one that was updated on this epoch boundary, i.e. changes to accounts (eg. treasury withdrawals, refunds, etc.) should ~not~ have been applied ~yet~.

However, It makes much more sense to start computing stake distribution for SPOs incrementally as well as we do today in the DRepPulser, and then we can re-use it for the "Mark" stake distribution snapshot in the SNAP, thus solving #3034 for us at the same time!

The reason why it makes more sense is because we must use the updated state for the pulser due to requiring newest EnactState and protocol parameters and all other parts of the state that are needed for governance. In any case, it should solve the goal for us of having consistent stake distribution when comparing voting and leader election.

Here is a more detailed description of a problem reported by @KtorZ:

> - The snapshot used for the stake pools voting stake distribution is taken at [the beginning of the transition rule](https://github.com/IntersectMBO/cardano-ledger/blob/8467e3585304d0a69a39232178db114a52d25323/eras/conway/impl/src/Cardano/Ledger/Conway/Rules/Epoch.hs#L322) before any of the refunds and withdrawal payment have happened.

> - Yet, the [next pulser state is started from this incomplete stake pool distribution](https://github.com/IntersectMBO/cardano-ledger/blob/8467e3585304d0a69a39232178db114a52d25323/eras/conway/impl/src/Cardano/Ledger/Conway/Rules/Epoch.hs#L403), and with a list of proposals that has been pruned from expired ones. So the deposit for proposals that just expired isn't part of the pools' distribution and won't be added during the incremental pulser calculations. As for the withdrawals, you'll have to wait for _the next epoch_ to see them part of the stake pools voting stake distribution (whereas they are already part of the DRep voting stake distribution!)

> If you look at the voting stake distribution corresponding to the epoch 172 on PreProd (so, computed incrementally during epoch 173); the pool `pool1rccstu3l9ty3k0a5cd06fl3szsss9r34dcg5j38fqgq9kvng0tg` is reported to have:

> - `12,999,926,548,027` lovelaces as voting power, whereas it should have `13,099,926,548,027`.

> Maybe a little table would help:

> epoch | rewards/consensus stake | voting stake |
> --- | --- | --- |
> 171 | 12979415865053 | 13079415865053 |
> 172 | 12999926548027 | 12999926548027 |
> 173 | 13120113522015 | 13120113522015 |

> Note that, the _rewards/consensus stake distribution_ corresponds to the stake *at the end* of the epoch, after rewards have been paid out. It corresponds to the stake resulting from the `SNAP` rule. Whereas the _voting stake distribution_ corresponds to the stake at the beginning of the following epoch, after refunds and withdrawals have been processed, resulting from the `EPOCH` rule.

> The latter may seem weird but, this is exactly what is happening for DReps too, and what the Agda specs seems to indicate (as far as my Agda goes 🙃). The reason I claim the stake is missing from the pool voting power is mainly because of the discrepancy with the DReps. While withdrawals and refunds immediately counts toward the DRep voting stake distribution once processed, the effect on the pool stake distribution is deferred by one epoch.

> .. the issue comes from the `EPOCH` transition rule, which uses the pre-computed stake pool stake distribution as a base for the voting stake distribution before processing refunds and withdrawals. So the incremental pulser calculations are using an incomplete pool stake distribution. Resulting in missing voting stake for the epoch following an expiry or a withdrawal.

@KtorZ thanks again for the bug report. Please add any other useful info to the ticket if you think I missed something.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.