Skip to content

Commit

Permalink
Squash merge from remote branch whitelisting-validators
Browse files Browse the repository at this point in the history
  • Loading branch information
nxqbao committed Sep 21, 2022
1 parent 46f07a3 commit 0db9b0f
Show file tree
Hide file tree
Showing 14 changed files with 707 additions and 7 deletions.
20 changes: 20 additions & 0 deletions contracts/interfaces/IRoninValidatorSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import "./ICandidateManager.sol";
interface IRoninValidatorSet is ICandidateManager {
/// @dev Emitted when the number of max validator is updated
event MaxValidatorNumberUpdated(uint256);
/// @dev Emitted when the number of reserved slots for prioritized validators is updated
event MaxPrioritizedValidatorNumberUpdated(uint256);
/// @dev Emitted when the number of blocks in epoch is updated
event NumberOfBlocksInEpochUpdated(uint256);
/// @dev Emitted when the number of epochs in period is updated
Expand All @@ -23,6 +25,8 @@ interface IRoninValidatorSet is ICandidateManager {
event MiningRewardDistributed(address validatorAddr, uint256 amount);
/// @dev Emitted when the amount of RON reward is distributed.
event StakingRewardDistributed(uint256 amount);
/// @dev Emitted when the priority status of validators is updated
event ValidatorPriorityStatusUpdated(address[], bool[]);

///////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS FOR COINBASE //
Expand Down Expand Up @@ -108,6 +112,11 @@ interface IRoninValidatorSet is ICandidateManager {
*/
function maxValidatorNumber() external view returns (uint256 _maximumValidatorNumber);

/**
* @dev Returns the number of reserved slots for prioritized validators
*/
function maxPrioritizedValidatorNumber() external view returns (uint256 _maximumPrioritizedValidatorNumber);

/**
* @dev Returns the epoch index from the block number.
*/
Expand Down Expand Up @@ -169,4 +178,15 @@ interface IRoninValidatorSet is ICandidateManager {
*
*/
function setNumberOfEpochsInPeriod(uint256 _numberOfEpochsInPeriod) external;

/**
* @dev Updates the status to an array of validators that they will be prioritized or not
*
* Requirements:
* - The method caller is the governance admin
*
* Emits the event `MaxPrioritizedValidatorNumberUpdated`
*
*/
function setPrioritizedValidators(address[] memory _validatorAddresses, bool[] memory _prioritizedStatuses) external;
}
7 changes: 7 additions & 0 deletions contracts/libraries/Math.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ library Math {
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}

/**
* @dev Returns the difference between to unsigned numbers.
*/
function diff(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : b - a;
}
}
17 changes: 17 additions & 0 deletions contracts/mocks/MockRoninValidatorSetArrangeValidators.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import "../validator/RoninValidatorSet.sol";

contract MockRoninValidatorSetArrangeValidators is RoninValidatorSet {
constructor() RoninValidatorSet() {}

function arrangeValidatorCandidates(address[] memory _candidates, uint _newValidatorCount)
external
view
returns (address[] memory)
{
return _arrangeValidatorCandidates(_candidates, _newValidatorCount);
}
}
12 changes: 12 additions & 0 deletions contracts/mocks/MockValidatorSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,16 @@ contract MockValidatorSet is IRoninValidatorSet, CandidateManager {
function setNumberOfEpochsInPeriod(uint256 _numberOfEpochsInPeriod) external override {}

function maxValidatorNumber() external view override returns (uint256 _maximumValidatorNumber) {}

function maxPrioritizedValidatorNumber()
external
view
override
returns (uint256 _maximumPrioritizedValidatorNumber)
{}

function setPrioritizedValidators(address[] memory __validatorAddresses, bool[] memory __prioritizedList)
external
override
{}
}
89 changes: 86 additions & 3 deletions contracts/validator/RoninValidatorSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,14 @@ contract RoninValidatorSet is
uint256 public validatorCount;
/// @dev Mapping from validator index => validator address
mapping(uint256 => address) internal _validator;
/// @dev Mapping from validator address => bool
/// @dev Mapping from address => flag indicating whether the address is validator or not
mapping(address => bool) internal _validatorMap;

/// @dev Mapping from address => flag indicating whether the address is prioritized to be a validator
mapping(address => bool) internal _prioritizedRegisterredMap;
/// @dev The number of slot that is reserved for prioritized validators
uint256 internal _maxPrioritizedValidatorNumber;

/// @dev Mapping from validator address => the last period that the validator has no pending reward
mapping(address => mapping(uint256 => bool)) internal _rewardDeprecatedAtPeriod;
/// @dev Mapping from validator address => the last block that the validator is jailed
Expand Down Expand Up @@ -79,6 +84,7 @@ contract RoninValidatorSet is
address __stakingVestingContract,
uint256 __maxValidatorNumber,
uint256 __maxValidatorCandidate,
uint256 __maxPrioritizedValidatorNumber,
uint256 __numberOfBlocksInEpoch,
uint256 __numberOfEpochsInPeriod
) external initializer {
Expand All @@ -87,6 +93,7 @@ contract RoninValidatorSet is
_setStakingVestingContract(__stakingVestingContract);
_setMaxValidatorNumber(__maxValidatorNumber);
_setMaxValidatorCandidate(__maxValidatorCandidate);
_setPrioritizedReservedSlotNumber(__maxPrioritizedValidatorNumber);
_setNumberOfBlocksInEpoch(__numberOfBlocksInEpoch);
_setNumberOfEpochsInPeriod(__numberOfEpochsInPeriod);
}
Expand Down Expand Up @@ -303,6 +310,13 @@ contract RoninValidatorSet is
return _maxValidatorNumber;
}

