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

Dev #6

Merged
merged 5 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 19 additions & 6 deletions contracts/BlockRewardHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ contract BlockRewardHbbft is
for (uint256 i = 0; i < governancePotShareNominatorParams.length; i++) {
governancePotShareNominatorParams[i] = 10 + i;
}

initAllowedChangeableParameter(
"setGovernancePotShareNominator(uint256)",
"governancePotShareNominatorParams()",
"governancePotShareNominator()",
governancePotShareNominatorParams
);
}
Expand Down Expand Up @@ -252,11 +252,9 @@ contract BlockRewardHbbft is
IStakingHbbft stakingContract = IStakingHbbft(validatorSetContract.getStakingContract());

// If this is the last block of the epoch i.e. master key has been generated.
if (_isEpochEndBlock || earlyEpochEnd) {
if (_isEpochEndBlock) {
rewardsNative = _closeEpoch(stakingContract);

earlyEpochEnd = false;

emit CoinsRewarded(rewardsNative);
} else {
_closeBlock(stakingContract);
Expand Down Expand Up @@ -451,9 +449,15 @@ contract BlockRewardHbbft is
}
}

if ((isPhaseTransition || toBeUpscaled) && validatorSetContract.getPendingValidators().length == 0) {
bool validatorSetChangeTrigger = isPhaseTransition || toBeUpscaled || earlyEpochEnd;

if (validatorSetChangeTrigger && validatorSetContract.getPendingValidators().length == 0) {
// Choose new validators
validatorSetContract.newValidatorSet();

// in any case we reset early epoch end flag because
// the validator set change already in progress
earlyEpochEnd = false;
} else if (block.timestamp >= stakingContract.stakingFixedEpochEndTime()) {
validatorSetContract.handleFailedKeyGeneration();
}
Expand Down Expand Up @@ -482,6 +486,8 @@ contract BlockRewardHbbft is
// Indicates whether the validator is entitled to share the rewartds or not.
bool[] memory isRewardedValidator = new bool[](validators.length);

uint256 currentEpochStartTime = stakingContract.stakingEpochStartTime();

// Number of validators that are being rewarded.
uint256 numRewardedValidators = 0;

Expand All @@ -495,6 +501,13 @@ contract BlockRewardHbbft is
continue;
}

// validator who is unavailable or set himself available (because it was unavailable)
// in current epoch, should not get rewards for it.
uint256 availableSince = validatorSetContract.validatorAvailableSince(validators[i]);
if (availableSince == 0 || availableSince >= currentEpochStartTime) {
continue;
}

isRewardedValidator[i] = true;
++numRewardedValidators;
}
Expand Down
31 changes: 23 additions & 8 deletions contracts/ConnectivityTrackerHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
mapping(uint256 => EnumerableSet.AddressSet) private _flaggedValidators;
mapping(uint256 => mapping(address => EnumerableSet.AddressSet)) private _reporters;

mapping(address => uint256) private _disconnectTimestamp;
/// @custom:oz-renamed-from _disconnectTimestamp
mapping(address => uint256) private _unused;
mapping(uint256 => bool) private _epochPenaltiesSent;

IBonusScoreSystem public bonusScoreContract;

mapping(uint256 => mapping(address => uint256)) private _disconnectTimestamp;

event SetMinReportAgeBlocks(uint256 _minReportAge);
event SetEarlyEpochEndToleranceLevel(uint256 _level);
event ReportMissingConnectivity(address indexed reporter, address indexed validator, uint256 indexed blockNumber);
Expand Down Expand Up @@ -118,8 +121,8 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
// slither-disable-next-line unused-return
_reporters[epoch][validator].add(msg.sender);

