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

Time based Connectivity Tracker reports. NatSpec documentation #259

Merged
merged 3 commits into from
Nov 7, 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
181 changes: 168 additions & 13 deletions contracts/ConnectivityTrackerHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,101 @@ import { Unauthorized, ZeroAddress } from "./lib/Errors.sol";
contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnectivityTrackerHbbft {
using EnumerableSet for EnumerableSet.AddressSet;

/**
* @dev The address of the {ValidatorSetHbbft} contract.
*/
IValidatorSetHbbft public validatorSetContract;

/**
* @dev The address of the {StakingHbbft} contract.
*/
IStakingHbbft public stakingContract;

/**
* @dev The address of the {BlockRewardHbbft} contract.
*/
IBlockRewardHbbft public blockRewardContract;

uint256 public minReportAgeBlocks;
/**
* @dev Time since the beginning of the epoch during which reports are not accepted.
* @custom:oz-renamed-from minReportAgeBlocks
*/
uint256 public reportDisallowPeriod;

/**
* @dev Parameter that binds Hbbft Fault tolerance with
*/
uint256 public earlyEpochEndToleranceLevel;

/**
* @dev Early epoch end historical data.
*/
mapping(uint256 => bool) public isEarlyEpochEnd;

/**
* @dev Mapping the epoch number to the list of validators that have disconnected in it.
*/
mapping(uint256 => EnumerableSet.AddressSet) private _flaggedValidators;

/**
* @dev Mapping of reported validators and their reporters by epoch number.
*/
mapping(uint256 => mapping(address => EnumerableSet.AddressSet)) private _reporters;

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

/**
* @dev Indicats wheter validators were penalised for bad performance in specific epoch.
*/
mapping(uint256 => bool) private _epochPenaltiesSent;

/**
* @dev The address of the {BonusScoreSystem} contract.
*/
IBonusScoreSystem public bonusScoreContract;

/**
* @dev Timestamp when the validator was marked as faulty in a specific epoch.
*/
mapping(uint256 => mapping(address => uint256)) private _disconnectTimestamp;

event SetMinReportAgeBlocks(uint256 _minReportAge);
/**
* @dev Emitted by the {setReportDisallowPeriod} function.
* @param _reportDisallowPeriodSeconds New report disallow period value in seconds.
*/
event SetReportDisallowPeriod(uint256 _reportDisallowPeriodSeconds);

/**
* @dev Emitted by the {setEarlyEpochEndToleranceLevel} function.
* @param _level New early epoch end tolerance level.
*/
event SetEarlyEpochEndToleranceLevel(uint256 _level);

/**
* @dev Emitted when `validator` was reported by `reporter` for lost connection at block `blockNumber`.
* @param reporter Reporting validator address.
* @param validator Address of the validator with which the connection was lost.
* @param blockNumber Block number when connection was lost.
*/
event ReportMissingConnectivity(address indexed reporter, address indexed validator, uint256 indexed blockNumber);

/**
* @dev Emitted when `validator` was reported by `reporter` as reconnected at block `blockNumber`.
* @param reporter Reporting validator address.
* @param validator Address of the reconnected validator.
* @param blockNumber Block number when reported validator reconnected.
*/
event ReportReconnect(address indexed reporter, address indexed validator, uint256 indexed blockNumber);

/**
* @dev Emitted to signal that the count of disconnected validators exceeded
* the threshold and current epoch `epoch` will end earlier.
* @param epoch Staking epoch number.
* @param blockNumber Block number in which the decision was made.
*/
event NotifyEarlyEpochEnd(uint256 indexed epoch, uint256 indexed blockNumber);

error AlreadyReported(address reporter, address validator);
Expand All @@ -52,12 +122,19 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
error UnknownReconnectReporter(address reporter, address validator);
error EpochPenaltiesAlreadySent(uint256 epoch);

/// @custom:oz-upgrades-unsafe-allow constructor
/**
* @custom:oz-upgrades-unsafe-allow constructor
*/
constructor() {
// Prevents initialization of implementation contract
_disableInitializers();
}

/**
* @dev Check that the caller is {BlockRewardHbbft} contract.
*
* Reverts with an {Unauthorized} error.
*/
modifier onlyBlockRewardContract() {
if (msg.sender != address(blockRewardContract)) {
revert Unauthorized();
Expand All @@ -71,7 +148,7 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
address _stakingContract,
address _blockRewardContract,
address _bonusScoreContract,
uint256 _minReportAgeBlocks
uint256 _reportDisallowPeriodSeconds
) external initializer {
if (
_contractOwner == address(0) ||
Expand All @@ -90,16 +167,30 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
blockRewardContract = IBlockRewardHbbft(_blockRewardContract);
bonusScoreContract = IBonusScoreSystem(_bonusScoreContract);

minReportAgeBlocks = _minReportAgeBlocks;
reportDisallowPeriod = _reportDisallowPeriodSeconds;
earlyEpochEndToleranceLevel = 2;
}

function setMinReportAge(uint256 _minReportAge) external onlyOwner {
minReportAgeBlocks = _minReportAge;

emit SetMinReportAgeBlocks(_minReportAge);
/**
* @dev This function sets the period of time during which reports are not accepted.
* Can only be called by contract owner.
* @param _reportDisallowPeriodSeconds Time period in seconds.
*
* Emits a {SetReportDisallowPeriod} event.
*/
function setReportDisallowPeriod(uint256 _reportDisallowPeriodSeconds) external onlyOwner {
reportDisallowPeriod = _reportDisallowPeriodSeconds;

emit SetReportDisallowPeriod(_reportDisallowPeriodSeconds);
}

/**
* @dev This function sets the early epoch end tolerance level.
* Can only be called by contract owner.
* @param _level New early epoch end tolerance level.
*
* Emits a {SetEarlyEpochEndToleranceLevel} event.
*/
function setEarlyEpochEndToleranceLevel(uint256 _level) external onlyOwner {
earlyEpochEndToleranceLevel = _level;

Expand All @@ -108,6 +199,16 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
emit SetEarlyEpochEndToleranceLevel(_level);
}

/**
* @dev Report that the connection to the specified validator was lost at block `blockNumber`.
* Callable only by active validators.
*
* @param validator Validator address with which the connection was lost.
* @param blockNumber Block number where the connection was lost.
* @param blockHash Hash of this block.
*
* Emits a {ReportMissingConnectivity} event.
*/
function reportMissingConnectivity(address validator, uint256 blockNumber, bytes32 blockHash) external {
checkReportMissingConnectivityCallable(msg.sender, validator, blockNumber, blockHash);

Expand All @@ -130,6 +231,16 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
emit ReportMissingConnectivity(msg.sender, validator, blockNumber);
}

/**
* @dev Report that the connection to the specified validator was restored at block `blockNumber`.
* Callable only by active validators.
*
* @param validator Validator address with which the connection was restored.
* @param blockNumber Block number where the connection was restored.
* @param blockHash Hash of this block.
*
* Emits a {ReportReconnect} event.
*/
function reportReconnect(address validator, uint256 blockNumber, bytes32 blockHash) external {
checkReportReconnectCallable(msg.sender, validator, blockNumber, blockHash);

Expand Down Expand Up @@ -160,6 +271,15 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
emit ReportReconnect(msg.sender, validator, blockNumber);
}

/**
* @dev Send bad performance bonus score penalties to validators
* that have not yet reconnected at the end of the epoch.
* Can only be called by {BlockRewardHbbft} contract.
*
* @param epoch Staking epoch number.
*
* Reverts with {EpochPenaltiesAlreadySent} if penalties for specified `epoch` already sent.
*/
function penaliseFaultyValidators(uint256 epoch) external onlyBlockRewardContract {
if (_epochPenaltiesSent[epoch]) {
revert EpochPenaltiesAlreadySent(epoch);
Expand All @@ -178,7 +298,12 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
}
}

/// @dev Returns true if the specified validator was reported by the specified reporter at the given epoch.
/**
* @dev Returns true if the validator `validator` was reported
* by the specified `reporter`at the current epoch.
* @param validator Valdiator address.
* @param reporter Reporting validator address.
*/
function isReported(uint256, address validator, address reporter) external view returns (bool) {
return _reporters[currentEpoch()][validator].contains(reporter);
}
Expand All @@ -187,6 +312,12 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
return _reporters[epoch][validator].length();
}

/**
* @dev Returns true if the validator `validator` was marked as faulty
* (majority of other validators reported missing connectivity) in the specified `epoch`.
* @param epoch Staking epoch number.
* @param validator Validator address
*/
function isFaultyValidator(uint256 epoch, address validator) public view returns (bool) {
return getValidatorConnectivityScore(epoch, validator) >= _getReportersThreshold(epoch);
}
Expand Down Expand Up @@ -222,22 +353,42 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
}
}

/**
* @dev Get list of validators flagged for missing connectivity in the specified `epoch`.
* @param epoch Staking epoch number.
*/
function getFlaggedValidatorsByEpoch(uint256 epoch) public view returns (address[] memory) {
return _flaggedValidators[epoch].values();
}

/**
* @dev Get list of validators flagged for missing connectivity in the current epoch.
* See {getFlaggedValidatorsByEpoch}.
*/
function getFlaggedValidators() public view returns (address[] memory) {
return getFlaggedValidatorsByEpoch(currentEpoch());
}

/**
* @dev Get list of validators flagged for missing connectivity in the specified staking epoch `epoch`.
* @param epoch Staking epoch number.
*/
function getFlaggedValidatorsCount(uint256 epoch) public view returns (uint256) {
return _flaggedValidators[epoch].length();
}

/**
* @dev Get current staking epoch number.
* See {StakingHbbft-stakingEpoch}
*/
function currentEpoch() public view returns (uint256) {
return IStakingHbbft(stakingContract).stakingEpoch();
}

/**
* @dev Returns the number of validators that, if exceeded,
* will trigger an early end of the current staking epoch.
*/
function earlyEpochEndThreshold() public view returns (uint256) {
uint256 networkSize = IValidatorSetHbbft(validatorSetContract).getCurrentValidatorsCount();
uint256 hbbftFaultTolerance = networkSize / 3;
Expand All @@ -249,6 +400,10 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
}
}