/**
* @inheritdoc IRoninValidatorSet
*/
function maxPrioritizedValidatorNumber() external view override returns (uint256 _maximumPrioritizedValidatorNumber) {
return _maxPrioritizedValidatorNumber;
}

///////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS FOR GOVERNANCE ADMIN //
///////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -328,6 +342,25 @@ contract RoninValidatorSet is
_setNumberOfEpochsInPeriod(__numberOfEpochsInPeriod);
}

/**
* @inheritdoc IRoninValidatorSet
*/
function setPrioritizedValidators(address[] memory _addrs, bool[] memory _prioritizedStatuses)
external
override
onlyAdmin
{
require(_addrs.length == _prioritizedStatuses.length, "RoninValidatorSet: length of two input arrays mismatches");

for (uint _i = 0; _i < _addrs.length; _i++) {
if (_prioritizedRegisterredMap[_addrs[_i]] != _prioritizedStatuses[_i]) {
_prioritizedRegisterredMap[_addrs[_i]] = _prioritizedStatuses[_i];
}
}

emit ValidatorPriorityStatusUpdated(_addrs, _prioritizedStatuses);
}

///////////////////////////////////////////////////////////////////////////////////////
// HELPER FUNCTIONS //
///////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -366,6 +399,7 @@ contract RoninValidatorSet is
function _updateValidatorSet() internal virtual {
address[] memory _candidates = _syncNewValidatorSet();
uint256 _newValidatorCount = Math.min(_maxValidatorNumber, _candidates.length);
address[] memory _arrangedCandidates = _arrangeValidatorCandidates(_candidates, _newValidatorCount);

assembly {
mstore(_candidates, _newValidatorCount)
Expand All @@ -377,7 +411,7 @@ contract RoninValidatorSet is
}

for (uint256 _i = 0; _i < _newValidatorCount; _i++) {
address _newValidator = _candidates[_i];
address _newValidator = _arrangedCandidates[_i];
if (_newValidator == _validator[_i]) {
continue;
}
Expand All @@ -387,7 +421,7 @@ contract RoninValidatorSet is
}

validatorCount = _newValidatorCount;
emit ValidatorSetUpdated(_candidates);
emit ValidatorSetUpdated(_arrangedCandidates);
}

