Summary
The LLMOracleRegistry lacks a slashing mechanism for malicious or consistently incorrect validators, allowing them to retain their staked tokens despite malicious behavior or poor performance.
Vulnerability Details
https://github.com/Cyfrin/2024-10-swan-dria/blob/main/contracts/llm/LLMOracleRegistry.sol
https://github.com/Cyfrin/2024-10-swan-dria/blob/main/contracts/llm/LLMOracleCoordinator.sol
Current staking mechanism in LLMOracleRegistry:
contract LLMOracleRegistry {
mapping(address oracle => mapping(LLMOracleKind => uint256 amount)) public registrations;
function register(LLMOracleKind kind) public {
uint256 amount = getStakeAmount(kind);
if (isRegistered(msg.sender, kind)) {
revert AlreadyRegistered(msg.sender);
}
token.transferFrom(msg.sender, address(this), amount);
registrations[msg.sender][kind] = amount;
}
function unregister(LLMOracleKind kind) public returns (uint256 amount) {
amount = registrations[msg.sender][kind];
if (amount == 0) {
revert NotRegistered(msg.sender);
}
delete registrations[msg.sender][kind];
token.approve(msg.sender, token.allowance(address(this), msg.sender) + amount);
}
}
In LLMOracleCoordinator, validation scoring:
function finalizeValidation(uint256 taskId) private {
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);
}
}
}
Issues with current system:
-
No Penalty for Bad Actors:
Validators can provide incorrect validations with no consequence
Can unregister and withdraw full stake even after malicious behavior
No history tracking of validator performance
-
Economic Attack Vectors:
Validators can collude to manipulate scores
Cost of attack is only gas fees, stake is fully recoverable
No economic deterrent against manipulation
-
Missing Performance Tracking:
No mechanism to track validator accuracy over time
No cumulative scoring system
No way to identify consistently poor performers
Impact
It has several impacts on the protocol -
-
Economic Security:
No financial deterrent against malicious behavior
Protocol relies solely on stake locking without slashing
Cost of attack is minimal (only gas fees)
-
Data Quality:
No incentive for high-quality validations
Potential for sustained incorrect validations
Risk of coordinated validation manipulation
-
Oracle Reliability:
Reduced trust in oracle system
No mechanism to remove bad actors
Potential for validation market manipulation
Tools Used
Manual Review
Recommendations
Here are few recommendations -
Implement Slashing Mechanism:
contract LLMOracleRegistry {
struct ValidatorInfo {
uint256 stake;
uint256 slashableAmount;
uint256 lastSlashTime;
uint256 incorrectValidations;
}
mapping(address => ValidatorInfo) public validatorInfo;
function slash(address validator, uint256 amount) external onlyCoordinator {
ValidatorInfo storage info = validatorInfo[validator];
require(info.stake >= amount, "Insufficient stake");
info.stake -= amount;
info.slashableAmount += amount;
info.lastSlashTime = block.timestamp;
info.incorrectValidations++;
if (info.incorrectValidations > MAX_INCORRECT_VALIDATIONS) {
_forceUnregister(validator);
}
}
}
contract LLMOracleRegistry {
uint256 public constant COOLDOWN_PERIOD = 21 days;
function unregister(LLMOracleKind kind) public returns (uint256 amount) {
ValidatorInfo storage info = validatorInfo[msg.sender];
require(block.timestamp >= info.lastSlashTime + COOLDOWN_PERIOD, "Cooldown period active");
}
}