Skip to content

Commit

Permalink
Made Slashing Penalty a Governance Parameter (#175)
Browse files Browse the repository at this point in the history
* made slash penalty gov param

* Transfer funds to signers[19] & remove if statement from slash function

* Revert "Transfer funds to signers[19] & remove if statement from slash function"

This reverts commit e23085c.

* Transfer funds to signers[19] & remove if statement from slash function

* Made num and denom params for slashing and added test cases for slashing at stake 0 and 1

* Made denom and num five digits and fixed linting issues

Co-authored-by: Skanda Bhat <[email protected]>
  • Loading branch information
GauravJain9 and SkandaBhat authored Jul 8, 2021
1 parent 5d8aac2 commit 6627105
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 14 deletions.
10 changes: 10 additions & 0 deletions contracts/Core/Parameters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ contract Parameters is IParameters, ACL {
// penalty not reveal = 0.01% per epch
uint256 public override penaltyNotRevealNum = 1;
uint256 public override penaltyNotRevealDenom = 10000;
uint256 public override slashPenaltyNum = 10000;
uint256 public override slashPenaltyDenom = 10000;

uint256 public override minStake = 100 * (10 ** 18);
uint256 public override withdrawLockPeriod = 1;
Expand Down Expand Up @@ -53,6 +55,14 @@ contract Parameters is IParameters, ACL {
function setPenaltyNotRevealDeom(uint256 _penaltyNotRevealDenom) external onlyRole(DEFAULT_ADMIN_ROLE) {
penaltyNotRevealDenom = _penaltyNotRevealDenom;
}

function setSlashPenaltyNum(uint256 _slashPenaltyNumerator) external onlyRole(DEFAULT_ADMIN_ROLE){
slashPenaltyNum = _slashPenaltyNumerator;
}

function setSlashPenaltyDenom(uint256 _slashPenaltyDenominator) external onlyRole(DEFAULT_ADMIN_ROLE){
slashPenaltyDenom = _slashPenaltyDenominator;
}

function setWithdrawLockPeriod(uint256 _withdrawLockPeriod) external onlyRole(DEFAULT_ADMIN_ROLE) {
withdrawLockPeriod = _withdrawLockPeriod;
Expand Down
14 changes: 7 additions & 7 deletions contracts/Core/RewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,20 @@ contract RewardManager is Initializable, ACL, RewardStorage {
}

/// @notice The function is used by the Votemanager reveal function
/// to penalise the staker who lost his secret and make his stake zero and
/// transfer to bounty hunter half the schelling tokens of the stakers stake
/// to penalise the staker who lost his secret and make his stake less by "slashPenaltyAmount" and
/// transfer to bounty hunter half the "slashPenaltyAmount" of the staker
/// @param id The ID of the staker who is penalised
/// @param bountyHunter The address of the bounty hunter
function slash(
uint256 id,
address bountyHunter,
uint256 epoch
) external onlyRole(parameters.getRewardModifierHash()) {
uint256 halfStake = stakeManager.getStaker(id).stake / (2);
stakeManager.setStakerStake(id, 0, "Slashed", epoch);
if (halfStake > 1) {
stakeManager.transferBounty(bountyHunter, halfStake);
}
uint256 slashPenaltyAmount = (stakeManager.getStaker(id).stake*parameters.slashPenaltyNum())/parameters.slashPenaltyDenom();
uint256 Stake = stakeManager.getStaker(id).stake - slashPenaltyAmount;
uint256 bountyReward = slashPenaltyAmount/2;
stakeManager.setStakerStake(id, Stake, "Slashed", epoch);
stakeManager.transferBounty(bountyHunter, bountyReward);
}

/// @notice This function is used by StakeManager to increment reward pool,
Expand Down
2 changes: 2 additions & 0 deletions contracts/Core/interface/IParameters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface IParameters {
function gracePeriod() external view returns(uint256);
function aggregationRange() external view returns(uint256);
function exposureDenominator() external view returns(uint256);
function slashPenaltyNum() external view returns(uint256);
function slashPenaltyDenom() external view returns(uint256);
function getEpoch() external view returns(uint256);
function getState() external view returns(uint256);

Expand Down
9 changes: 6 additions & 3 deletions test/BlockManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,16 +258,19 @@ describe('BlockManager', function () {

const firstProposedBlockIndex = (firstProposedBlock.proposerId.gt(secondProposedBlock.proposerId))
? 1 : 0;
const stakerIdAccount = await stakeManager.stakerIds(signers[5].address);
const stakeBeforeAcc5 = (await stakeManager.getStaker(stakerIdAccount)).stake;
const balanceBeforeAcc19 = await schellingCoin.balanceOf(signers[19].address);

await blockManager.connect(signers[19]).finalizeDispute(epoch, firstProposedBlockIndex);
const proposedBlock = await blockManager.proposedBlocks(epoch, firstProposedBlockIndex);

assert((await proposedBlock.valid) === false);

const stakerIdAccount = await stakeManager.stakerIds(signers[5].address);
const slashPenaltyAmount = (stakeBeforeAcc5.mul((await parameters.slashPenaltyNum()))).div(await parameters.slashPenaltyDenom());

assertBNEqual((await stakeManager.getStaker(stakerIdAccount)).stake, toBigNumber('0'));
assertBNEqual(await schellingCoin.balanceOf(signers[19].address), tokenAmount('210000'));
assertBNEqual((await stakeManager.getStaker(stakerIdAccount)).stake, stakeBeforeAcc5.sub(slashPenaltyAmount));
assertBNEqual(await schellingCoin.balanceOf(signers[19].address), balanceBeforeAcc19.add(slashPenaltyAmount.div('2')));
});

it('block proposed by account 6 should be confirmed', async function () {
Expand Down
87 changes: 83 additions & 4 deletions test/VoteManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ describe('VoteManager', function () {
await mineToNextEpoch();
await schellingCoin.transfer(signers[3].address, tokenAmount('423000'));
await schellingCoin.transfer(signers[4].address, tokenAmount('19000'));
await schellingCoin.transfer(signers[5].address, tokenAmount('1000'));
await schellingCoin.transfer(signers[6].address, tokenAmount('1000'));
await schellingCoin.connect(signers[3]).approve(stakeManager.address, tokenAmount('420000'));
await schellingCoin.connect(signers[4]).approve(stakeManager.address, tokenAmount('19000'));
await schellingCoin.connect(signers[5]).approve(stakeManager.address, tokenAmount('1000'));
await schellingCoin.connect(signers[6]).approve(stakeManager.address, tokenAmount('1000'));
const epoch = await getEpoch();
await stakeManager.connect(signers[3]).stake(epoch, tokenAmount('420000'));
await stakeManager.connect(signers[4]).stake(epoch, tokenAmount('19000'));
Expand Down Expand Up @@ -299,7 +303,7 @@ describe('VoteManager', function () {
const epoch = await getEpoch();

const stakerIdAcc4 = await stakeManager.stakerIds(signers[4].address);
const stakeBefore = (await stakeManager.stakers(stakerIdAcc4)).stake;
const stakeBeforeAcc4 = (await stakeManager.stakers(stakerIdAcc4)).stake;

const votes = [100, 200, 300, 400, 500, 600, 700, 800, 900];
const tree = merkle('keccak256').sync(votes);
Expand All @@ -312,10 +316,10 @@ describe('VoteManager', function () {
'0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd',
signers[4].address);

const stakeAfter = (await stakeManager.stakers(stakerIdAcc4)).stake;
const stakeAcc10 = await schellingCoin.connect(signers[10]).balanceOf(signers[10].address);
assertBNEqual(stakeAfter, toBigNumber('0'), 'stake should be zero');
assertBNEqual(stakeAcc10, stakeBefore.div('2'), 'the bounty hunter should receive half of the stake of account 4');
const slashPenaltyAmount = (stakeBeforeAcc4.mul((await parameters.slashPenaltyNum()))).div(await parameters.slashPenaltyDenom());
assertBNEqual((await stakeManager.stakers(stakerIdAcc4)).stake, stakeBeforeAcc4.sub(slashPenaltyAmount), 'stake should be less by slashPenalty');
assertBNEqual(stakeAcc10, slashPenaltyAmount.div('2'), 'the bounty hunter should receive half of the slashPenaltyAmount of account 4');
});

it('Account 3 should be able to reveal again with correct rewards', async function () {
Expand Down Expand Up @@ -343,6 +347,81 @@ describe('VoteManager', function () {
const stakeAfter = (await stakeManager.stakers(stakerIdAcc3)).stake;
assertBNEqual(stakeBefore.add(rewardPool), stakeAfter);
});

it('Should be able to slash if stake is zero', async function () {
await mineToNextEpoch();
const epoch = await getEpoch();

await parameters.setMinStake(0);
await stakeManager.connect(signers[6]).stake(epoch, tokenAmount('0'));

const votes2 = [100, 200, 300, 400, 500, 600, 700, 800, 900];
const tree2 = merkle('keccak256').sync(votes2);
const root2 = tree2.root();
const commitment3 = utils.solidityKeccak256(
['uint256', 'uint256', 'bytes32'],
[epoch, root2, '0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd']
);

await voteManager.connect(signers[6]).commit(epoch, commitment3);

const stakerIdAcc6 = await stakeManager.stakerIds(signers[6].address);
const stakeBeforeAcc6 = (await stakeManager.stakers(stakerIdAcc6)).stake;
const votes = [100, 200, 300, 400, 500, 600, 700, 800, 900];
const tree = merkle('keccak256').sync(votes);
const proof = [];
for (let i = 0; i < votes.length; i++) {
proof.push(tree.getProofPath(i, true, true));
}
const balanceBeforeAcc10 = await schellingCoin.balanceOf(signers[10].address);

await voteManager.connect(signers[10]).reveal(epoch, tree.root(), votes, proof,
'0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd',
signers[6].address);

const balanceAfterAcc10 = await schellingCoin.balanceOf(signers[10].address);
const slashPenaltyAmount = stakeBeforeAcc6.mul(((await parameters.slashPenaltyNum())).div(await parameters.slashPenaltyDenom()));
const stakeAcc6 = (await stakeManager.stakers(stakerIdAcc6)).stake;
assertBNEqual(stakeAcc6, toBigNumber('0'), 'Stake of account 6 should be zero');
assertBNEqual(balanceAfterAcc10, balanceBeforeAcc10.add(slashPenaltyAmount.div('2')),
'the bounty hunter should receive half of the slashPenaltyAmount of account 4');
});

it('Should be able to slash if stake is one', async function () {
const epoch = await getEpoch();
await stakeManager.connect(signers[5]).stake(epoch, tokenAmount('1'));

const votes2 = [100, 200, 300, 400, 500, 600, 700, 800, 900];
const tree2 = merkle('keccak256').sync(votes2);
const root2 = tree2.root();
const commitment3 = utils.solidityKeccak256(
['uint256', 'uint256', 'bytes32'],
[epoch, root2, '0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd']
);

await voteManager.connect(signers[5]).commit(epoch, commitment3);

const stakerIdAcc5 = await stakeManager.stakerIds(signers[5].address);
const stakeBeforeAcc5 = (await stakeManager.stakers(stakerIdAcc5)).stake;
const votes = [100, 200, 300, 400, 500, 600, 700, 800, 900];
const tree = merkle('keccak256').sync(votes);
const proof = [];
for (let i = 0; i < votes.length; i++) {
proof.push(tree.getProofPath(i, true, true));
}
const balanceBeforeAcc10 = await schellingCoin.balanceOf(signers[10].address);

await voteManager.connect(signers[10]).reveal(epoch, tree.root(), votes, proof,
'0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd',
signers[5].address);

const balanceAfterAcc10 = await schellingCoin.balanceOf(signers[10].address);
const slashPenaltyAmount = (stakeBeforeAcc5.mul((await parameters.slashPenaltyNum()))).div(await parameters.slashPenaltyDenom());
const stakeAfterAcc5 = (await stakeManager.stakers(stakerIdAcc5)).stake;
assertBNEqual(stakeAfterAcc5, stakeBeforeAcc5.sub(slashPenaltyAmount), 'Stake of account 5 should lessen by slashPenaltyAmount');
assertBNEqual(balanceAfterAcc10, balanceBeforeAcc10.add(slashPenaltyAmount.div('2')),
'the bounty hunter should receive half of the slashPenaltyAmount of account 4');
});
});
});
});

0 comments on commit 6627105

Please sign in to comment.