Validator checkpoint signing stake power adjustment

Status: Draft

Abstract: We introduce a new way of weighting stakes where lower stake validators, including delegation, have a higher weight per 1 Matic staked. Instead of signing checkpoints on per Matic staked basis, we use curve-based staking power, where a lower total stake yields a higher relative power.

Motivation: Delegated Proof of Stake networks tends to cluster stakes around the few largest validators and leave the rest of the network as a “dust”. It leads to security issues when Sybil attacks and cartels become increasingly more likely to appear. A new method of adjusting signing power will mitigate some of the power from the biggest validators in the network.

Specification: Current implementation maps 1 Matic to 1 unit of power, and this is a core of the issue because the user has no incentive to stake to low stake validator, and most of the users flock towards popular options, increasing centralization of the network.

In order to fix that, we adopt Uniswap price equation x * y = k for the stake to the shares map function.

Define stake_pool * shares_pool = k, where k is some predefined constant. Now when user stakes, increasing stake_pool, will decrease shares_pool accordingly, so k stays constant. Because of that, the conversion rate of the stake to shares moves on a curve, defined by k.

With that in mind, a new way of staking will be based on shares now rather than staked tokens, and distribution of the power will be on per shares basis rather than a staked tokens.

Pseudocode and algorithm for stake/unstake

struct StakeState {
   uint shares;
   uint sharesPool;
   uint stakePool;
}

mapping(uint => StakeState) stakeState; // validator id => StakeState
uint totalShares; // tracks how many shares in the system.

function toShares(amount, poolIn, poolOut) {
    return amount * poolOut / (poolIn + amount);
}

function getRequiredShares(amountOut, reserveIn, reserveOut) {
  return reserveIn * amountOut / (reserveOut - amountOut);
}

function stake(uint amount, uint validatorId) {
   StakeState state = stakeState[validatorId];
   uint shares = toShares(amount, state.stakePool, state.sharesPool);

   state.stakePool += amount;
   state.sharesPool -= shares;
   state.shares += shares;

   totalShares += shares;
}

function unstake(unit amount, unit validatorId) {
   StakeState state = stakeState[validatorId];
   uint shares = getRequiredShares(amount, state.sharesPool, state.stakePool);

   state.stakePool -= amount;
   state.sharesPool += shares;
   state.shares -= shares;

   totalShares -= shares;
} 

// check if 2/3+1 holds using shares
function commitCheckpoint(uint256 reward) {}

Rationale: The design choice uses a diminishing returns effect. And to control it Isoquant curve is used. By lowering down the amount of power validator gains with each extra Matic staked, we can mitigate some of the power of the biggest validators into the hands of lesser ones.

The choice of the curve is very important to define how fast power per Matic drops. To take a view of different curves visit this spreadsheet (click-me).

Here is a model of the power distribution using current and new approaches. Go to the model (click-me).

Backwards Compatibility: Shares approach would require us to run a special function to convert validators’ stakes into shares just once. Old variables will be untouched, and in case of some unpredictable behavior, implementation can be reverted back to the old one.

Security Considerations: Shares approach is the same as current, with the only difference - curve-based conversion of stake and shares. Changing the curve at any given moment is possible, and will require re-purchasing and resetting the per-share reward counter and should lie within the block gas limit for all validators at once.

FAQ:

  1. Any alternatives considered? Yes, there are other approaches, mostly based on distributing stakes by the network itself, like Polkadot and capping the maximum stake of the validator like Harmony for example. Polkadot option is not viable for us, due to the inability to execute it on the smart contracts. For the Harmony approach - we will get back to it later with adjusted rewards system based on stake shares.
  2. Does this change decrease my existing rewards? No.
  3. Is it possible to get less MATIC out than I put in? No.
2 Likes

Initial reaction: I get the motivation, but I have mixed feelings. Delegation isn’t very liquid today due to the long unbonding period. I am sure a lot of delegators will be very unhappy if their APY can be ruined by some whale joining the same validator, and there isn’t a way to move their funds to another validator that has less MATIC staked.

I think for this change to work, we need to figure out a way to let delegators jump between validators quickly. I understand that it is quite complicated due to the potential fraud risk, but it’s not impossible to fix, we can still slash funds as long as their funds are still staked.

I totally agree that the illiquidity of the stake is a problem! I will take a look at how that could be possibly solved.