/**
Expand All @@ -411,6 +445,42 @@ contract RoninValidatorSet is
return _validatorMap[_addr];
}

/**
* @dev Arranges the sorted candidates to list of validators, by asserting prioritized and non-prioritized candidates
*
* @param _candidates A sorted list of candidates
*/
function _arrangeValidatorCandidates(address[] memory _candidates, uint _newValidatorCount)
internal
view
returns (address[] memory _arrangedValidators)
{
_arrangedValidators = new address[](_newValidatorCount);

int _prioritySlotLeft = int(_maxPrioritizedValidatorNumber);
uint _prioritySlotCounter;

address[] memory _waitingCandidates = new address[](_candidates.length);
uint _waitingCounter;

for (uint _i = 0; _i < _candidates.length; _i++) {
if (_prioritizedRegisterredMap[_candidates[_i]]) {
if (_prioritySlotLeft-- > 0) {
_arrangedValidators[_prioritySlotCounter++] = _candidates[_i];
continue;
}
}
_waitingCandidates[_waitingCounter++] = _candidates[_i];
}

uint _unfilledSlotLeft = _newValidatorCount - _prioritySlotCounter;
uint _unfilledSlotCounter = _prioritySlotCounter;

for (uint _i = 0; _i < _unfilledSlotLeft; _i++) {
_arrangedValidators[_unfilledSlotCounter++] = _waitingCandidates[_i];
}
}

/**
* @dev Updates the max validator number
*
Expand All @@ -422,6 +492,19 @@ contract RoninValidatorSet is
emit MaxValidatorNumberUpdated(_number);
}

/**
* @dev Updates the number of reserved slots for prioritized validators
*/
function _setPrioritizedReservedSlotNumber(uint256 __maxPrioritizedValidatorNumber) internal {
require(
__maxPrioritizedValidatorNumber <= _maxValidatorNumber,
"RoninValidatorSet: cannot set number of prioritized greater than number of max validators"
);

_maxPrioritizedValidatorNumber = __maxPrioritizedValidatorNumber;
emit MaxPrioritizedValidatorNumberUpdated(__maxPrioritizedValidatorNumber);
}

