Skip to content

Commit

Permalink
Use Unit.decimal() and uint64
Browse files Browse the repository at this point in the history
  • Loading branch information
thaarok committed Nov 8, 2024
1 parent d215bbb commit 637f7f7
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 38 deletions.
18 changes: 9 additions & 9 deletions contracts/sfc/ConstantsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ contract ConstantsManager is Ownable {
uint256 public targetGasPowerPerSecond;
uint256 public gasPriceBalancingCounterweight;

// Epoch threshold for stop counting alive epochs (avoid diminishing impact of new uptimes).
// The number of epochs to calculate the average uptime ratio from, acceptable bound [10, 87600].
// Is also the minimum number of epochs necessary for deactivation of offline validators.
uint32 public averageUptimeEpochsThreshold;
uint32 public averageUptimeEpochWindow;

// Minimum average uptime in Q1.30 format; acceptable bounds [0,0.9]
// Minimum average uptime ratio in fixed-point format; acceptable bounds [0,0.9].
// Zero to disable validators deactivation by this metric.
uint32 public minAverageUptime;
uint64 public minAverageUptime;

/**
* @dev Given value is too small
Expand Down Expand Up @@ -162,19 +162,19 @@ contract ConstantsManager is Ownable {
gasPriceBalancingCounterweight = v;
}

function updateAverageUptimeEpochsThreshold(uint32 v) external virtual onlyOwner {
function updateAverageUptimeEpochWindow(uint32 v) external virtual onlyOwner {
if (v < 10) {
// needs to be long enough to allow permissible downtime for validators maintenance
revert ValueTooSmall();
}
if (v > 87600) {
revert ValueTooLarge();
}
averageUptimeEpochsThreshold = v;
averageUptimeEpochWindow = v;
}

function updateMinAverageUptime(uint32 v) external virtual onlyOwner {
if (v > 966367641) {
// 0.9 in Q1.30
function updateMinAverageUptime(uint64 v) external virtual onlyOwner {
if (v > ((Decimal.unit() * 9) / 10)) {
revert ValueTooLarge();
}
minAverageUptime = v;
Expand Down
2 changes: 1 addition & 1 deletion contracts/sfc/NetworkInitializer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract NetworkInitializer {
consts.updateOfflinePenaltyThresholdBlocksNum(1000);
consts.updateTargetGasPowerPerSecond(2000000);
consts.updateGasPriceBalancingCounterweight(3600);
consts.updateAverageUptimeEpochsThreshold(100);
consts.updateAverageUptimeEpochWindow(100);
consts.updateMinAverageUptime(0); // check disabled by default
consts.transferOwnership(_owner);

Expand Down
45 changes: 17 additions & 28 deletions contracts/sfc/SFC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@ contract SFC is Initializable, Ownable, Version {

// data structure to compute average uptime for each active validator
struct AverageUptime {
// average uptime ratio as a value between 0 and (1 << 30)
uint32 averageUptime;
// remainder from the division in the average calculation
uint32 remainder;
// average uptime ratio as a value between 0 and 1e18
uint64 averageUptime;
// number of epochs in the average (at most averageUptimeEpochsWindow)
uint32 epochs;
}
Expand Down Expand Up @@ -535,7 +533,7 @@ contract SFC is Initializable, Ownable, Version {
}

/// Get average uptime for a validator in a given epoch.
function getEpochAverageUptime(uint256 epoch, uint256 validatorID) public view returns (uint32) {
function getEpochAverageUptime(uint256 epoch, uint256 validatorID) public view returns (uint64) {
return getEpochSnapshot[epoch].averageUptime[validatorID].averageUptime;
}

Expand Down Expand Up @@ -930,53 +928,44 @@ contract SFC is Initializable, Ownable, Version {
) internal {
for (uint256 i = 0; i < validatorIDs.length; i++) {
uint256 validatorID = validatorIDs[i];
// compute normalised uptime as a percentage in the Q1.30 fixed-point format
uint256 normalisedUptime = (uptimes[i] * (1 << 30)) / epochDuration;
if (normalisedUptime > 1 << 30) {
normalisedUptime = 1 << 30;
// compute normalised uptime as a percentage in the fixed-point format
uint256 normalisedUptime = (uptimes[i] * Decimal.unit()) / epochDuration;
if (normalisedUptime > Decimal.unit()) {
normalisedUptime = Decimal.unit();
}
AverageUptime memory previous = prevSnapshot.averageUptime[validatorID];
AverageUptime memory current = _addElementIntoAverageUptime(uint32(normalisedUptime), previous);
AverageUptime memory current = _addElementIntoAverageUptime(uint64(normalisedUptime), previous);
snapshot.averageUptime[validatorID] = current;

// remove validator if average uptime drops below min average uptime
// (by setting minAverageUptime to zero, this check is ignored)
if (current.averageUptime < c.minAverageUptime() && current.epochs >= c.averageUptimeEpochsThreshold()) {
if (current.averageUptime < c.minAverageUptime() && current.epochs >= c.averageUptimeEpochWindow()) {
_setValidatorDeactivated(validatorID, OFFLINE_AVG_BIT);
_syncValidator(validatorID, false);
}
}
}

function _addElementIntoAverageUptime(
uint32 newValue,
uint64 newValue,
AverageUptime memory prev
) private view returns (AverageUptime memory) {
AverageUptime memory cur;
if (prev.epochs == 0) {
// the only element for the average
cur.averageUptime = uint32(newValue);
cur.averageUptime = newValue; // the only element for the average
cur.epochs = 1;
return cur;
}

// the number of elements to calculate the average from
uint64 n = prev.epochs + 1;
// the number of elements the average is calculated from
uint128 n = prev.epochs + 1;
// use lemma to add new value into the average
uint64 tmp = (n - 1) * uint64(prev.averageUptime) + uint64(newValue);
cur.averageUptime = uint64(((n - 1) * uint128(prev.averageUptime) + uint128(newValue)) / n);

// apply remainder from the division in the previous iteration to reduce error
if (prev.remainder != 0) {
tmp += (n * prev.remainder) / (n - 1);
if (cur.averageUptime > Decimal.unit()) {
cur.averageUptime = uint64(Decimal.unit());
}

cur.averageUptime = uint32(tmp / n);
cur.remainder = uint32(tmp % n);

if (cur.averageUptime > 1 << 30) {
cur.averageUptime = 1 << 30;
}
if (prev.epochs < c.averageUptimeEpochsThreshold()) {
if (prev.epochs < c.averageUptimeEpochWindow()) {
cur.epochs = prev.epochs + 1;
} else {
cur.epochs = prev.epochs;
Expand Down

0 comments on commit 637f7f7

Please sign in to comment.