if (isFaultyValidator(validator, epoch)) {
_disconnectTimestamp[validator] = block.timestamp;
if (isFaultyValidator(epoch, validator)) {
_markValidatorFaulty(epoch, validator);
}

_decideEarlyEpochEndNeeded(epoch);
Expand All @@ -142,12 +145,13 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect

// All reporters confirmed that this validator reconnected,
// decrease validator bonus score for bad performance based on disconnect time interval.
if (_disconnectTimestamp[validator] != 0) {
uint256 disconnectPeriod = block.timestamp - _disconnectTimestamp[validator];
uint256 disconnectTimestamp = _disconnectTimestamp[epoch][validator];
if (disconnectTimestamp != 0) {
uint256 disconnectPeriod = block.timestamp - disconnectTimestamp;

bonusScoreContract.penaliseBadPerformance(validator, disconnectPeriod);

delete _disconnectTimestamp[validator];
delete _disconnectTimestamp[epoch][validator];
}
}

Expand All @@ -166,7 +170,7 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
address[] memory flaggedValidators = getFlaggedValidatorsByEpoch(epoch);

for (uint256 i = 0; i < flaggedValidators.length; ++i) {
if (!isFaultyValidator(flaggedValidators[i], epoch)) {
if (!isFaultyValidator(epoch, flaggedValidators[i])) {
continue;
}

Expand All @@ -183,7 +187,7 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
return _reporters[epoch][validator].length();
}

function isFaultyValidator(address validator, uint256 epoch) public view returns (bool) {
function isFaultyValidator(uint256 epoch, address validator) public view returns (bool) {
return getValidatorConnectivityScore(epoch, validator) >= _getReportersThreshold(epoch);
}

Expand Down Expand Up @@ -269,6 +273,17 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
emit NotifyEarlyEpochEnd(epoch, block.number);
}

function _markValidatorFaulty(uint256 epoch, address validator) private {
if (_disconnectTimestamp[epoch][validator] != 0) {
// validator already marked as faulty
return;
}

_disconnectTimestamp[epoch][validator] = block.timestamp;

validatorSetContract.notifyUnavailability(validator);
}

function _getReportersThreshold(uint256 epoch) private view returns (uint256) {
uint256 unflaggedValidatorsCount = validatorSetContract.getCurrentValidatorsCount() -
getFlaggedValidatorsCount(epoch);
Expand Down
40 changes: 29 additions & 11 deletions contracts/ValidatorSetHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,21 @@

IBonusScoreSystem public bonusScoreSystem;

address public connectivityTracker;

// ================================================ Events ========================================================

event ValidatorAvailable(address validator, uint256 timestamp);

/// @dev Emitted by the `handleFailedKeyGeneration` function to signal that a specific validator was
/// marked as unavailable since he dit not contribute to the required key shares.
/// @dev Emitted by the `handleFailedKeyGeneration` and `notifyUnavailability` functions to signal that a specific
/// validator was marked as unavailable since he dit not contribute to the required key shares or 2/3 of other
/// validators reporter him as disconnected.
event ValidatorUnavailable(address validator, uint256 timestamp);

event SetMaxValidators(uint256 _count);
event SetValidatorInactivityThreshold(uint256 _value);
event SetBonusScoreContract(address _address);
event SetConnectivityTrackerContract(address _address);

error AnnounceBlockNumberTooOld();
error CantAnnounceAvailability();
Expand All @@ -132,17 +136,16 @@
_;
}

/// @dev Ensures the caller is the RandomHbbft contract address.
modifier onlyRandomContract() {
if (msg.sender != randomContract) {
/// @dev Ensures the caller is the StakingHbbft contract address.
modifier onlyStakingContract() {
if (msg.sender != address(stakingContract)) {
revert Unauthorized();
}
_;
}

/// @dev Ensures the caller is the StakingHbbft contract address.
modifier onlyStakingContract() {
if (msg.sender != address(stakingContract)) {
modifier onlyConnectivityTracker() {
if (msg.sender != connectivityTracker) {
revert Unauthorized();
}
_;
Expand Down Expand Up @@ -202,6 +205,7 @@
stakingContract = IStakingHbbft(_params.stakingContract);
keyGenHistoryContract = IKeyGenHistory(_params.keyGenHistoryContract);
bonusScoreSystem = IBonusScoreSystem(_params.bonusScoreContract);
connectivityTracker = _params.connectivityTrackerContract;
validatorInactivityThreshold = _params.validatorInactivityThreshold;

// Add initial validators to the `_currentValidators` array
Expand Down Expand Up @@ -271,6 +275,16 @@
emit SetBonusScoreContract(_address);
}

function setConnectivityTracker(address _address) external onlyOwner {
if (_address == address(0)) {
revert ZeroAddress();
}

connectivityTracker = _address;

emit SetConnectivityTrackerContract(_address);
}

/// @dev called by validators when a validator comes online after
/// getting marked as unavailable caused by a failed key generation.
function announceAvailability(uint256 _blockNumber, bytes32 _blockhash) external {
Expand Down Expand Up @@ -431,13 +445,17 @@
/// @dev Notifies hbbft validator set contract that a validator
/// asociated with the given `_stakingAddress` became
/// unavailable and must be flagged as unavailable.
/// @param _stakingAddress The address of the validator which became unavailable.
function notifyUnavailability(address _stakingAddress) external onlyStakingContract {
_writeValidatorAvailableSince(miningByStakingAddress[_stakingAddress], 0);
/// @param _miningAddress The address of the validator which became unavailable.
function notifyUnavailability(address _miningAddress) external onlyConnectivityTracker {
stakingContract.removePool(stakingByMiningAddress[_miningAddress]);

_writeValidatorAvailableSince(_miningAddress, 0);

emit ValidatorUnavailable(_miningAddress, block.timestamp);
}

/// @dev Binds a mining address to the specified staking address. Called by the `StakingHbbft.addPool` function
/// when a user wants to become a candidate and creates a pool.

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

/// See also the `miningByStakingAddress` and `stakingByMiningAddress` public mappings.
/// @param _miningAddress The mining address of the newly created pool. Cannot be equal to the `_stakingAddress`
/// and should never be used as a pool before.
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IValidatorSetHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IValidatorSetHbbft {
address stakingContract;
address keyGenHistoryContract;
address bonusScoreContract;
address connectivityTrackerContract;
uint256 validatorInactivityThreshold;
}

Expand Down
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "@typechain/hardhat";
import "hardhat-gas-reporter";
import "hardhat-contract-sizer";
import 'solidity-docgen';
import 'hardhat-tracer';

import './tasks/make_spec';

Expand Down
Loading
Loading