/**
* @dev Updates the number of blocks in epoch
*
Expand Down
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface RoninValidatorSetConf {
| {
maxValidatorNumber: BigNumberish;
maxValidatorCandidate: BigNumberish;
maxPrioritizedValidatorNumber: BigNumberish;
numberOfBlocksInEpoch: BigNumberish;
numberOfEpochsInPeriod: BigNumberish;
}
Expand Down Expand Up @@ -108,6 +109,7 @@ export const roninValidatorSetConf: RoninValidatorSetConf = {
[Network.Hardhat]: undefined,
[Network.Devnet]: {
maxValidatorNumber: 21,
maxPrioritizedValidatorNumber: 11,
maxValidatorCandidate: 100,
numberOfBlocksInEpoch: 600,
numberOfEpochsInPeriod: 48, // 1 day
Expand Down
1 change: 1 addition & 0 deletions src/deploy/proxy/ronin-validator-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const deploy = async ({ getNamedAccounts, deployments }: HardhatRuntimeEnvironme
initAddress[network.name]!.stakingVestingContract,
roninValidatorSetConf[network.name]!.maxValidatorNumber,
roninValidatorSetConf[network.name]!.maxValidatorCandidate,
roninValidatorSetConf[network.name]!.maxPrioritizedValidatorNumber,
roninValidatorSetConf[network.name]!.numberOfBlocksInEpoch,
roninValidatorSetConf[network.name]!.numberOfEpochsInPeriod,
]);
Expand Down
17 changes: 17 additions & 0 deletions test/helpers/ronin-validator-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,21 @@ export const expects = {
1
);
},

emitValidatorPriorityStatusUpdatedEvent: async function (
tx: ContractTransaction,
expectingAddressList: string[],
expectingPriorityStatusList: boolean[]
) {
await expectEvent(
contractInterface,
'ValidatorPriorityStatusUpdated',
tx,
(event) => {
expect(event.args[0], 'invalid address list').eql(expectingAddressList);
expect(event.args[1], 'invalid priority status list').eql(expectingPriorityStatusList);
},
1
);
},
};
2 changes: 2 additions & 0 deletions test/integration/ActionSlashValidators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const slashFelonyAmount = BigNumber.from(1);
const slashDoubleSignAmount = 1000;

const maxValidatorNumber = 3;
const maxPrioritizedValidatorNumber = 0;
const numberOfBlocksInEpoch = 600;
const numberOfEpochsInPeriod = 48;

Expand Down Expand Up @@ -69,6 +70,7 @@ describe('[Integration] Slash validators', () => {
roninValidatorSetConf[network.name] = {
maxValidatorNumber: maxValidatorNumber,
maxValidatorCandidate: maxValidatorCandidate,
maxPrioritizedValidatorNumber: maxPrioritizedValidatorNumber,
numberOfBlocksInEpoch: numberOfBlocksInEpoch,
numberOfEpochsInPeriod: numberOfEpochsInPeriod,
};
Expand Down
2 changes: 2 additions & 0 deletions test/integration/ActionSubmitReward.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const slashFelonyAmount = BigNumber.from(1);
const slashDoubleSignAmount = 1000;

const maxValidatorNumber = 3;
const maxPrioritizedValidatorNumber = 0;
const numberOfBlocksInEpoch = 600;
const numberOfEpochsInPeriod = 48;

Expand Down Expand Up @@ -69,6 +70,7 @@ describe('[Integration] Submit Block Reward', () => {
roninValidatorSetConf[network.name] = {
maxValidatorNumber: maxValidatorNumber,
maxValidatorCandidate: maxValidatorCandidate,
maxPrioritizedValidatorNumber: maxPrioritizedValidatorNumber,
numberOfBlocksInEpoch: numberOfBlocksInEpoch,
numberOfEpochsInPeriod: numberOfEpochsInPeriod,
};
Expand Down
2 changes: 2 additions & 0 deletions test/integration/ActionWrapUpEpoch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const slashFelonyAmount = BigNumber.from(1);
const slashDoubleSignAmount = 1000;

const maxValidatorNumber = 3;
const maxPrioritizedValidatorNumber = 0;
const numberOfBlocksInEpoch = 600;
const numberOfEpochsInPeriod = 48;

Expand Down Expand Up @@ -71,6 +72,7 @@ describe('[Integration] Wrap up epoch', () => {
roninValidatorSetConf[network.name] = {
maxValidatorNumber: maxValidatorNumber,
maxValidatorCandidate: maxValidatorCandidate,
maxPrioritizedValidatorNumber: maxPrioritizedValidatorNumber,
numberOfBlocksInEpoch: numberOfBlocksInEpoch,
numberOfEpochsInPeriod: numberOfEpochsInPeriod,
};
Expand Down
14 changes: 10 additions & 4 deletions test/integration/Configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,20 @@ let governanceAdmin: SignerWithAddress;
let proxyAdmin: SignerWithAddress;
let validatorCandidates: SignerWithAddress[];

const felonyJailDuration = 28800 * 2;
const misdemeanorThreshold = 10;
const felonyThreshold = 20;
const slashFelonyAmount = BigNumber.from(1);
const slashDoubleSignAmount = 1000;

const maxValidatorNumber = 4;
const minValidatorBalance = BigNumber.from(100);
const maxPrioritizedValidatorNumber = 0;
const numberOfBlocksInEpoch = 600;
const numberOfEpochsInPeriod = 48;
const felonyJailDuration = 28800 * 2;
const misdemeanorThreshold = 10;
const felonyThreshold = 20;

const minValidatorBalance = BigNumber.from(100);
const maxValidatorCandidate = 10;

const bonusPerBlock = BigNumber.from(1);
const topUpAmount = BigNumber.from(10000);

Expand All @@ -60,6 +65,7 @@ describe('[Integration] Configuration check', () => {
roninValidatorSetConf[network.name] = {
maxValidatorNumber: maxValidatorNumber,
maxValidatorCandidate: maxValidatorNumber,
maxPrioritizedValidatorNumber: maxPrioritizedValidatorNumber,
numberOfBlocksInEpoch: numberOfBlocksInEpoch,
numberOfEpochsInPeriod: numberOfEpochsInPeriod,
};
Expand Down
Loading

0 comments on commit 0db9b0f

Please sign in to comment.