/**
* @dev Returns faulty validators count in given epoch `epoch`.
* @param epoch Staking epoch number.
*/
function countFaultyValidators(uint256 epoch) public view returns (uint256) {
return _countFaultyValidators(epoch);
}
Expand Down Expand Up @@ -321,8 +476,8 @@ contract ConnectivityTrackerHbbft is Initializable, OwnableUpgradeable, IConnect
revert CannotReportByFlaggedValidator(caller);
}

uint256 epochStartBlock = stakingContract.stakingEpochStartBlock();
if (block.number < epochStartBlock + minReportAgeBlocks) {
uint256 epochStartTimestamp = stakingContract.stakingEpochStartTime();
if (block.timestamp < epochStartTimestamp + reportDisallowPeriod) {
revert ReportTooEarly();
}
}
Expand Down
6 changes: 3 additions & 3 deletions tasks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class NetworkConfiguration {
public minimumBlockTime?: number;
public maximumBlockTime?: number;
public validatorInactivityThreshold?: number;
public minReportAgeBlocks?: number;
public reportDisallowPeriod?: number;

static create(fileName: string): NetworkConfiguration {
const instance = new NetworkConfiguration();
Expand All @@ -59,7 +59,7 @@ export class NetworkConfiguration {

instance.minimumBlockTime = Number.parseInt(process.env.MINIMUM_BLOCK_TIME ? process.env.MINIMUM_BLOCK_TIME : "0");
instance.maximumBlockTime = Number.parseInt(process.env.MAXIMUM_BLOCK_TIME ? process.env.MAXIMUM_BLOCK_TIME : "0");
instance.minReportAgeBlocks = Number.parseInt(process.env.MIN_REPORT_AGE_BLOCKS ? process.env.MIN_REPORT_AGE_BLOCKS : "10");
instance.reportDisallowPeriod = Number.parseInt(process.env.REPORT_DISALLOW_PERIOD ? process.env.REPORT_DISALLOW_PERIOD : "10");

instance.networkName = process.env.NETWORK_NAME;
instance.networkId = process.env.NETWORK_ID;
Expand Down Expand Up @@ -379,7 +379,7 @@ export class InitialContractsConfiguration {
this.getAddress('StakingHbbft'),
this.getAddress('BlockRewardHbbft'),
this.getAddress('BonusScoreSystem'),
config.minReportAgeBlocks
config.reportDisallowPeriod
];
case 'BonusScoreSystem':
return [
Expand Down
Loading
Loading