Skip to content

Commit c22baf0

Browse files
committed
test: compute registry tests
1 parent 98be0a7 commit c22baf0

File tree

2 files changed

+364
-0
lines changed

2 files changed

+364
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import "src/contracts/interfaces/IReleaseManager.sol";
5+
6+
contract ReleaseManagerMock is IReleaseManager {
7+
mapping(bytes32 => bool) public hasRelease;
8+
mapping(bytes32 => string) public metadataURIs;
9+
10+
function publishRelease(OperatorSet calldata operatorSet, Release calldata release) external override returns (uint releaseId) {
11+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
12+
hasRelease[key] = true;
13+
return 1;
14+
}
15+
16+
function publishMetadataURI(OperatorSet calldata operatorSet, string calldata metadataURI) external override {
17+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
18+
metadataURIs[key] = metadataURI;
19+
}
20+
21+
function getLatestRelease(OperatorSet memory operatorSet) external view override returns (uint, Release memory) {
22+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
23+
if (!hasRelease[key]) revert NoReleases();
24+
25+
Artifact[] memory artifacts = new Artifact[](1);
26+
artifacts[0] = Artifact({digest: keccak256("test-artifact"), registry: "https://example.com/registry"});
27+
28+
return (1, Release({artifacts: artifacts, upgradeByTime: uint32(block.timestamp + 7 days)}));
29+
}
30+
31+
function getRelease(OperatorSet memory operatorSet, uint releaseId) external view override returns (Release memory) {
32+
Artifact[] memory artifacts = new Artifact[](1);
33+
artifacts[0] = Artifact({digest: keccak256("test-artifact"), registry: "https://example.com/registry"});
34+
35+
return Release({artifacts: artifacts, upgradeByTime: uint32(block.timestamp + 7 days)});
36+
}
37+
38+
function getTotalReleases(OperatorSet memory operatorSet) external view override returns (uint) {
39+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
40+
return hasRelease[key] ? 1 : 0;
41+
}
42+
43+
function getLatestUpgradeByTime(OperatorSet memory operatorSet) external view override returns (uint32) {
44+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
45+
if (!hasRelease[key]) return 0;
46+
return uint32(block.timestamp + 7 days);
47+
}
48+
49+
function isValidRelease(OperatorSet memory operatorSet, uint releaseId) external view override returns (bool) {
50+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
51+
return hasRelease[key] && releaseId == 1;
52+
}
53+
54+
function getMetadataURI(OperatorSet memory operatorSet) external view override returns (string memory) {
55+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
56+
return metadataURIs[key];
57+
}
58+
59+
function setHasRelease(OperatorSet memory operatorSet, bool _hasRelease) external {
60+
bytes32 key = keccak256(abi.encode(operatorSet.avs, operatorSet.id));
61+
hasRelease[key] = _hasRelease;
62+
}
63+
}
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import "src/contracts/cloud/ComputeRegistry.sol";
5+
import "src/test/utils/EigenLayerUnitTestSetup.sol";
6+
import "src/test/mocks/MockAVSRegistrar.sol";
7+
import "src/test/mocks/ReleaseManagerMock.sol";
8+
9+
contract ComputeRegistryUnitTests is EigenLayerUnitTestSetup, IComputeRegistryErrors, IComputeRegistryEvents {
10+
using StdStyle for *;
11+
using ArrayLib for *;
12+
using OperatorSetLib for OperatorSet;
13+
14+
// Constants
15+
bytes32 constant TOS_HASH = keccak256("Terms of Service v1.0");
16+
string constant VERSION = "1.0.0";
17+
uint constant MAX_EXPIRY = type(uint).max;
18+
19+
// Contracts
20+
ComputeRegistry computeRegistry;
21+
ReleaseManagerMock releaseManagerMock;
22+
23+
// Test variables
24+
address defaultAVS;
25+
address defaultSigner;
26+
uint defaultSignerPrivateKey;
27+
OperatorSet defaultOperatorSet;
28+
29+
function setUp() public virtual override {
30+
EigenLayerUnitTestSetup.setUp();
31+
32+
// Setup mock contracts
33+
releaseManagerMock = new ReleaseManagerMock();
34+
35+
// Setup default test accounts
36+
defaultSignerPrivateKey = 0x1234;
37+
defaultSigner = vm.addr(defaultSignerPrivateKey);
38+
defaultAVS = makeAddr("defaultAVS");
39+
40+
// Deploy ComputeRegistry
41+
computeRegistry = new ComputeRegistry(
42+
IReleaseManager(address(releaseManagerMock)),
43+
IAllocationManager(address(allocationManagerMock)),
44+
IPermissionController(address(permissionController)),
45+
TOS_HASH,
46+
VERSION
47+
);
48+
49+
// Setup default operator set
50+
defaultOperatorSet = OperatorSet(defaultAVS, 0);
51+
52+
// Configure mocks
53+
allocationManagerMock.setIsOperatorSet(defaultOperatorSet, true);
54+
releaseManagerMock.setHasRelease(defaultOperatorSet, true);
55+
56+
// Setup permissions for default signer to call registerForCompute and deregisterFromCompute
57+
vm.startPrank(defaultAVS);
58+
permissionController.setAppointee(defaultAVS, defaultSigner, address(computeRegistry), computeRegistry.registerForCompute.selector);
59+
permissionController.setAppointee(
60+
defaultAVS, defaultSigner, address(computeRegistry), computeRegistry.deregisterFromCompute.selector
61+
);
62+
vm.stopPrank();
63+
}
64+
65+
// Helper functions
66+
function _generateTOSSignature(OperatorSet memory operatorSet, address signer, uint signerPrivateKey)
67+
internal
68+
view
69+
returns (bytes memory)
70+
{
71+
bytes32 digest = computeRegistry.calculateTOSAgreementDigest(operatorSet, signer);
72+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, digest);
73+
return abi.encodePacked(r, s, v);
74+
}
75+
76+
function _generateInvalidSignature() internal pure returns (bytes memory) {
77+
return abi.encodePacked(bytes32(0), bytes32(0), uint8(0));
78+
}
79+
}
80+
81+
contract ComputeRegistryUnitTests_Initialization is ComputeRegistryUnitTests {
82+
function test_initialization() public view {
83+
assertEq(address(computeRegistry.RELEASE_MANAGER()), address(releaseManagerMock));
84+
assertEq(address(computeRegistry.ALLOCATION_MANAGER()), address(allocationManagerMock));
85+
assertEq(address(computeRegistry.permissionController()), address(permissionController));
86+
assertEq(computeRegistry.TOS_HASH(), TOS_HASH);
87+
assertEq(computeRegistry.MAX_EXPIRY(), MAX_EXPIRY);
88+
assertEq(
89+
computeRegistry.TOS_AGREEMENT_TYPEHASH(),
90+
keccak256("TOSAgreement(bytes32 tosHash,address avs,uint32 operatorSetId,address signer,uint256 expiry)")
91+
);
92+
}
93+
94+
function test_domainSeparator() public view {
95+
bytes32 expectedDomainSeparator = keccak256(
96+
abi.encode(
97+
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
98+
keccak256(bytes("EigenLayer")),
99+
keccak256(bytes("1")), // Major version only
100+
block.chainid,
101+
address(computeRegistry)
102+
)
103+
);
104+
assertEq(computeRegistry.domainSeparator(), expectedDomainSeparator);
105+
}
106+
}
107+
108+
contract ComputeRegistryUnitTests_RegisterForCompute is ComputeRegistryUnitTests {
109+
function test_registerForCompute_success() public {
110+
// Generate valid signature
111+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, defaultSignerPrivateKey);
112+
113+
// Register
114+
vm.expectEmit(true, true, true, true);
115+
emit OperatorSetRegistered(defaultOperatorSet, defaultSigner, TOS_HASH, signature);
116+
117+
vm.prank(defaultSigner);
118+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
119+
120+
// Verify registration
121+
assertTrue(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
122+
123+
// Verify TOS signature storage
124+
IComputeRegistryTypes.TOSSignature memory tosSignature = computeRegistry.getOperatorSetTosSignature(defaultOperatorSet);
125+
assertEq(tosSignature.signer, defaultSigner);
126+
assertEq(tosSignature.tosHash, TOS_HASH);
127+
assertEq(tosSignature.signature, signature);
128+
}
129+
130+
function test_registerForCompute_revert_invalidPermissions() public {
131+
address unauthorizedCaller = address(0x999);
132+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, unauthorizedCaller, defaultSignerPrivateKey);
133+
134+
vm.prank(unauthorizedCaller);
135+
vm.expectRevert(PermissionControllerMixin.InvalidPermissions.selector);
136+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
137+
}
138+
139+
function test_registerForCompute_revert_invalidOperatorSet() public {
140+
OperatorSet memory invalidOperatorSet = OperatorSet(defaultAVS, 999);
141+
allocationManagerMock.setIsOperatorSet(invalidOperatorSet, false);
142+
143+
bytes memory signature = _generateTOSSignature(invalidOperatorSet, defaultSigner, defaultSignerPrivateKey);
144+
145+
vm.prank(defaultSigner);
146+
vm.expectRevert(InvalidOperatorSet.selector);
147+
computeRegistry.registerForCompute(invalidOperatorSet, signature);
148+
}
149+
150+
function test_registerForCompute_revert_alreadyRegistered() public {
151+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, defaultSignerPrivateKey);
152+
153+
// First registration succeeds
154+
vm.prank(defaultSigner);
155+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
156+
157+
// Second registration fails
158+
vm.prank(defaultSigner);
159+
vm.expectRevert(OperatorSetAlreadyRegistered.selector);
160+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
161+
}
162+
163+
function test_registerForCompute_revert_noReleases() public {
164+
OperatorSet memory operatorSet = OperatorSet(defaultAVS, 1);
165+
allocationManagerMock.setIsOperatorSet(operatorSet, true);
166+
releaseManagerMock.setHasRelease(operatorSet, false);
167+
168+
bytes memory signature = _generateTOSSignature(operatorSet, defaultSigner, defaultSignerPrivateKey);
169+
170+
vm.prank(defaultSigner);
171+
vm.expectRevert(IReleaseManagerErrors.NoReleases.selector);
172+
computeRegistry.registerForCompute(operatorSet, signature);
173+
}
174+
175+
function test_registerForCompute_revert_invalidSignature() public {
176+
bytes memory invalidSignature = _generateInvalidSignature();
177+
178+
vm.prank(defaultSigner);
179+
vm.expectRevert(ISignatureUtilsMixinErrors.InvalidSignature.selector);
180+
computeRegistry.registerForCompute(defaultOperatorSet, invalidSignature);
181+
}
182+
183+
function test_registerForCompute_revert_wrongSigner() public {
184+
// Generate signature with different private key
185+
uint wrongPrivateKey = 0x5678;
186+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, wrongPrivateKey);
187+
188+
vm.prank(defaultSigner);
189+
vm.expectRevert(ISignatureUtilsMixinErrors.InvalidSignature.selector);
190+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
191+
}
192+
}
193+
194+
contract ComputeRegistryUnitTests_DeregisterFromCompute is ComputeRegistryUnitTests {
195+
function setUp() public override {
196+
super.setUp();
197+
198+
// Pre-register default operator set
199+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, defaultSignerPrivateKey);
200+
vm.prank(defaultSigner);
201+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
202+
}
203+
204+
function test_deregisterFromCompute_success() public {
205+
assertTrue(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
206+
207+
vm.expectEmit(true, true, true, true);
208+
emit OperatorSetDeregistered(defaultOperatorSet);
209+
210+
vm.prank(defaultSigner);
211+
computeRegistry.deregisterFromCompute(defaultOperatorSet);
212+
213+
assertFalse(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
214+
}
215+
216+
function test_deregisterFromCompute_revert_invalidPermissions() public {
217+
address unauthorizedCaller = address(0x999);
218+
219+
vm.prank(unauthorizedCaller);
220+
vm.expectRevert(PermissionControllerMixin.InvalidPermissions.selector);
221+
computeRegistry.deregisterFromCompute(defaultOperatorSet);
222+
}
223+
224+
function test_deregisterFromCompute_revert_invalidOperatorSet() public {
225+
OperatorSet memory invalidOperatorSet = OperatorSet(defaultAVS, 999);
226+
allocationManagerMock.setIsOperatorSet(invalidOperatorSet, false);
227+
228+
vm.prank(defaultSigner);
229+
vm.expectRevert(InvalidOperatorSet.selector);
230+
computeRegistry.deregisterFromCompute(invalidOperatorSet);
231+
}
232+
233+
function test_deregisterFromCompute_revert_notRegistered() public {
234+
OperatorSet memory unregisteredOperatorSet = OperatorSet(defaultAVS, 1);
235+
allocationManagerMock.setIsOperatorSet(unregisteredOperatorSet, true);
236+
237+
vm.prank(defaultSigner);
238+
vm.expectRevert(OperatorSetNotRegistered.selector);
239+
computeRegistry.deregisterFromCompute(unregisteredOperatorSet);
240+
}
241+
242+
function test_deregisterFromCompute_canReregisterAfterDeregister() public {
243+
// Deregister
244+
vm.prank(defaultSigner);
245+
computeRegistry.deregisterFromCompute(defaultOperatorSet);
246+
assertFalse(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
247+
248+
// Re-register
249+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, defaultSignerPrivateKey);
250+
vm.prank(defaultSigner);
251+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
252+
assertTrue(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
253+
}
254+
}
255+
256+
contract ComputeRegistryUnitTests_ViewFunctions is ComputeRegistryUnitTests {
257+
function test_getOperatorSetTosSignature_registered() public {
258+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, defaultSignerPrivateKey);
259+
260+
vm.prank(defaultSigner);
261+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
262+
263+
IComputeRegistryTypes.TOSSignature memory tosSignature = computeRegistry.getOperatorSetTosSignature(defaultOperatorSet);
264+
assertEq(tosSignature.signer, defaultSigner);
265+
assertEq(tosSignature.tosHash, TOS_HASH);
266+
assertEq(tosSignature.signature, signature);
267+
}
268+
269+
function test_getOperatorSetTosSignature_notRegistered() public {
270+
IComputeRegistryTypes.TOSSignature memory tosSignature = computeRegistry.getOperatorSetTosSignature(defaultOperatorSet);
271+
assertEq(tosSignature.signer, address(0));
272+
assertEq(tosSignature.tosHash, bytes32(0));
273+
assertEq(tosSignature.signature.length, 0);
274+
}
275+
276+
function test_calculateTOSAgreementDigest() public view {
277+
bytes32 digest = computeRegistry.calculateTOSAgreementDigest(defaultOperatorSet, defaultSigner);
278+
279+
// Verify digest is deterministic
280+
bytes32 digest2 = computeRegistry.calculateTOSAgreementDigest(defaultOperatorSet, defaultSigner);
281+
assertEq(digest, digest2);
282+
283+
// Verify digest changes with different parameters
284+
bytes32 digestDifferentSigner = computeRegistry.calculateTOSAgreementDigest(defaultOperatorSet, address(0x999));
285+
assertTrue(digest != digestDifferentSigner);
286+
287+
OperatorSet memory differentOperatorSet = OperatorSet(defaultAVS, 1);
288+
bytes32 digestDifferentOperatorSet = computeRegistry.calculateTOSAgreementDigest(differentOperatorSet, defaultSigner);
289+
assertTrue(digest != digestDifferentOperatorSet);
290+
}
291+
292+
function test_isOperatorSetRegistered() public {
293+
assertFalse(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
294+
295+
bytes memory signature = _generateTOSSignature(defaultOperatorSet, defaultSigner, defaultSignerPrivateKey);
296+
vm.prank(defaultSigner);
297+
computeRegistry.registerForCompute(defaultOperatorSet, signature);
298+
299+
assertTrue(computeRegistry.isOperatorSetRegistered(defaultOperatorSet.key()));
300+
}
301+
}

0 commit comments

Comments
 (0)