Skip to content

Commit 85b83df

Browse files
authored
fix/blacklist-low-stake (#33)
* make sure provider is blacklisted after work is invalidated if they lack sufficient stake
1 parent 0a343de commit 85b83df

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

src/PrimeNetwork.sol

+14-5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ contract PrimeNetwork is AccessControlEnumerable {
9292
return domainId;
9393
}
9494

95+
function updateDomainValidationLogic(uint256 domainId, address validationLogic) external onlyRole(FEDERATOR_ROLE) {
96+
domainRegistry.updateValidationLogic(domainId, validationLogic);
97+
}
98+
9599
function registerProvider(uint256 stake) external {
96100
uint256 stakeMinimum = stakeManager.getStakeMinimum();
97101
require(stake >= stakeMinimum, "Stake amount is below minimum");
@@ -168,19 +172,24 @@ contract PrimeNetwork is AccessControlEnumerable {
168172
emit ComputeNodeRemoved(provider, nodekey);
169173
}
170174

171-
function slash(address provider, uint256 amount, bytes calldata reason) external onlyRole(VALIDATOR_ROLE) {
172-
uint256 slashed = stakeManager.slash(provider, amount, reason);
173-
AIToken.transfer(msg.sender, slashed);
175+
function _blacklistIfStakeTooLow(address provider) internal {
174176
if (stakeManager.getStake(provider) < calculateMinimumStake(provider, 0)) {
175177
computeRegistry.setWhitelistStatus(provider, false);
176178
emit ProviderBlacklisted(provider);
177179
}
178180
}
179181

182+
function slash(address provider, uint256 amount, bytes calldata reason) external onlyRole(VALIDATOR_ROLE) {
183+
uint256 slashed = stakeManager.slash(provider, amount, reason);
184+
AIToken.transfer(msg.sender, slashed);
185+
_blacklistIfStakeTooLow(provider);
186+
}
187+
180188
function invalidateWork(uint256 poolId, uint256 penalty, bytes calldata data) external onlyRole(VALIDATOR_ROLE) {
181189
(address provider, address node) = computePool.invalidateWork(poolId, data);
182-
try stakeManager.slash(provider, penalty, data) {}
183-
catch {
190+
try stakeManager.slash(provider, penalty, data) {
191+
_blacklistIfStakeTooLow(provider);
192+
} catch {
184193
// if slashing failed for whatever reason, blacklist provider to make sure they can't submit more work
185194
computeRegistry.setWhitelistStatus(provider, false);
186195
emit ProviderBlacklisted(provider);

test/PrimeNetwork.t.sol

+57
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {RewardsDistributorFactory} from "../src/RewardsDistributorFactory.sol";
1616
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
1717
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
1818
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
19+
import {SyntheticDataWorkValidator} from "../src/SyntheticDataWorkValidator.sol";
1920

2021
contract PrimeNetworkTest is Test {
2122
using ECDSA for bytes32;
@@ -189,6 +190,14 @@ contract PrimeNetworkTest is Test {
189190
return primeNetwork.createDomain(name, IWorkValidation(address(0)), uri);
190191
}
191192

193+
function newDomainWithValidation(string memory name, string memory uri, address workValidator)
194+
public
195+
returns (uint256)
196+
{
197+
vm.startPrank(federator);
198+
return primeNetwork.createDomain(name, IWorkValidation(workValidator), uri);
199+
}
200+
192201
function newPool(uint256 domainId, string memory name, string memory uri) public returns (uint256) {
193202
vm.startPrank(pool_creator);
194203
return computePool.createComputePool(domainId, computeManager, name, uri, 0);
@@ -729,4 +738,52 @@ contract PrimeNetworkTest is Test {
729738
withdrawStake(provider_good1);
730739
assertEq(AI.balanceOf(provider_good1), startingBalance);
731740
}
741+
742+
function test_stakeBlacklisting() public {
743+
uint256 domain = newDomain("Decentralized Training", "https://primeintellect.ai/training/params");
744+
uint256 pool = newPool(domain, "INTELLECT-1", "https://primeintellect.ai/pools/intellect-1");
745+
746+
SyntheticDataWorkValidator workValidator = new SyntheticDataWorkValidator(domain, address(computePool), 1 days);
747+
748+
vm.startPrank(federator);
749+
primeNetwork.updateDomainValidationLogic(domain, address(workValidator));
750+
751+
uint256 minStakeNow = stakeManager.getStakeMinimum();
752+
uint256 nodeComputeUnits = 1000;
753+
754+
bytes memory work_id = hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
755+
756+
startPool(pool);
757+
758+
addProviderWithStake(provider_good1, minStakeNow);
759+
whitelistProvider(provider_good1);
760+
761+
// add stake with some extra
762+
increaseStake(provider_good1, nodeComputeUnits * 2 * minStakeNow);
763+
addNodeWithCompute(provider_good1, node_good1, node_good1_sk, nodeComputeUnits);
764+
765+
// ensure that the stake is locked by the compute units
766+
uint256 computeLockedStake = primeNetwork.calculateMinimumStake(provider_good1, 0);
767+
assertEq(computeLockedStake, (nodeComputeUnits * minStakeNow) + minStakeNow);
768+
769+
validateNode(provider_good1, node_good1);
770+
nodeJoin(domain, pool, provider_good1, node_good1);
771+
772+
computePool.submitWork(pool, node_good1, work_id);
773+
774+
// slash provider
775+
uint256 stake = stakeManager.getStake(provider_good1);
776+
vm.startPrank(validator);
777+
primeNetwork.invalidateWork(pool, stake, work_id);
778+
779+
// check that stake has been slashed to 0
780+
assertEq(stakeManager.getStake(provider_good1), 0);
781+
782+
// check that provider is now blacklisted
783+
assertEq(computeRegistry.getProvider(provider_good1).isWhitelisted, false);
784+
785+
// check that provider is not allowed to add new nodes to the pool
786+
vm.expectRevert();
787+
nodeJoin(domain, pool, provider_good1, node_good1);
788+
}
732789
}

0 commit comments

Comments
 (0)