Staking and rotating Proposers

How Proposers get to make blocks

Nightfall selects proposers using a similar approach to Polygon's Proposer selection protocol, with proposer set based on staking, and Tendermint's proposer selection algorithm.


  • Slots. Units assigned to Proposers based on their stake. The more that a Proposer stakes, the more slots they get assigned. A Proposer must stake at least a minimum amount when they register. Every time a Proposer proposes a block, this stake is reduced by an amount called the block stake. The block stake can be recovered only when the challenge period is over. The minimum stake and block stake are configurable through the variables minimumStake and blockStake in the Config contract.

  • Value per slot. Amount of the stake for each slot, used to calculate the slots that a proposer will be given, based on their stake. For example, if a proposer has 10K staked and the value per slot is 1K then the proposer will have 10 slots to calculate their stake power in the weighted round robin algorithm. This parameter is calculated dynamically based on the proposer with the higher stake.

  • Sprint. The number of L1 blocks during which the current Proposer can propose blocks before another Proposer gets a turn. This is configurable through the variable rotateProposerBlocks in the Config contract.

  • Span. Unit composed of various sprints in which we use the same proposer set for the Weighted Round Robin algorithm to select next current proposer. The number of sprints in a span is configurable through the variable sprintsInSpan in the Config contract.

  • Proposer set. Proposers with calculated weights that will be proposers during the sprints of next span.

  • Proposer set count. After shuffling the slots, we take this number of slots to build the proposer set. The proposer set count is configurable through the variable proposerSetCount in the Config contract.

We need to shuffle the slots at the beginning of each span to introduce some randomness in the proposer selection process. The keccak hash of the block time is used. It's not super random, but adequate for this purpose.

Using a method similar to the Polygon approach, we define the following initial numbers that are configured either by the multisig administrator contract or at contract deployment:

  • Minimum stake; 20K MATIC on Polygon PoS; 1 MWei on Mumbai.

  • Block stake; 200 MATIC on Polygon PoS; 1 Wei on Mumbai.

  • Each Proposer slot is calculated from the biggest stake divided by 10, in order to have 10 slots maximum for the proposer with the biggest stake. 1 Slot = MaxProsposerStake / 10 MATIC.

  • The Proposer set for each span is built from 10 slots after shuffling all the slots. (Random shuffling is employed by Ethereum 2.0 too. It helps to mitigate DoS attacks and collusion among nodes). Proposer set = 10 slots.

  • The span is 10 sprints for a complete rotation of the proposer set. The same number as proposer set count. Span = 10 Sprints.

  • A Sprint is set to 32 blocks. Sprint = 32 Blocks.

The block stake is held in escrow during the optimist challenge window (set at 1 week). If a Layer 2 block is successfully challenged during that period, the Proposer who created that block will lose their block stake (as will any Proposer that build a block on top of the bad block). In the next span the slashed Proposer will have less staking power. (total stake - blocked stake * number of bad block attributed to the Proposer). Once the challenge window is over then the block stake will be restored to the proposer.

If changes are made to the staking of the proposers, or new Proposers are registered, this is taken into account in next span and not in the current span. Thus if you register a new Proposer, you will need to wait for the current span to complete before being considered for inclusion in a span.


Let's suppose we have 3 Proposers - Alice, Bill and Clara:

  • Alice staked 5K Matic tokens whereas Bill and Clara staked 2K Matic tokens.

  • We choose 1K Matic per slot to define the staking power.

  • All the proposers are given these slots [ A, A, A, A, A, B, B, C, C ]

  • Using the block timestamp as a seed, we shuffle this array.

  • After shuffling the slots using the seed, say we get this array [ A, B, A, A, C, B, A, A, C]

  • Now we pop 5 Proposers slots from the top defined by Proposer set as [ A, B, A, A, C]

  • Hence the Proposer set is defined as [ A: 3, B:1, C:1 ].

  • Using this Proposer set and Tendermint's Proposer selection algorithm based on the Weighted Round Robin we choose a proposer for every sprint (5 sprints for 1 span defined). In every sprint the current Proposer proposes blocks and at the end of the sprint a new current Proposer is chosen.

Note that it is necessary to call the changeCurrentProposer function in the State contract to actually rotate the Proposer, but it is in the other Proposers' interest to do that, so that they ca get their turn to make blocks and accrue fees.

A model that gives a good intuition on how / why the selection algorithm works and it is fair is that of a priority queue. The proposers move ahead in this queue according to their voting power (the higher the voting power the faster a proposer moves towards the head of the queue). When the algorithm runs the following happens:

  • All proposers move "ahead" according to their powers: for each proposer, increase the priority by the voting power

  • First in the queue becomes the proposer: select the proposer with highest priority

  • Move the proposer back in the queue: decrease the proposer's priority by the total voting power

How the economics work

The disposition of the different actors profitability and fees are explained below.

The Proposer

  • Proposer profitability: Fees collected from transfers in L2 block.

    ProposerProfit=FeetxtxPerBlockblockProposegasCostgasPriceProposerProfit = Fee_{tx}·txPerBlock-blockPropose_{gasCost}·gasPrice
  • Proposers should need a stake to guarantee possible block challenges or idle situation while they are proposing blocks.

    StakeNeeded=idleProposerStake+(blocksCpblockStake){StakeNeeded} = {idleProposerStake}+\,(blocksCp·blockStake)

    Where StakeNeeded is the stake needed to cover all these possible penalisations, idleProposerStake is the equivalent to the current minimum stake when registered, blocksCp are the blocks in the challenge window still pending for this proposer and _blockStake is the block stake that proposers must submit at every block proposal.

  • The stake the Proposer has in the State contract is used for the Weighted Round Robin algorithm to select next proposer and if they have more amount staked they have a higher probability of being eligible as next proposer, but also they have to guarantee that they have a minimum amount staked to propose new blocks if they are still in the challenge window and are susceptible to challenge. The proposer will be incentivised to stake more MATIC as it will increase the probability to propose blocks in a Weighted Round Robin algorithm.

The Challenger

  • Challengers profitability.

    ChallengerProfitchallengeSuccess=blockStake(challengegasCostgasPrice)ChallengerProfit_{challengeSuccess} = blockStake-(challenge_{gasCost}·gasPrice)
    RoCEperChallengeSuccess=ChallengerProfitchallengeSuccesschallengegasCostgasPriceRoCE_{perChallengeSuccess} = \frac{ChallengerProfit_{challengeSuccess}}{challenge_{gasCost}·gasPrice}
    blockStake>>(challengegasCostgasPrice)blockStake >> (challenge_{gasCost}·gasPrice)

    The blockStakeblockStake will be slashed from the stake of the Proposer in case of successful challenge. So the Proposer will lose this amount of the staking and also will lose power in the Weighted Round Robin algorithm for next span, as it would be calculated with the adjustment of the new stake amount, which is less than before. If they are the current proposer they will also lose their turn because they will be removed from the proposer set.

  • The blockStake should be enough to incentivise the Challenger to create challenges.




Last updated