Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #343 from maticnetwork/missing-rewards
Browse files Browse the repository at this point in the history
Adjust rewards for missed checkpoints.
  • Loading branch information
coinsandsteeldev authored Feb 9, 2021
2 parents 51d15e2 + e76fa70 commit 3874b5b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 47 deletions.
135 changes: 101 additions & 34 deletions contracts/staking/stakeManager/StakeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ import {IGovernance} from "../../common/governance/IGovernance.sol";
import {Initializable} from "../../common/mixin/Initializable.sol";
import {ValidatorAuction} from "./ValidatorAuction.sol";

contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, DelegateProxyForwarder, StakeManagerStorageExtension {
contract StakeManager is
StakeManagerStorage,
Initializable,
IStakeManager,
DelegateProxyForwarder,
StakeManagerStorageExtension
{
using SafeMath for uint256;
using Merkle for bytes32;
using RLPReader for bytes;
Expand Down Expand Up @@ -105,7 +111,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
Public View Methods
*/

function getRegistry() public view returns(address) {
function getRegistry() public view returns (address) {
return registry;
}

Expand Down Expand Up @@ -213,12 +219,22 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
CHECKPOINT_REWARD = newReward;
}

function updateCheckpointRewardParams(
uint256 _rewardDecreasePerCheckpoint,
uint256 _maxSkippedCheckpoints,
uint256 _checkpointRewardDelta
) public onlyGovernance {
require(_maxSkippedCheckpoints.mul(_rewardDecreasePerCheckpoint) <= CHK_REWARD_PRECISION);
require(_checkpointRewardDelta <= CHK_REWARD_PRECISION);

rewardDecreasePerCheckpoint = _rewardDecreasePerCheckpoint;
maxSkippedCheckpoints = _maxSkippedCheckpoints;
checkpointRewardDelta = _checkpointRewardDelta;
}

// New implementation upgrade

function migrateValidatorsData(
uint256 validatorIdFrom,
uint256 validatorIdTo
) public onlyOwner {
function migrateValidatorsData(uint256 validatorIdFrom, uint256 validatorIdTo) public onlyOwner {
for (uint256 i = validatorIdFrom; i < validatorIdTo; ++i) {
ValidatorShare contractAddress = ValidatorShare(validators[i].contractAddress);
if (contractAddress != ValidatorShare(0)) {
Expand All @@ -230,9 +246,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
}
}

function insertSigners(
address[] calldata _signers
) external onlyOwner {
function insertSigners(address[] memory _signers) public onlyOwner {
signers = _signers;
}

Expand Down Expand Up @@ -664,11 +678,12 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele

address delegationContract = validators[validatorId].contractAddress;
if (delegationContract != address(0x0)) {
uint256 delSlashedAmount = IValidatorShare(delegationContract).slash(
validators[validatorId].amount,
validators[validatorId].delegatedAmount,
_amount
);
uint256 delSlashedAmount =
IValidatorShare(delegationContract).slash(
validators[validatorId].amount,
validators[validatorId].delegatedAmount,
_amount
);
_amount = _amount.sub(delSlashedAmount);
}

Expand Down Expand Up @@ -790,6 +805,59 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
return context;
}

function _calculateCheckpointReward(
uint256 blockInterval,
uint256 signedStakePower,
uint256 currentTotalStake
) internal returns (uint256) {
// checkpoint rewards are based on BlockInterval multiplied on `CHECKPOINT_REWARD`
// for bigger checkpoints reward is reduced by rewardDecreasePerCheckpoint for each subsequent interval

// for smaller checkpoints
// if interval is 50% of checkPointBlockInterval then reward R is half of `CHECKPOINT_REWARD`
// and then stakePower is 90% of currentValidatorSetTotalStake then final reward is 90% of R

uint256 targetBlockInterval = checkPointBlockInterval;
uint256 ckpReward = CHECKPOINT_REWARD;
uint256 fullIntervals = Math.min(blockInterval / targetBlockInterval, maxSkippedCheckpoints);

// only apply to full checkpoints
if (fullIntervals > 0 && fullIntervals != prevBlockInterval) {
if (prevBlockInterval != 0) {
// give more reward for faster and less for slower checkpoint
uint256 delta = (ckpReward * checkpointRewardDelta / CHK_REWARD_PRECISION);

if (prevBlockInterval > fullIntervals) {
// checkpoint is faster
ckpReward += delta;
} else {
ckpReward -= delta;
}
}

prevBlockInterval = fullIntervals;
}

uint256 reward;

if (blockInterval > targetBlockInterval) {
// count how many full intervals
uint256 _rewardDecreasePerCheckpoint = rewardDecreasePerCheckpoint;

// calculate reward for full intervals
reward = ckpReward.mul(fullIntervals).sub(ckpReward.mul(((fullIntervals - 1) * fullIntervals / 2).mul(_rewardDecreasePerCheckpoint)).div(CHK_REWARD_PRECISION));
// adjust block interval, in case last interval is not full
blockInterval = blockInterval.sub(fullIntervals.mul(targetBlockInterval));
// adjust checkpoint reward by the amount it suppose to decrease
ckpReward = ckpReward.sub(ckpReward.mul(fullIntervals).mul(_rewardDecreasePerCheckpoint).div(CHK_REWARD_PRECISION));
}

// give proportionally less for the rest
reward = reward.add(blockInterval.mul(ckpReward).div(targetBlockInterval));
reward = reward.mul(signedStakePower).div(currentTotalStake);
return reward;
}

function _increaseRewardAndAssertConsensus(
uint256 blockInterval,
address proposer,
Expand All @@ -803,13 +871,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
uint256 currentTotalStake = validatorState.amount;
require(signedStakePower >= currentTotalStake.mul(2).div(3).add(1), "2/3+1 non-majority!");

// checkpoint rewards are based on BlockInterval multiplied on `CHECKPOINT_REWARD`
// for bigger checkpoints reward is capped at `CHECKPOINT_REWARD`
// if interval is 50% of checkPointBlockInterval then reward R is half of `CHECKPOINT_REWARD`
// and then stakePower is 90% of currentValidatorSetTotalStake then final reward is 90% of R
uint256 reward = blockInterval.mul(CHECKPOINT_REWARD).div(checkPointBlockInterval);
reward = reward.mul(signedStakePower).div(currentTotalStake);
reward = Math.min(CHECKPOINT_REWARD, reward);
uint256 reward = _calculateCheckpointReward(blockInterval, signedStakePower, currentTotalStake);

uint256 _proposerBonus = reward.mul(proposerBonus).div(MAX_PROPOSER_BONUS);
uint256 proposerId = signerToValidator[proposer];
Expand All @@ -825,9 +887,8 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
// update stateMerkleTree root for accounts balance on heimdall chain
accountStateRoot = stateRoot;

uint256 newRewardPerStake = rewardPerStake.add(
reward.sub(_proposerBonus).mul(REWARD_PRECISION).div(signedStakePower)
);
uint256 newRewardPerStake =
rewardPerStake.add(reward.sub(_proposerBonus).mul(REWARD_PRECISION).div(signedStakePower));

// evaluate rewards for validator who did't sign and set latest reward per stake to new value to avoid them from getting new rewards.
_updateValidatorsRewards(unsignedValidators, totalUnsignedValidators, newRewardPerStake);
Expand Down Expand Up @@ -860,7 +921,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
) private {
uint256 initialRewardPerStake = validators[validatorId].initialRewardPerStake;

// attempt to save gas in case if rewards were updated previosuly
// attempt to save gas in case if rewards were updated previosuly
if (initialRewardPerStake < currentRewardPerStake) {
uint256 validatorsStake = validators[validatorId].amount;
uint256 delegatedAmount = validators[validatorId].delegatedAmount;
Expand All @@ -870,12 +931,22 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
validatorId,
validatorsStake,
delegatedAmount,
_getEligibleValidatorReward(validatorId, combinedStakePower, currentRewardPerStake, initialRewardPerStake)
_getEligibleValidatorReward(
validatorId,
combinedStakePower,
currentRewardPerStake,
initialRewardPerStake
)
);
} else {
_increaseValidatorReward(
validatorId,
_getEligibleValidatorReward(validatorId, validatorsStake, currentRewardPerStake, initialRewardPerStake)
_getEligibleValidatorReward(
validatorId,
validatorsStake,
currentRewardPerStake,
initialRewardPerStake
)
);
}
}
Expand Down Expand Up @@ -910,12 +981,8 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
uint256 reward
) private {
uint256 combinedStakePower = delegatedAmount.add(validatorsStake);
(uint256 validatorReward, uint256 delegatorsReward) = _getValidatorAndDelegationReward(
validatorId,
validatorsStake,
reward,
combinedStakePower
);
(uint256 validatorReward, uint256 delegatorsReward) =
_getValidatorAndDelegationReward(validatorId, validatorsStake, reward, combinedStakePower);

if (delegatorsReward > 0) {
validators[validatorId].delegatorsReward = validators[validatorId].delegatorsReward.add(delegatorsReward);
Expand Down Expand Up @@ -1134,7 +1201,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
delete signers[totalSigners - 1];

// bubble last element to the beginning until target signer is met
for (uint i = totalSigners - 1; i > 0; --i) {
for (uint256 i = totalSigners - 1; i > 0; --i) {
if (swapSigner == signerToDelete) {
break;
}
Expand Down
11 changes: 10 additions & 1 deletion contracts/staking/stakeManager/StakeManagerStorageExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,13 @@ contract StakeManagerStorageExtension {
uint256 public rewardPerStake;
address public auctionImplementation;
address[] public signers;
}

uint256 constant CHK_REWARD_PRECISION = 100;
uint256 public prevBlockInterval;
// how much less reward per skipped checkpoint, 0 - 100%
uint256 public rewardDecreasePerCheckpoint;
// how many skipped checkpoints to reward
uint256 public maxSkippedCheckpoints;
// increase / decrease value for faster or slower checkpoints, 0 - 100%
uint256 public checkpointRewardDelta;
}
7 changes: 7 additions & 0 deletions test/helpers/deployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ class Deployer {
stakeManager.contract.methods.updateSignerUpdateLimit(val).encodeABI()
)
}

stakeManager.updateCheckpointRewardParams = (val1, val2, val3) => {
return governance.update(
stakeManager.address,
stakeManager.contract.methods.updateCheckpointRewardParams(val1, val2, val3).encodeABI()
)
}
}

async deployStakeManager(wallets) {
Expand Down
4 changes: 0 additions & 4 deletions test/units/root/RootChain.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ contract('RootChain', async function(accounts) {
accountState[i + 1] = 0
validators.push(i + 1)
}

this.reward = await stakeManager.CHECKPOINT_REWARD()
}

function buildRoot(that) {
Expand Down Expand Up @@ -100,7 +98,6 @@ contract('RootChain', async function(accounts) {
await expectEvent(this.result, 'NewHeaderBlock', {
proposer: this.proposer,
root: this.root,
reward: this.reward,
headerBlockId: this.headerBlockId,
start: this.start.toString(),
end: this.end.toString()
Expand Down Expand Up @@ -201,7 +198,6 @@ contract('RootChain', async function(accounts) {
this.headerBlockId = '10000'
this.proposer = accounts[0]
this.root = buildRoot(this)
this.reward = this.reward.div(new BN(2))
})

testCheckpoint()
Expand Down
Loading

0 comments on commit 3874b5b

Please sign in to comment.