Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new ban mechanism based on validator participation #17

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/RewardPool/RewardPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ contract RewardPool is RewardPoolBase, System, StakingRewards, DelegationRewards
require(uptime.signedBlocks <= totalBlocks, "SIGNED_BLOCKS_EXCEEDS_TOTAL");

(, , uint256 totalStake, uint256 commission, , ) = validatorSet.getValidator(uptime.validator);

DelegationPool storage delegationPool = delegationPools[uptime.validator];
uint256 delegation = delegationPool.supply;
Vitomir2 marked this conversation as resolved.
Show resolved Hide resolved
// slither-disable-next-line divide-before-multiply
Expand All @@ -105,6 +106,9 @@ contract RewardPool is RewardPoolBase, System, StakingRewards, DelegationRewards
_saveValRewardData(uptime.validator, epochId);
}

// Update the validator's last activity
validatorSet.updateValidatorParticipation(uptime.validator);

return validatorReward;
}

Expand Down
12 changes: 7 additions & 5 deletions contracts/RewardPool/RewardPoolBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ abstract contract RewardPoolBase is IRewardPool, Initializable {
/// @notice The address of the ValidatorSet contract
IValidatorSet public validatorSet;

// _______________ Modifiers _______________

modifier onlyValidatorSet() {
if (msg.sender != address(validatorSet)) revert Unauthorized("VALIDATORSET");
_;
}

// _______________ Initializer _______________

function __RewardPoolBase_init(IValidatorSet newValidatorSet) internal onlyInitializing {
Expand All @@ -23,9 +30,4 @@ abstract contract RewardPoolBase is IRewardPool, Initializable {
function __RewardPoolBase_init_unchained(IValidatorSet newValidatorSet) internal onlyInitializing {
validatorSet = newValidatorSet;
}

modifier onlyValidatorSet() {
if (msg.sender != address(validatorSet)) revert Unauthorized("VALIDATORSET");
_;
}
}
37 changes: 21 additions & 16 deletions contracts/RewardPool/modules/StakingRewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ pragma solidity 0.8.17;

import "./Vesting.sol";
import "./RewardsWithdrawal.sol";

import "./../RewardPoolBase.sol";
import "./../libs/DelegationPoolLib.sol";
import "./../libs/VestingPositionLib.sol";

import "./../../common/Errors.sol";
import "./../../common/Errors.sol";

struct ValReward {
Expand All @@ -22,6 +21,8 @@ abstract contract StakingRewards is RewardPoolBase, Vesting, RewardsWithdrawal {
/// @notice The validator rewards mapped to a validator's address
mapping(address => ValReward) public valRewards;

// _______________ External functions _______________

/**
* @inheritdoc IRewardPool
*/
Expand All @@ -47,20 +48,6 @@ abstract contract StakingRewards is RewardPoolBase, Vesting, RewardsWithdrawal {
emit ValidatorRewardClaimed(msg.sender, reward);
}

function claimValidatorReward(uint256 rewardHistoryIndex) public {
if (!positions[msg.sender].isMaturing()) {
revert StakeRequirement({src: "vesting", msg: "NOT_MATURING"});
}

uint256 reward = _calcValidatorReward(msg.sender, rewardHistoryIndex);
if (reward == 0) return;

_claimValidatorReward(msg.sender, reward);
_withdrawRewards(msg.sender, reward);

emit ValidatorRewardClaimed(msg.sender, reward);
}

/**
* @inheritdoc IRewardPool
*/
Expand Down Expand Up @@ -126,6 +113,24 @@ abstract contract StakingRewards is RewardPoolBase, Vesting, RewardsWithdrawal {
return valRewards[validator].total - valRewards[validator].taken;
}

// _______________ Public functions _______________

function claimValidatorReward(uint256 rewardHistoryIndex) public {
if (!positions[msg.sender].isMaturing()) {
revert StakeRequirement({src: "vesting", msg: "NOT_MATURING"});
}

uint256 reward = _calcValidatorReward(msg.sender, rewardHistoryIndex);
if (reward == 0) return;

_claimValidatorReward(msg.sender, reward);
_withdrawRewards(msg.sender, reward);

emit ValidatorRewardClaimed(msg.sender, reward);
}

// _______________ Internal functions _______________

function _saveValRewardData(address validator, uint256 epoch) internal {
ValRewardHistory memory rewardData = ValRewardHistory({
totalReward: valRewards[validator].total,
Expand Down
7 changes: 7 additions & 0 deletions contracts/ValidatorSet/IValidatorSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ enum ValidatorStatus {
None,
Whitelisted,
Registered,
Active,
Banned
}

Expand Down Expand Up @@ -93,4 +94,10 @@ interface IValidatorSet {
* @return Returns array of addresses
*/
function getValidators() external view returns (address[] memory);

/**
* @notice Method to update when the validator was lastly active which can be executed only by the RewardPool
* @param validator The validator to set the last participation for
*/
function updateValidatorParticipation(address validator) external;
}
40 changes: 18 additions & 22 deletions contracts/ValidatorSet/ValidatorSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,6 @@ import "./../common/libs/SafeMathInt.sol";
contract ValidatorSet is ValidatorSetBase, System, AccessControl, PowerExponent, Staking, Delegation, Inspector {
using ArraysUpgradeable for uint256[];

/// @notice Epoch data linked with the epoch id
mapping(uint256 => Epoch) public epochs;
/// @notice Array with epoch ending blocks
uint256[] public epochEndBlocks;

// _______________ Modifiers _______________

modifier onlyRewardPool() {
if (msg.sender != address(rewardPool)) revert Unauthorized("REWARD_POOL");
_;
}

// _______________ Initializer _______________

/**
Expand Down Expand Up @@ -91,8 +79,7 @@ contract ValidatorSet is ValidatorSetBase, System, AccessControl, PowerExponent,
}

/**
* @notice Get the validator by its address
* @param validatorAddress address
* @inheritdoc IValidatorSet
*/
function getValidator(
address validatorAddress
Expand All @@ -114,14 +101,7 @@ contract ValidatorSet is ValidatorSetBase, System, AccessControl, PowerExponent,
stake = totalStake - rewardPool.totalDelegationOf(validatorAddress);
commission = v.commission;
withdrawableRewards = rewardPool.getValidatorReward(validatorAddress);
active = v.status == ValidatorStatus.Registered;
}

/**
* @inheritdoc IValidatorSet
*/
function getValidators() public view returns (address[] memory) {
return validatorsAddresses;
active = v.status == ValidatorStatus.Active;
}

/**
Expand All @@ -140,6 +120,22 @@ contract ValidatorSet is ValidatorSetBase, System, AccessControl, PowerExponent,
return epochs[epochIndex];
}

/**
* @inheritdoc IValidatorSet
*/
function updateValidatorParticipation(address validator) external onlyRewardPool {
_updateParticipation(validator);
}

// _______________ Public functions _______________

/**
* @inheritdoc IValidatorSet
*/
function getValidators() public view returns (address[] memory) {
return validatorsAddresses;
}

// slither-disable-next-line unused-state,naming-convention
uint256[50] private __gap;
}
51 changes: 51 additions & 0 deletions contracts/ValidatorSet/ValidatorSetBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,55 @@ abstract contract ValidatorSetBase is IValidatorSet, Initializable {
bytes32 public constant DOMAIN = keccak256("DOMAIN_VALIDATOR_SET");

IBLS public bls;

IRewardPool public rewardPool;

uint256 public currentEpochId;

// slither-disable-next-line naming-convention
mapping(address => Validator) public validators;

address[] public validatorsAddresses;

/// @notice Epoch data linked with the epoch id
mapping(uint256 => Epoch) public epochs;

/// @notice Array with epoch ending blocks
uint256[] public epochEndBlocks;

mapping(uint256 => uint256) internal _commitBlockNumbers;

/**
* @notice Mapping that keeps the last time when a validator has participated in the consensus
* @dev Keep in mind that the validator will initially be set active when stake,
* but it will be able to participate in the next epoch. So, the validator will have
* less blocks to be considered for ban.
Vitomir2 marked this conversation as resolved.
Show resolved Hide resolved
*/
mapping(address => uint256) public validatorParticipation;

// _______________ Modifiers _______________

modifier onlyRewardPool() {
if (msg.sender != address(rewardPool)) revert Unauthorized("REWARD_POOL");
_;
}

modifier onlyActiveValidator(address validator) {
if (validators[validator].status != ValidatorStatus.Active) revert Unauthorized("INACTIVE_VALIDATOR");
_;
}

/// @notice Modifier to check if the validator is registered or active
modifier onlyValidator(address validator) {
if (
validators[validator].status != ValidatorStatus.Registered &&
validators[validator].status != ValidatorStatus.Active
) revert Unauthorized("INVALID_VALIDATOR");
_;
}

// _______________ Initializer _______________

function __ValidatorSetBase_init(IBLS newBls, IRewardPool newRewardPool) internal onlyInitializing {
__ValidatorSetBase_init_unchained(newBls, newRewardPool);
}
Expand All @@ -29,6 +70,8 @@ abstract contract ValidatorSetBase is IValidatorSet, Initializable {
currentEpochId = 1;
}

// _______________ Internal functions _______________

function _verifyValidatorRegistration(
address signer,
uint256[2] calldata signature,
Expand All @@ -45,6 +88,14 @@ abstract contract ValidatorSetBase is IValidatorSet, Initializable {
return bls.hashToPoint(DOMAIN, abi.encodePacked(signer, block.chainid));
}

/**
* @notice Method used to update the participation
* @param validator address
*/
function _updateParticipation(address validator) internal {
validatorParticipation[validator] = block.number;
}

// slither-disable-next-line unused-state,naming-convention
uint256[50] private __gap;
}
4 changes: 1 addition & 3 deletions contracts/ValidatorSet/modules/Delegation/Delegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ abstract contract Delegation is

// _______________ Private functions _______________

function _delegate(address validator, address delegator, uint256 amount) private {
if (validators[validator].status != ValidatorStatus.Registered) revert Unauthorized("INVALID_VALIDATOR");

function _delegate(address validator, address delegator, uint256 amount) private onlyActiveValidator(validator) {
_mint(validator, amount); // increase validator power
StateSyncer._syncStake(validator, balanceOf(validator));
LiquidStaking._distributeTokens(delegator, amount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ abstract contract VestedDelegation is IVestedDelegation, VestFactory {
if (!isVestingManager(msg.sender)) {
revert NotVestingManager();
}

_;
}

Expand Down
20 changes: 14 additions & 6 deletions contracts/ValidatorSet/modules/Inspector/IInspector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ struct WithdrawalInfo {
interface IInspector {
event ValidatorBanned(address indexed validator);

/**
* @notice Manual ban of a validator
* @dev Function can be executed only by the governor/owner
* @param validator Address of the validator
*/
function banValidator(address validator) external;
error ThresholdNotReached();

/**
* @notice Set the penalty amount for the banned validators
Expand All @@ -33,9 +28,22 @@ interface IInspector {
*/
function setReporterReward(uint256 newReward) external;

/**
* @notice Set the threshold that needs to be reached to ban a validator
* @param newThreshold The new threshold in blocks
*/
function setBanThreshold(uint256 newThreshold) external;

/**
* @notice Withdraw funds left for a banned validator
* @dev Function can be executed only by the banned validator
*/
function withdrawBannedFunds() external;

/**
* @notice Method used to ban a validator, if the ban threshold is reached
* @dev This function will validate the threshold only if the executor is not the governor, otherwise will forcely ban the validator
* @param validator Address of the validator
*/
function banValidator(address validator) external;
}
Loading
Loading