Skip to content

Commit d12501e

Browse files
authored
test: ecdsa stake registry unit tests (#436)
**Motivation:** Improve coverage on ECDSA Stake Registry **Modifications:** Add tests for edge cases for reverts with Quorum Config updates and tests for event emission **Result:** improved test coverage
1 parent 4d63f27 commit d12501e

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed

test/mocks/ECDSAStakeRegistryMock.sol

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,35 @@
22
pragma solidity ^0.8.27;
33

44
import "../../src/unaudited/ECDSAStakeRegistry.sol";
5+
import {CheckpointsUpgradeable} from
6+
"@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol";
57

68
/**
79
* @title Mock for ECDSAStakeRegistry
810
* @dev This contract is a mock implementation of the ECDSAStakeRegistry for testing purposes.
911
*/
1012
contract ECDSAStakeRegistryMock is ECDSAStakeRegistry {
13+
using CheckpointsUpgradeable for CheckpointsUpgradeable.History;
14+
1115
constructor(
1216
IDelegationManager _delegationManager
1317
) ECDSAStakeRegistry(_delegationManager) {}
18+
19+
/**
20+
* @notice Sets the total weight at a specific block for testing
21+
* @param blockNumber The block number
22+
* @param weight The weight to set
23+
*/
24+
function setTotalWeightAtBlock(uint32 blockNumber, uint256 weight) external {
25+
_totalWeightHistory.push(weight);
26+
}
27+
28+
/**
29+
* @notice Sets the threshold weight at a specific block for testing
30+
* @param blockNumber The block number
31+
* @param weight The weight to set
32+
*/
33+
function setThresholdWeightAtBlock(uint32 blockNumber, uint256 weight) external {
34+
_thresholdWeightHistory.push(weight);
35+
}
1436
}

test/unit/ECDSAStakeRegistryUnit.t.sol

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,4 +863,257 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup {
863863
}
864864
return (operators, signatures);
865865
}
866+
867+
function test_RevertsWhen_ZeroStrategies_UpdateQuorumConfig() public {
868+
IECDSAStakeRegistryTypes.Quorum memory invalidQuorum = IECDSAStakeRegistryTypes.Quorum({
869+
strategies: new IECDSAStakeRegistryTypes.StrategyParams[](0)
870+
});
871+
address[] memory operators = new address[](0);
872+
873+
vm.expectRevert(IECDSAStakeRegistryErrors.InvalidQuorum.selector);
874+
registry.updateQuorumConfig(invalidQuorum, operators);
875+
}
876+
877+
function test_RevertsWhen_PartialBPSSum_UpdateQuorumConfig() public {
878+
IStrategy s1 = IStrategy(address(0x1));
879+
IStrategy s2 = IStrategy(address(0x2));
880+
IECDSAStakeRegistryTypes.StrategyParams[] memory strategies =
881+
new IECDSAStakeRegistryTypes.StrategyParams[](2);
882+
strategies[0] = IECDSAStakeRegistryTypes.StrategyParams({strategy: s1, multiplier: 9000});
883+
strategies[1] = IECDSAStakeRegistryTypes.StrategyParams({strategy: s2, multiplier: 999});
884+
885+
address[] memory operators = new address[](0);
886+
887+
vm.expectRevert(IECDSAStakeRegistryErrors.InvalidQuorum.selector);
888+
registry.updateQuorumConfig(
889+
IECDSAStakeRegistryTypes.Quorum({strategies: strategies}), operators
890+
);
891+
}
892+
893+
function test_RevertsWhen_ExcessBPSSum_UpdateQuorumConfig() public {
894+
IStrategy s1 = IStrategy(address(0x1));
895+
IStrategy s2 = IStrategy(address(0x2));
896+
IECDSAStakeRegistryTypes.StrategyParams[] memory strategies =
897+
new IECDSAStakeRegistryTypes.StrategyParams[](2);
898+
strategies[0] = IECDSAStakeRegistryTypes.StrategyParams({strategy: s1, multiplier: 9000});
899+
strategies[1] = IECDSAStakeRegistryTypes.StrategyParams({strategy: s2, multiplier: 1001});
900+
901+
address[] memory operators = new address[](0);
902+
903+
vm.expectRevert(IECDSAStakeRegistryErrors.InvalidQuorum.selector);
904+
registry.updateQuorumConfig(
905+
IECDSAStakeRegistryTypes.Quorum({strategies: strategies}), operators
906+
);
907+
}
908+
909+
function test_RevertsWhen_FutureReferenceBlock_CheckSignatures() public {
910+
bytes32 digest = keccak256("data");
911+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(operator1Pk, digest);
912+
bytes[] memory signatures = new bytes[](1);
913+
signatures[0] = abi.encodePacked(r, s, v);
914+
address[] memory operators = new address[](1);
915+
operators[0] = operator1;
916+
917+
vm.expectRevert(IECDSAStakeRegistryErrors.InvalidReferenceBlock.selector);
918+
registry.isValidSignature(digest, abi.encode(operators, signatures, uint32(block.number)));
919+
}
920+
}
921+
922+
contract ECDSAStakeRegistryEventsTest is ECDSAStakeRegistrySetup {
923+
address private newOperator = address(0x123);
924+
address private newSigningKey = address(0x456);
925+
uint256 private blockBeforeUpdate;
926+
927+
function setUp() public override {
928+
super.setUp();
929+
// Ensure block number can be checkpointed
930+
vm.roll(block.number + 1);
931+
blockBeforeUpdate = block.number;
932+
}
933+
934+
function test_OperatorRegistered_Event() public {
935+
ISignatureUtilsMixinTypes.SignatureWithSaltAndExpiry memory sig;
936+
937+
// Expect event
938+
vm.expectEmit(true, true, false, true);
939+
emit OperatorRegistered(newOperator, address(mockServiceManager));
940+
941+
// Test action
942+
vm.prank(newOperator);
943+
registry.registerOperatorWithSignature(sig, newOperator);
944+
945+
// Verify state
946+
assertTrue(registry.operatorRegistered(newOperator));
947+
}
948+
949+
function test_OperatorDeregistered_Event() public {
950+
vm.expectEmit(true, true, false, true);
951+
emit OperatorDeregistered(operator1, address(mockServiceManager));
952+
953+
vm.prank(operator1);
954+
registry.deregisterOperator();
955+
956+
assertFalse(registry.operatorRegistered(operator1));
957+
}
958+
959+
function test_QuorumUpdated_Event() public {
960+
IECDSAStakeRegistryTypes.Quorum memory oldQuorum = registry.quorum();
961+
IECDSAStakeRegistryTypes.Quorum memory newQuorum = IECDSAStakeRegistryTypes.Quorum({
962+
strategies: new IECDSAStakeRegistryTypes.StrategyParams[](1)
963+
});
964+
newQuorum.strategies[0] = IECDSAStakeRegistryTypes.StrategyParams({
965+
strategy: IStrategy(address(0x1234)),
966+
multiplier: 10000
967+
});
968+
969+
vm.expectEmit(true, true, true, true);
970+
emit QuorumUpdated(oldQuorum, newQuorum);
971+
972+
registry.updateQuorumConfig(newQuorum, new address[](0));
973+
974+
assertEq(registry.quorum().strategies.length, newQuorum.strategies.length);
975+
}
976+
977+
function test_MinimumWeightUpdated_Event() public {
978+
uint256 oldWeight = registry.minimumWeight();
979+
uint256 newWeight = 5000;
980+
981+
vm.expectEmit(true, true, true, true);
982+
emit MinimumWeightUpdated(oldWeight, newWeight);
983+
984+
registry.updateMinimumWeight(newWeight, new address[](0));
985+
986+
assertEq(registry.minimumWeight(), newWeight);
987+
}
988+
989+
function test_MinimumWeightUpdated_Zero_Threshold() public {
990+
registry.updateMinimumWeight(1000, new address[](0));
991+
uint256 oldWeight = registry.minimumWeight();
992+
uint256 newWeight = 0;
993+
994+
vm.expectEmit(true, true, true, true);
995+
emit MinimumWeightUpdated(oldWeight, newWeight);
996+
997+
registry.updateMinimumWeight(newWeight, new address[](0));
998+
999+
assertEq(registry.minimumWeight(), newWeight);
1000+
}
1001+
1002+
function test_OperatorWeightUpdated_Event() public {
1003+
uint256 oldWeight = registry.getLastCheckpointOperatorWeight(operator1);
1004+
uint256 newWeight = oldWeight + 100; // Simulate weight increase
1005+
1006+
IStrategy[] memory strategies = new IStrategy[](1);
1007+
strategies[0] = registry.quorum().strategies[0].strategy;
1008+
uint256[] memory shares = new uint256[](1);
1009+
shares[0] = newWeight;
1010+
1011+
vm.mockCall(
1012+
address(mockDelegationManager),
1013+
abi.encodeWithSelector(
1014+
MockDelegationManager.getOperatorShares.selector, operator1, strategies
1015+
),
1016+
abi.encode(shares)
1017+
);
1018+
1019+
vm.expectEmit(true, true, true, true);
1020+
emit OperatorWeightUpdated(operator1, oldWeight, newWeight);
1021+
1022+
address[] memory operators = new address[](1);
1023+
operators[0] = operator1;
1024+
registry.updateOperators(operators);
1025+
1026+
assertEq(registry.getLastCheckpointOperatorWeight(operator1), newWeight);
1027+
}
1028+
1029+
function test_TotalWeightUpdated_Event() public {
1030+
uint256 oldTotal = registry.getLastCheckpointTotalWeight();
1031+
1032+
uint256 weight1 = registry.getLastCheckpointOperatorWeight(operator1);
1033+
uint256 weight2 = registry.getLastCheckpointOperatorWeight(operator2);
1034+
1035+
IStrategy[] memory strategies = new IStrategy[](1);
1036+
strategies[0] = registry.quorum().strategies[0].strategy;
1037+
1038+
uint256[] memory shares1 = new uint256[](1);
1039+
shares1[0] = weight1 * 2;
1040+
1041+
uint256[] memory shares2 = new uint256[](1);
1042+
shares2[0] = weight2 * 2;
1043+
1044+
uint256 newTotal = oldTotal + weight1 + weight2;
1045+
1046+
vm.mockCall(
1047+
address(mockDelegationManager),
1048+
abi.encodeWithSelector(
1049+
MockDelegationManager.getOperatorShares.selector, operator1, strategies
1050+
),
1051+
abi.encode(shares1)
1052+
);
1053+
1054+
vm.mockCall(
1055+
address(mockDelegationManager),
1056+
abi.encodeWithSelector(
1057+
MockDelegationManager.getOperatorShares.selector, operator2, strategies
1058+
),
1059+
abi.encode(shares2)
1060+
);
1061+
1062+
vm.expectEmit(true, true, true, true);
1063+
emit TotalWeightUpdated(oldTotal, newTotal);
1064+
1065+
address[] memory operators = new address[](2);
1066+
operators[0] = operator1;
1067+
operators[1] = operator2;
1068+
registry.updateOperators(operators);
1069+
1070+
assertEq(registry.getLastCheckpointTotalWeight(), newTotal);
1071+
}
1072+
1073+
function test_ThresholdWeightUpdated_Event() public {
1074+
uint256 newThreshold = 10000000000;
1075+
1076+
vm.expectEmit(true, true, true, true);
1077+
emit ThresholdWeightUpdated(newThreshold);
1078+
1079+
vm.prank(registry.owner());
1080+
registry.updateStakeThreshold(newThreshold);
1081+
1082+
assertEq(registry.getLastCheckpointThresholdWeight(), newThreshold);
1083+
}
1084+
1085+
function test_SigningKeyUpdate_Event() public {
1086+
address oldKey = registry.getLatestOperatorSigningKey(operator1);
1087+
address newKey = address(0x789);
1088+
1089+
vm.expectEmit(true, true, true, true);
1090+
emit SigningKeyUpdate(operator1, block.number, newKey, oldKey);
1091+
1092+
vm.prank(operator1);
1093+
registry.updateOperatorSigningKey(newKey);
1094+
1095+
assertEq(registry.getLatestOperatorSigningKey(operator1), newKey);
1096+
}
1097+
1098+
function test_MultiEvent_DeregisterOperator() public {
1099+
uint256 oldOperatorWeight = registry.getLastCheckpointOperatorWeight(operator1);
1100+
uint256 oldTotalWeight = registry.getLastCheckpointTotalWeight();
1101+
uint256 expectedNewTotal = oldTotalWeight - oldOperatorWeight;
1102+
1103+
vm.expectEmit(true, true, true, true);
1104+
emit OperatorWeightUpdated(operator1, oldOperatorWeight, 0);
1105+
1106+
vm.expectEmit(true, true, true, true);
1107+
emit TotalWeightUpdated(oldTotalWeight, expectedNewTotal);
1108+
1109+
vm.expectEmit(true, true, false, true);
1110+
emit OperatorDeregistered(operator1, address(mockServiceManager));
1111+
1112+
vm.prank(operator1);
1113+
registry.deregisterOperator();
1114+
1115+
assertFalse(registry.operatorRegistered(operator1));
1116+
assertEq(registry.getLastCheckpointOperatorWeight(operator1), 0);
1117+
assertEq(registry.getLastCheckpointTotalWeight(), expectedNewTotal);
1118+
}
8661119
}

0 commit comments

Comments
 (0)