As stated in the NatSpec, the function should revert if the number of validations for any task is zero; however, this implementation is currently missing.
The NatSpec state that the function should revert if the task has no validations. but the function do not implement any require or revert statement to keep this in check.
@>
function finalizeValidation(uint256 taskId) private {
TaskRequest storage task = requests[taskId];
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
uint256[] memory scores = new uint256[]();
for (uint256 v_i = 0; v_i < task.parameters.numValidations; v_i++) {
scores[v_i] = validations[taskId][v_i].scores[g_i];
}
(uint256 _stddev, uint256 _mean) = Statistics.stddev(scores);
uint256 innerSum = 0;
uint256 innerCount = 0;
for (uint256 v_i = 0; v_i < task.parameters.numValidations; ++v_i) {
uint256 score = scores[v_i];
if ((score >= _mean - _stddev) && (score <= _mean + _stddev)) {
innerSum += score;
innerCount++;
_increaseAllowance(validations[taskId][v_i].validator, task.validatorFee);
}
}
uint256 inner_score = innerCount == 0 ? 0 : innerSum / innerCount;
responses[taskId][g_i].score = inner_score;
}
uint256[] memory generationScores = new uint256[]();
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
generationScores[g_i] = responses[taskId][g_i].score;
}
(uint256 stddev, uint256 mean) = Statistics.stddev(generationScores);
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
if (generationScores[g_i] >= mean - generationDeviationFactor * stddev) {
_increaseAllowance(responses[taskId][g_i].responder, task.generatorFee);
}
}
}
The Tasks with zero validations will enter the function and cause miss calculation in the score and further damage the overall calculation .
manual review.
/// @notice Compute the validation scores for a given task.
@> /// @dev Reverts if the task has no validations.
/// @param taskId The ID of the task to compute scores for.
function finalizeValidation(uint256 taskId) private {
+ require(validations[taskId].length !=0, "invalid task validations");
TaskRequest storage task = requests[taskId];
// compute score for each generation
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
// get the scores for this generation, i.e. the g_i-th element of each validation
uint256[] memory scores = new uint256[]();
for (uint256 v_i = 0; v_i < task.parameters.numValidations; v_i++) {
scores[v_i] = validations[taskId][v_i].scores[g_i];
}
// compute the mean and standard deviation
(uint256 _stddev, uint256 _mean) = Statistics.stddev(scores);
uint256 innerSum = 0;
uint256 innerCount = 0;
for (uint256 v_i = 0; v_i < task.parameters.numValidations; ++v_i) {
uint256 score = scores[v_i];
if ((score >= _mean - _stddev) && (score <= _mean + _stddev)) {
innerSum += score;
innerCount++;
// send validation fee to the validator
_increaseAllowance(validations[taskId][v_i].validator, task.validatorFee);
}
}
// set score for this generation as the average of inner scores
uint256 inner_score = innerCount == 0 ? 0 : innerSum / innerCount;
responses[taskId][g_i].score = inner_score;
}
// now, we have the scores for each generation
// compute stddev for these and pick the ones above a threshold
uint256[] memory generationScores = new uint256[]();
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
generationScores[g_i] = responses[taskId][g_i].score;
}
// compute the mean and standard deviation
(uint256 stddev, uint256 mean) = Statistics.stddev(generationScores);
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
// ignore lower outliers
if (generationScores[g_i] >= mean - generationDeviationFactor * stddev) {
_increaseAllowance(responses[taskId][g_i].responder, task.generatorFee);
}
}
}