From f6759f5242bf89f3110d89fce209ea8290b10970 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 7 May 2024 07:48:48 -0400 Subject: [PATCH 01/13] feat: add operator key rotation --- .../IECDSAStakeRegistryEventsAndErrors.sol | 22 ++++- src/unaudited/ECDSAStakeRegistry.sol | 89 ++++++++++++++++--- src/unaudited/ECDSAStakeRegistryStorage.sol | 11 ++- 3 files changed, 104 insertions(+), 18 deletions(-) diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 48072f54..53ded372 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -36,13 +36,20 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Emitted when the weight required to be an operator changes /// @param oldMinimumWeight The previous weight /// @param newMinimumWeight The updated weight - event UpdateMinimumWeight(uint256 oldMinimumWeight, uint256 newMinimumWeight); + event UpdateMinimumWeight( + uint256 oldMinimumWeight, + uint256 newMinimumWeight + ); /// @notice Emitted when the system updates an operator's weight /// @param _operator The address of the operator updated /// @param oldWeight The operator's weight before the update /// @param newWeight The operator's weight after the update - event OperatorWeightUpdated(address indexed _operator, uint256 oldWeight, uint256 newWeight); + event OperatorWeightUpdated( + address indexed _operator, + uint256 oldWeight, + uint256 newWeight + ); /// @notice Emitted when the system updates the total weight /// @param oldTotalWeight The total weight before the update @@ -52,6 +59,15 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Emits when setting a new threshold weight. event ThresholdWeightUpdated(uint256 _thresholdWeight); + /// @notice Emitted when an operator's signing key is updated + /// @param operator The address of the operator whose signing key was updated + /// @param oldSigningKey The operator's signing key before the update + /// @param newSigningKey The operator's signing key after the update + event SigningKeyUpdate( + address indexed operator, + address indexed oldSigningKey, + address indexed newSigningKey + ); /// @notice Indicates when the lengths of the signers array and signatures array do not match. error LengthMismatch(); @@ -64,7 +80,7 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Thrown when the threshold update is greater than BPS error InvalidThreshold(); - /// @notice Thrown when missing operators in an update + /// @notice Thrown when missing operators in an update error MustUpdateAllOperators(); /// @notice Indicates operator weights were out of sync and the signed weight exceed the total diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index 5a168cb3..aa7548a1 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -43,13 +43,16 @@ contract ECDSAStakeRegistry is __ECDSAStakeRegistry_init(_serviceManager, _thresholdWeight, _quorum); } - /// @notice Registers a new operator using a provided signature + /// @notice Registers a new operator using a provided signature and signing key + /// @param _operator The address of the operator to register /// @param _operatorSignature Contains the operator's signature, salt, and expiry + /// @param _signingKey The signing key to add to the operator's history function registerOperatorWithSignature( address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _signingKey ) external { - _registerOperatorWithSig(_operator, _operatorSignature); + _registerOperatorWithSig(_operator, _operatorSignature, _signingKey); } /// @notice Deregisters an existing operator @@ -57,6 +60,18 @@ contract ECDSAStakeRegistry is _deregisterOperator(msg.sender); } + /** + * @notice Updates the signing key for an operator + * @dev Only callable by the operator themselves + * @param _newSigningKey The new signing key to set for the operator + */ + function updateOperatorSigningKey(address _newSigningKey) external { + if (!_operatorRegistered[msg.sender]) { + revert OperatorNotRegistered(); + } + _updateOperatorSigningKey(msg.sender, _newSigningKey); + } + /** * @notice Updates the StakeRegistry's view of one or more operators' stakes adding a new entry in their history of stake checkpoints, * @dev Queries stakes from the Eigenlayer core DelegationManager contract @@ -312,9 +327,11 @@ contract ECDSAStakeRegistry is /// @dev registers an operator through a provided signature /// @param _operatorSignature Contains the operator's signature, salt, and expiry + /// @param _signingKey The signing key to add to the operator's history function _registerOperatorWithSig( address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _signingKey ) internal virtual { if (_operatorRegistered[_operator]) { revert OperatorAlreadyRegistered(); @@ -323,6 +340,7 @@ contract ECDSAStakeRegistry is _operatorRegistered[_operator] = true; int256 delta = _updateOperatorWeight(_operator); _updateTotalWeight(delta); + _updateOperatorSigningKey(_operator, _signingKey); IServiceManager(_serviceManager).registerOperatorToAVS( _operator, _operatorSignature @@ -330,6 +348,23 @@ contract ECDSAStakeRegistry is emit OperatorRegistered(_operator, _serviceManager); } + /// @dev Internal function to update an operator's signing key + /// @param _operator The address of the operator to update the signing key for + /// @param _newSigningKey The new signing key to set for the operator + function _updateOperatorSigningKey( + address _operator, + address _newSigningKey + ) internal { + address oldSigningKey = address( + uint160(_operatorSigningKeyHistory[_operator].latest()) + ); + if (_newSigningKey == oldSigningKey) { + return; + } + _operatorSigningKeyHistory[_operator].push(uint160(_newSigningKey)); + emit SigningKeyUpdate(_operator, oldSigningKey, _newSigningKey); + } + /// @notice Updates the weight of an operator and returns the previous and current weights. /// @param _operator The address of the operator to update the weight of. function _updateOperatorWeight( @@ -400,30 +435,33 @@ contract ECDSAStakeRegistry is /** * @notice Common logic to verify a batch of ECDSA signatures against a hash, using either last stake weight or at a specific block. * @param _dataHash The hash of the data the signers endorsed. - * @param _signers A collection of addresses that endorsed the data hash. + * @param _operators A collection of addresses that endorsed the data hash. * @param _signatures A collection of signatures matching the signers. * @param _referenceBlock The block number for evaluating stake weight; use max uint32 for latest weight. */ function _checkSignatures( bytes32 _dataHash, - address[] memory _signers, + address[] memory _operators, bytes[] memory _signatures, uint32 _referenceBlock ) internal view { - uint256 signersLength = _signers.length; - address lastSigner; + uint256 signersLength = _operators.length; + address currentOperator; + address lastOperator; + address signer; uint256 signedWeight; _validateSignaturesLength(signersLength, _signatures.length); for (uint256 i; i < signersLength; i++) { - address currentSigner = _signers[i]; + currentOperator = _operators[i]; + signer = _getOperatorSigningKey(currentOperator, _referenceBlock); - _validateSortedSigners(lastSigner, currentSigner); - _validateSignature(currentSigner, _dataHash, _signatures[i]); + _validateSortedSigners(lastOperator, currentOperator); + _validateSignature(signer, _dataHash, _signatures[i]); - lastSigner = currentSigner; + lastOperator = currentOperator; uint256 operatorWeight = _getOperatorWeight( - currentSigner, + currentOperator, _referenceBlock ); signedWeight += operatorWeight; @@ -473,6 +511,31 @@ contract ECDSAStakeRegistry is } } + /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. + /// @param _operator The operator to query their signing key history for + /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. + /// @return The weight of the operator. + function _getOperatorSigningKey( + address _operator, + uint32 _referenceBlock + ) internal view returns (address) { + if (_referenceBlock == type(uint32).max) { + return + address( + uint160(_operatorSigningKeyHistory[_operator].latest()) + ); + } else { + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _referenceBlock + ) + ) + ); + } + } + /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. /// @param _signer The address of the signer whose weight is returned. /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index fe09b9d8..1d5589e4 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -5,7 +5,9 @@ import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/ import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; -abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors { +abstract contract ECDSAStakeRegistryStorage is + ECDSAStakeRegistryEventsAndErrors +{ /// @notice Manages staking delegations through the DelegationManager interface IDelegationManager internal immutable DELEGATION_MANAGER; @@ -27,6 +29,10 @@ abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors /// @notice Defines the duration after which the stake's weight expires. uint256 internal _stakeExpiry; + /// @notice Maps an operator to their signing key history using checkpoints + mapping(address => CheckpointsUpgradeable.History) + internal _operatorSigningKeyHistory; + /// @notice Tracks the total stake history over time using checkpoints CheckpointsUpgradeable.History internal _totalWeightHistory; @@ -34,7 +40,8 @@ abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors CheckpointsUpgradeable.History internal _thresholdWeightHistory; /// @notice Maps operator addresses to their respective stake histories using checkpoints - mapping(address => CheckpointsUpgradeable.History) internal _operatorWeightHistory; + mapping(address => CheckpointsUpgradeable.History) + internal _operatorWeightHistory; /// @notice Maps an operator to their registration status mapping(address => bool) internal _operatorRegistered; From 9cdb01e6af5121908ccd47055e2abe3e7ae4c3fe Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 7 May 2024 11:39:42 -0400 Subject: [PATCH 02/13] test: update existing tests to account for signing key --- src/unaudited/ECDSAStakeRegistry.sol | 6 +- .../ECDSAStakeRegistryPermissioned.sol | 24 +- .../ECDSAStakeRegistryEqualWeightUnit.t.sol | 74 +++- .../ECDSAStakeRegistryPermissionedUnit.t.sol | 64 +++- test/unit/ECDSAStakeRegistryUnit.t.sol | 336 +++++++++++++----- 5 files changed, 369 insertions(+), 135 deletions(-) diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index aa7548a1..5ff228f7 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -121,18 +121,18 @@ contract ECDSAStakeRegistry is /// @notice Verifies if the provided signature data is valid for the given data hash. /// @param _dataHash The hash of the data that was signed. - /// @param _signatureData Encoded signature data consisting of an array of signers, an array of signatures, and a reference block number. + /// @param _signatureData Encoded signature data consisting of an array of operators, an array of signatures, and a reference block number. /// @return The function selector that indicates the signature is valid according to ERC1271 standard. function isValidSignature( bytes32 _dataHash, bytes memory _signatureData ) external view returns (bytes4) { ( - address[] memory signers, + address[] memory operators, bytes[] memory signatures, uint32 referenceBlock ) = abi.decode(_signatureData, (address[], bytes[], uint32)); - _checkSignatures(_dataHash, signers, signatures, referenceBlock); + _checkSignatures(_dataHash, operators, signatures, referenceBlock); return IERC1271Upgradeable.isValidSignature.selector; } diff --git a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol index c5329ce2..ef2e691c 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol @@ -27,7 +27,9 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// @dev Custom error to signal that an operator is already allowlisted. error OperatorAlreadyAllowlisted(); - constructor(IDelegationManager _delegationManager) ECDSAStakeRegistry(_delegationManager) { + constructor( + IDelegationManager _delegationManager + ) ECDSAStakeRegistry(_delegationManager) { // _disableInitializers(); } @@ -63,38 +65,40 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// Doesn't register the operator into the operator set /// @param _operator The address of the operator to allowlist. function _permitOperator(address _operator) internal { - if (allowlistedOperators[_operator]){ + if (allowlistedOperators[_operator]) { revert OperatorAlreadyAllowlisted(); } allowlistedOperators[_operator] = true; emit OperatorPermitted(_operator); - } /// @dev Removes an operator from the allowlist. /// If the operator is registered, also deregisters the operator. /// @param _operator The address of the operator to be revoked. function _revokeOperator(address _operator) internal { - if (!allowlistedOperators[_operator]){ + if (!allowlistedOperators[_operator]) { revert OperatorNotAllowlisted(); } delete allowlistedOperators[_operator]; emit OperatorRevoked(_operator); - if (_operatorRegistered[_operator]){ + if (_operatorRegistered[_operator]) { _ejectOperator(_operator); } - } /// @inheritdoc ECDSAStakeRegistry function _registerOperatorWithSig( address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _operatorSigningKey ) internal override { - if (allowlistedOperators[_operator] != true){ + if (allowlistedOperators[_operator] != true) { revert OperatorNotAllowlisted(); } - super._registerOperatorWithSig(_operator, _operatorSignature); + super._registerOperatorWithSig( + _operator, + _operatorSignature, + _operatorSigningKey + ); } } - diff --git a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index 8775cd55..9f56d08b 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -11,40 +11,80 @@ import {ECDSAStakeRegistryEqualWeight} from "../../src/unaudited/examples/ECDSAS contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { ECDSAStakeRegistryEqualWeight internal fixedWeightRegistry; + function setUp() public virtual override { super.setUp(); - fixedWeightRegistry = new ECDSAStakeRegistryEqualWeight(IDelegationManager(address(mockDelegationManager))); + fixedWeightRegistry = new ECDSAStakeRegistryEqualWeight( + IDelegationManager(address(mockDelegationManager)) + ); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - fixedWeightRegistry.initialize(address(mockServiceManager), 100, quorum); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10000 + }); + fixedWeightRegistry.initialize( + address(mockServiceManager), + 100, + quorum + ); fixedWeightRegistry.permitOperator(operator1); fixedWeightRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - fixedWeightRegistry.registerOperatorWithSignature(operator1, operatorSignature); - fixedWeightRegistry.registerOperatorWithSignature(operator2, operatorSignature); + fixedWeightRegistry.registerOperatorWithSignature( + operator1, + operatorSignature, + operator1 + ); + fixedWeightRegistry.registerOperatorWithSignature( + operator2, + operatorSignature, + operator2 + ); } function test_FixedStakeUpdates() public { - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 1 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); vm.roll(block.number + 1); vm.prank(operator1); fixedWeightRegistry.deregisterOperator(); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 0); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 0 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 1); vm.roll(block.number + 1); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - fixedWeightRegistry.registerOperatorWithSignature(operator1, operatorSignature); + fixedWeightRegistry.registerOperatorWithSignature( + operator1, + operatorSignature, + operator1 + ); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 1 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); vm.roll(block.number + 1); @@ -53,8 +93,14 @@ contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { operators[1] = operator2; fixedWeightRegistry.updateOperators(operators); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 1 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); } } diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index 57031b29..cc63ac19 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -11,23 +11,41 @@ import {ECDSAStakeRegistryPermissioned} from "../../src/unaudited/examples/ECDSA contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ECDSAStakeRegistryPermissioned internal permissionedRegistry; + function setUp() public virtual override { super.setUp(); - permissionedRegistry = new ECDSAStakeRegistryPermissioned(IDelegationManager(address(mockDelegationManager))); + permissionedRegistry = new ECDSAStakeRegistryPermissioned( + IDelegationManager(address(mockDelegationManager)) + ); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - permissionedRegistry.initialize(address(mockServiceManager), 100, quorum); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10000 + }); + permissionedRegistry.initialize( + address(mockServiceManager), + 100, + quorum + ); permissionedRegistry.permitOperator(operator1); permissionedRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - permissionedRegistry.registerOperatorWithSignature(operator1, operatorSignature); - permissionedRegistry.registerOperatorWithSignature(operator2, operatorSignature); + permissionedRegistry.registerOperatorWithSignature( + operator1, + operatorSignature, + operator1 + ); + permissionedRegistry.registerOperatorWithSignature( + operator2, + operatorSignature, + operator1 + ); } function test_RevertsWhen_NotOwner_PermitOperator() public { - address notOwner=address(0xBEEF); + address notOwner = address(0xBEEF); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); permissionedRegistry.permitOperator(operator1); @@ -39,14 +57,14 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_NotOwner_RevokeOperator() public { - address notOwner=address(0xBEEF); + address notOwner = address(0xBEEF); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); permissionedRegistry.revokeOperator(operator1); } function test_When_NotOperator_RevokeOperator() public { - address notOperator=address(0xBEEF); + address notOperator = address(0xBEEF); permissionedRegistry.permitOperator(notOperator); permissionedRegistry.revokeOperator(notOperator); @@ -57,14 +75,14 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_NotOwner_EjectOperator() public { - address notOwner=address(0xBEEF); + address notOwner = address(0xBEEF); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); permissionedRegistry.ejectOperator(operator1); } function test_RevertsWhen_NotOperator_EjectOperator() public { - address notOperator=address(0xBEEF); + address notOperator = address(0xBEEF); vm.expectRevert(abi.encodeWithSelector(OperatorNotRegistered.selector)); permissionedRegistry.ejectOperator(notOperator); } @@ -77,26 +95,40 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address operator3 = address(0xBEEF); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - vm.expectRevert(abi.encodeWithSelector(ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector)); - permissionedRegistry.registerOperatorWithSignature(operator3, operatorSignature); - + vm.expectRevert( + abi.encodeWithSelector( + ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector + ) + ); + permissionedRegistry.registerOperatorWithSignature( + operator3, + operatorSignature, + operator3 + ); } function test_WhenAllowlisted_RegisterOperatorWithSig() public { address operator3 = address(0xBEEF); permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - permissionedRegistry.registerOperatorWithSignature(operator3, operatorSignature); + permissionedRegistry.registerOperatorWithSignature( + operator3, + operatorSignature, + operator3 + ); } function test_DeregisterOperator() public { address operator3 = address(0xBEEF); permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - permissionedRegistry.registerOperatorWithSignature(operator3, operatorSignature); + permissionedRegistry.registerOperatorWithSignature( + operator3, + operatorSignature, + operator3 + ); vm.prank(operator3); permissionedRegistry.deregisterOperator(); } - } diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 7ffbb7d8..e175c681 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -10,7 +10,6 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {ECDSAStakeRegistry} from "../../src/unaudited/ECDSAStakeRegistry.sol"; import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; - contract MockServiceManager { // solhint-disable-next-line function deregisterOperatorFromAVS(address) external {} @@ -26,9 +25,12 @@ contract MockDelegationManager { return 1000; // Return a dummy value for simplicity } - function getOperatorShares(address, address[] memory strategies) external pure returns (uint256[] memory) { - uint256[] memory response = new uint256[](strategies.length); - for (uint256 i; i < strategies.length; i++){ + function getOperatorShares( + address, + address[] memory strategies + ) external pure returns (uint256[] memory) { + uint256[] memory response = new uint256[](strategies.length); + for (uint256 i; i < strategies.length; i++) { response[i] = 1000; } return response; // Return a dummy value for simplicity @@ -53,33 +55,47 @@ contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { (operator2, operator2Pk) = makeAddrAndKey("Signer 2"); mockDelegationManager = new MockDelegationManager(); mockServiceManager = new MockServiceManager(); - } } -contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup{ +contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ECDSAStakeRegistry public registry; function setUp() public virtual override { super.setUp(); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - registry = new ECDSAStakeRegistry(IDelegationManager(address(mockDelegationManager))); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10000 + }); + registry = new ECDSAStakeRegistry( + IDelegationManager(address(mockDelegationManager)) + ); registry.initialize(address(mockServiceManager), 100, quorum); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - registry.registerOperatorWithSignature(operator1, operatorSignature); - registry.registerOperatorWithSignature(operator2, operatorSignature); - + registry.registerOperatorWithSignature( + operator1, + operatorSignature, + operator1 + ); + registry.registerOperatorWithSignature( + operator2, + operatorSignature, + operator2 + ); } -function test_UpdateQuorumConfig() public { + function test_UpdateQuorumConfig() public { IStrategy mockStrategy = IStrategy(address(420)); Quorum memory oldQuorum = registry.quorum(); Quorum memory newQuorum = Quorum({strategies: new StrategyParams[](1)}); - newQuorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - address[] memory operators = new address[](2); + newQuorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10000 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -90,25 +106,34 @@ function test_UpdateQuorumConfig() public { } function test_RevertsWhen_InvalidQuorum_UpdateQuourmConfig() public { - Quorum memory invalidQuorum = Quorum({strategies: new StrategyParams[](1)}); + Quorum memory invalidQuorum = Quorum({ + strategies: new StrategyParams[](1) + }); invalidQuorum.strategies[0] = StrategyParams({ /// TODO: Make mock strategy strategy: IStrategy(address(420)), multiplier: 5000 // This should cause the update to revert as it's not the total required }); - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector + ); registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertsWhen_NotOwner_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](1)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 10000}); + Quorum memory validQuorum = Quorum({ + strategies: new StrategyParams[](1) + }); + validQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 10000 + }); - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -121,7 +146,7 @@ function test_UpdateQuorumConfig() public { function test_RevertsWhen_SameQuorum_UpdateQuorumConfig() public { Quorum memory quorum = registry.quorum(); - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -130,58 +155,87 @@ function test_UpdateQuorumConfig() public { } function test_RevertSWhen_Duplicate_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](2)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 5_000}); - address[] memory operators = new address[](2); + Quorum memory validQuorum = Quorum({ + strategies: new StrategyParams[](2) + }); + validQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 5_000 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - validQuorum.strategies[1] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 5_000}); + validQuorum.strategies[1] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 5_000 + }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); registry.updateQuorumConfig(validQuorum, operators); } function test_RevertSWhen_NotSorted_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](2)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 5_000}); - address[] memory operators = new address[](2); + Quorum memory validQuorum = Quorum({ + strategies: new StrategyParams[](2) + }); + validQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 5_000 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - validQuorum.strategies[1] = StrategyParams({strategy: IStrategy(address(419)), multiplier: 5_000}); + validQuorum.strategies[1] = StrategyParams({ + strategy: IStrategy(address(419)), + multiplier: 5_000 + }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); registry.updateQuorumConfig(validQuorum, operators); } function test_RevertSWhen_OverMultiplierTotal_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](1)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 10001}); - address[] memory operators = new address[](2); + Quorum memory validQuorum = Quorum({ + strategies: new StrategyParams[](1) + }); + validQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 10001 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector); - registry.updateQuorumConfig(validQuorum,operators); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector + ); + registry.updateQuorumConfig(validQuorum, operators); } function test_RegisterOperatorWithSignature() public { address operator3 = address(0x125); ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - registry.registerOperatorWithSignature(operator3, signature); + registry.registerOperatorWithSignature(operator3, signature, operator3); assertTrue(registry.operatorRegistered(operator3)); assertEq(registry.getLastCheckpointOperatorWeight(operator3), 1000); } - function test_RevertsWhen_AlreadyRegistered_RegisterOperatorWithSignature() public { + function test_RevertsWhen_AlreadyRegistered_RegisterOperatorWithSignature() + public + { assertEq(registry.getLastCheckpointOperatorWeight(operator1), 1000); assertEq(registry.getLastCheckpointTotalWeight(), 2000); ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.OperatorAlreadyRegistered.selector); - registry.registerOperatorWithSignature(operator1, signature); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.OperatorAlreadyRegistered.selector + ); + registry.registerOperatorWithSignature(operator1, signature, operator1); } - function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() public { + function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() + public + { bytes memory signatureData; vm.mockCall( address(mockServiceManager), @@ -212,7 +266,9 @@ function test_UpdateQuorumConfig() public { function test_RevertsWhen_NotOperator_DeregisterOperator() public { address notOperator = address(0x2); vm.prank(notOperator); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.OperatorNotRegistered.selector); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.OperatorNotRegistered.selector + ); registry.deregisterOperator(); } @@ -229,7 +285,6 @@ function test_UpdateQuorumConfig() public { operators[2] = operator3; registry.updateOperators(operators); assertEq(registry.getLastCheckpointOperatorWeight(operator3), 0); - } function test_When_SingleOperator_UpdateOperators() public { @@ -237,7 +292,9 @@ function test_UpdateQuorumConfig() public { operators[0] = operator1; registry.updateOperators(operators); - uint256 updatedWeight = registry.getLastCheckpointOperatorWeight(operator1); + uint256 updatedWeight = registry.getLastCheckpointOperatorWeight( + operator1 + ); assertEq(updatedWeight, 1000); } @@ -269,8 +326,12 @@ function test_UpdateQuorumConfig() public { registry.updateOperators(operators); - uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight(operator1); - uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight(operator2); + uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight( + operator1 + ); + uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight( + operator2 + ); assertEq(updatedWeight1, 1000); assertEq(updatedWeight2, 1000); } @@ -282,7 +343,9 @@ function test_UpdateQuorumConfig() public { registry.updateOperators(operators); - uint256 updatedWeight = registry.getLastCheckpointOperatorWeight(operator1); + uint256 updatedWeight = registry.getLastCheckpointOperatorWeight( + operator1 + ); assertEq(updatedWeight, 1000); } @@ -291,8 +354,14 @@ function test_UpdateQuorumConfig() public { IStrategy mockStrategy2 = IStrategy(address(421)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 5_000}); - quorum.strategies[1] = StrategyParams({strategy: mockStrategy2, multiplier: 5_000}); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 5_000 + }); + quorum.strategies[1] = StrategyParams({ + strategy: mockStrategy2, + multiplier: 5_000 + }); address[] memory operators = new address[](2); operators[0] = operator1; @@ -302,20 +371,28 @@ function test_UpdateQuorumConfig() public { address[] memory strategies = new address[](2); uint256[] memory shares = new uint256[](2); - strategies[0]=address(mockStrategy); - strategies[1]=address(mockStrategy2); + strategies[0] = address(mockStrategy); + strategies[1] = address(mockStrategy2); shares[0] = 50; shares[1] = 1000; vm.mockCall( address(mockDelegationManager), - abi.encodeWithSelector(MockDelegationManager.getOperatorShares.selector, operator1, strategies), + abi.encodeWithSelector( + MockDelegationManager.getOperatorShares.selector, + operator1, + strategies + ), abi.encode(shares) ); registry.updateOperators(operators); - uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight(operator1); - uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight(operator2); + uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight( + operator1 + ); + uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight( + operator2 + ); assertEq(updatedWeight1, 525); assertEq(updatedWeight2, 1000); vm.roll(block.number + 1); @@ -327,7 +404,7 @@ function test_UpdateQuorumConfig() public { assertEq(initialMinimumWeight, 0); // Assuming initial state is 0 - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; registry.updateMinimumWeight(newMinimumWeight, operators); @@ -338,7 +415,7 @@ function test_UpdateQuorumConfig() public { function test_RevertsWhen_NotOwner_UpdateMinimumWeight() public { uint256 newMinimumWeight = 5000; - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; vm.prank(address(0xBEEF)); // An arbitrary non-owner address @@ -348,7 +425,7 @@ function test_UpdateQuorumConfig() public { function test_When_SameWeight_UpdateMinimumWeight() public { uint256 initialMinimumWeight = 5000; - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; registry.updateMinimumWeight(initialMinimumWeight, operators); @@ -359,7 +436,7 @@ function test_UpdateQuorumConfig() public { function test_When_Weight0_UpdateMinimumWeight() public { uint256 initialMinimumWeight = 5000; - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; registry.updateMinimumWeight(initialMinimumWeight, operators); @@ -371,6 +448,7 @@ function test_UpdateQuorumConfig() public { uint256 updatedMinimumWeight = registry.minimumWeight(); assertEq(updatedMinimumWeight, newMinimumWeight); } + function testUpdateThresholdStake_UpdateThresholdStake() public { uint256 thresholdWeight = 10000000000; vm.prank(registry.owner()); @@ -384,6 +462,7 @@ function test_UpdateQuorumConfig() public { vm.expectRevert("Ownable: caller is not the owner"); registry.updateStakeThreshold(thresholdWeight); } + function test_CheckSignatures() public { msgHash = keccak256("data"); signers = new address[](2); @@ -394,7 +473,10 @@ function test_UpdateQuorumConfig() public { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevertsWhen_LengthMismatch_CheckSignatures() public { @@ -405,8 +487,13 @@ function test_UpdateQuorumConfig() public { (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator1Pk, msgHash); signatures[0] = abi.encode(v, r, s); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector + ); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevertsWhen_InvalidLength_CheckSignatures() public { @@ -414,8 +501,13 @@ function test_UpdateQuorumConfig() public { address[] memory signers = new address[](0); bytes[] memory signatures = new bytes[](0); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevertsWhen_NotSorted_CheckSignatures() public { @@ -430,14 +522,17 @@ function test_UpdateQuorumConfig() public { signatures[0] = abi.encodePacked(r, s, v); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevertsWhen_Duplicates_CheckSignatures() public { msgHash = keccak256("data"); signers = new address[](2); - signers[1]=operator1; - signers[0]=operator1; + signers[1] = operator1; + signers[0] = operator1; /// Duplicate assertEq(signers[0], signers[1]); @@ -448,7 +543,10 @@ function test_UpdateQuorumConfig() public { signatures[1] = abi.encodePacked(r, s, v); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevetsWhen_InvalidSignature_CheckSignatures() public { @@ -458,8 +556,13 @@ function test_UpdateQuorumConfig() public { bytes[] memory signatures = new bytes[](1); signatures[0] = "invalid-signature"; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidSignature.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidSignature.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevertsWhen_InsufficientSignedStake_CheckSignatures() public { @@ -480,12 +583,20 @@ function test_UpdateQuorumConfig() public { vm.mockCall( address(registry), - abi.encodeWithSelector(ECDSAStakeRegistry.getLastCheckpointOperatorWeight.selector, operator1), + abi.encodeWithSelector( + ECDSAStakeRegistry.getLastCheckpointOperatorWeight.selector, + operator1 + ), abi.encode(50) ); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector + ); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, type(uint32).max) + ); } function test_RevertsWhen_LengthMismatch_CheckSignaturesAtBlock() public { @@ -496,8 +607,13 @@ function test_UpdateQuorumConfig() public { signers[1] = operator2; bytes[] memory signatures = new bytes[](1); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, referenceBlock)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, referenceBlock) + ); } function test_RevertsWhen_InvalidLength_CheckSignaturesAtBlock() public { @@ -506,8 +622,13 @@ function test_UpdateQuorumConfig() public { address[] memory signers = new address[](0); bytes[] memory signatures = new bytes[](0); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, referenceBlock)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, referenceBlock) + ); } function test_RevertsWhen_NotSorted_CheckSignaturesAtBlock() public { @@ -524,10 +645,15 @@ function test_UpdateQuorumConfig() public { signatures[0] = abi.encodePacked(r, s, v); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, referenceBlock)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, referenceBlock) + ); } - function test_RevetsWhen_InsufficientSignedStake_CheckSignaturesAtBlock() public { + function test_RevetsWhen_InsufficientSignedStake_CheckSignaturesAtBlock() + public + { uint32 referenceBlock = 123; msgHash = keccak256("data"); signers = new address[](2); @@ -546,12 +672,21 @@ function test_UpdateQuorumConfig() public { vm.mockCall( address(registry), - abi.encodeWithSelector(ECDSAStakeRegistry.getOperatorWeightAtBlock.selector, operator1, referenceBlock), + abi.encodeWithSelector( + ECDSAStakeRegistry.getOperatorWeightAtBlock.selector, + operator1, + referenceBlock + ), abi.encode(50) ); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, referenceBlock)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector + ); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, referenceBlock) + ); } function test_Gas_UpdateOperators() public { @@ -564,14 +699,18 @@ function test_UpdateQuorumConfig() public { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; address[] memory operators = new address[](30); - for (uint256 i; i< operators.length;i++) { - operators[i]=address(uint160(i)); - registry.registerOperatorWithSignature(operators[i], operatorSignature); + for (uint256 i; i < operators.length; i++) { + operators[i] = address(uint160(i)); + registry.registerOperatorWithSignature( + operators[i], + operatorSignature, + operators[i] + ); } vm.resumeGasMetering(); registry.updateOperators(operators); - emit log_named_uint("Gas consumed",before - gasleft()); + emit log_named_uint("Gas consumed", before - gasleft()); } function test_Gas_CheckSignatures() public { @@ -589,23 +728,36 @@ function test_UpdateQuorumConfig() public { uint8 v; bytes32 r; bytes32 s; - for (uint256 i=1; i< operators.length+1;i++) { - operators[i-1]=address(vm.addr(i)); - registry.registerOperatorWithSignature(operators[i-1], operatorSignature); + for (uint256 i = 1; i < operators.length + 1; i++) { + operators[i - 1] = address(vm.addr(i)); + registry.registerOperatorWithSignature( + operators[i - 1], + operatorSignature, + operators[i - 1] + ); (v, r, s) = vm.sign(i, msgHash); - signatures[i-1] = abi.encodePacked(r, s, v); + signatures[i - 1] = abi.encodePacked(r, s, v); } (operators, signatures) = _sort(operators, signatures); registry.updateOperators(operators); vm.resumeGasMetering(); - registry.isValidSignature(msgHash, abi.encode(operators, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(operators, signatures, type(uint32).max) + ); - emit log_named_uint("Gas consumed",before - gasleft()); + emit log_named_uint("Gas consumed", before - gasleft()); } - function _sort(address[] memory operators, bytes[] memory signatures) internal pure returns (address[] memory, bytes[] memory) { - require(operators.length == signatures.length, "Operators and signatures length mismatch"); - + function _sort( + address[] memory operators, + bytes[] memory signatures + ) internal pure returns (address[] memory, bytes[] memory) { + require( + operators.length == signatures.length, + "Operators and signatures length mismatch" + ); + uint256 length = operators.length; for (uint256 i = 0; i < length - 1; i++) { uint256 minIndex = i; From 528243161eb510bd1f09fc54898585cf6adb478c Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 14 May 2024 07:52:22 -0400 Subject: [PATCH 03/13] fix: frontrunning with different signing key --- src/unaudited/ECDSAStakeRegistry.sol | 4 +--- .../ECDSAStakeRegistryEqualWeightUnit.t.sol | 6 ++--- .../ECDSAStakeRegistryPermissionedUnit.t.sol | 10 ++++---- test/unit/ECDSAStakeRegistryUnit.t.sol | 24 ++++++++----------- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index 5ff228f7..0920c275 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -44,15 +44,13 @@ contract ECDSAStakeRegistry is } /// @notice Registers a new operator using a provided signature and signing key - /// @param _operator The address of the operator to register /// @param _operatorSignature Contains the operator's signature, salt, and expiry /// @param _signingKey The signing key to add to the operator's history function registerOperatorWithSignature( - address _operator, ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, address _signingKey ) external { - _registerOperatorWithSig(_operator, _operatorSignature, _signingKey); + _registerOperatorWithSig(msg.sender, _operatorSignature, _signingKey); } /// @notice Deregisters an existing operator diff --git a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index 9f56d08b..bc6337c5 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -32,13 +32,13 @@ contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { fixedWeightRegistry.permitOperator(operator1); fixedWeightRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + vm.prank(operator1); fixedWeightRegistry.registerOperatorWithSignature( - operator1, operatorSignature, operator1 ); + vm.prank(operator2); fixedWeightRegistry.registerOperatorWithSignature( - operator2, operatorSignature, operator2 ); @@ -71,8 +71,8 @@ contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { vm.roll(block.number + 1); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + vm.prank(operator1); fixedWeightRegistry.registerOperatorWithSignature( - operator1, operatorSignature, operator1 ); diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index cc63ac19..dffb9174 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -32,13 +32,13 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { permissionedRegistry.permitOperator(operator1); permissionedRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + vm.prank(operator1); permissionedRegistry.registerOperatorWithSignature( - operator1, operatorSignature, operator1 ); + vm.prank(operator2); permissionedRegistry.registerOperatorWithSignature( - operator2, operatorSignature, operator1 ); @@ -100,8 +100,8 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector ) ); + vm.prank(operator3); permissionedRegistry.registerOperatorWithSignature( - operator3, operatorSignature, operator3 ); @@ -111,8 +111,8 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address operator3 = address(0xBEEF); permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + vm.prank(operator3); permissionedRegistry.registerOperatorWithSignature( - operator3, operatorSignature, operator3 ); @@ -122,8 +122,8 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address operator3 = address(0xBEEF); permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + vm.prank(operator3); permissionedRegistry.registerOperatorWithSignature( - operator3, operatorSignature, operator3 ); diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index e175c681..357b1041 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -74,16 +74,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); registry.initialize(address(mockServiceManager), 100, quorum); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - registry.registerOperatorWithSignature( - operator1, - operatorSignature, - operator1 - ); - registry.registerOperatorWithSignature( - operator2, - operatorSignature, - operator2 - ); + vm.prank(operator1); + registry.registerOperatorWithSignature(operatorSignature, operator1); + vm.prank(operator2); + registry.registerOperatorWithSignature(operatorSignature, operator2); } function test_UpdateQuorumConfig() public { @@ -215,7 +209,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { function test_RegisterOperatorWithSignature() public { address operator3 = address(0x125); ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - registry.registerOperatorWithSignature(operator3, signature, operator3); + vm.prank(operator3); + registry.registerOperatorWithSignature(signature, operator3); assertTrue(registry.operatorRegistered(operator3)); assertEq(registry.getLastCheckpointOperatorWeight(operator3), 1000); } @@ -230,7 +225,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.expectRevert( ECDSAStakeRegistryEventsAndErrors.OperatorAlreadyRegistered.selector ); - registry.registerOperatorWithSignature(operator1, signature, operator1); + vm.prank(operator1); + registry.registerOperatorWithSignature(signature, operator1); } function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() @@ -701,8 +697,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address[] memory operators = new address[](30); for (uint256 i; i < operators.length; i++) { operators[i] = address(uint160(i)); + vm.prank(operators[i]); registry.registerOperatorWithSignature( - operators[i], operatorSignature, operators[i] ); @@ -730,8 +726,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { bytes32 s; for (uint256 i = 1; i < operators.length + 1; i++) { operators[i - 1] = address(vm.addr(i)); + vm.prank(operators[i - 1]); registry.registerOperatorWithSignature( - operators[i - 1], operatorSignature, operators[i - 1] ); From f4990694007d0d85e9f1e50b14f865d36749e063 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 May 2024 10:12:32 -0400 Subject: [PATCH 04/13] test: verify the checkpoint logic for the signing keys --- src/unaudited/ECDSAStakeRegistry.sol | 31 ++++++ test/unit/ECDSAStakeRegistryUnit.t.sol | 144 +++++++++++++++++++++++++ 2 files changed, 175 insertions(+) diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index 0920c275..f5ffbba5 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -140,6 +140,37 @@ contract ECDSAStakeRegistry is return _quorum; } + /** + * @notice Retrieves the latest signing key for a given operator. + * @param _operator The address of the operator. + * @return The latest signing key of the operator. + */ + function getLastestOperatorSigningKey( + address _operator + ) external view returns (address) { + return address(uint160(_operatorSigningKeyHistory[_operator].latest())); + } + + /** + * @notice Retrieves the latest signing key for a given operator at a specific block number. + * @param _operator The address of the operator. + * @param _blockNumber The block number to get the operator's signing key. + * @return The signing key of the operator at the given block. + */ + function getLastestOperatorSigningKeyAtBlock( + address _operator, + uint256 _blockNumber + ) external view returns (address) { + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _blockNumber + ) + ) + ); + } + /// @notice Retrieves the last recorded weight for a given operator. /// @param _operator The address of the operator. /// @return uint256 - The latest weight of the operator. diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 357b1041..6e9fc32d 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -775,4 +775,148 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } return (operators, signatures); } + + // Define private and public keys for operator3 and signer + uint256 private operator3Pk = 3; + address private operator3 = address(vm.addr(operator3Pk)); + uint256 private signerPk = 4; + address private signer = address(vm.addr(signerPk)); + + function test_SuccessfulRegistrationOfDifferentSigningKey() public { + address operator = operator3; + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with a different signing key + vm.prank(operator); + registry.registerOperatorWithSignature(operatorSignature, signer); + + // Verify that the signing key has been successfully registered for the operator + address registeredSigningKey = registry.getLastestOperatorSigningKey( + operator + ); + assertEq( + registeredSigningKey, + signer, + "The registered signing key does not match the provided signing key" + ); + } + + function test_SuccessfulUseOfRegisteredSigningKeyInCheckSignatures() + public + { + address operator = operator3; + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with a different signing key + vm.prank(operator); + registry.registerOperatorWithSignature(operatorSignature, signer); + + // Prepare data for signature + bytes32 dataHash = keccak256("data"); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + + // Generate signature using the signing key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Check signatures using the registered signing key + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, type(uint32).max) + ); + } + + function test_SuccessfulUseOfUpdatedRegisteredSigningKeyInCheckSignaturesWithBlockIncrease() + public + { + address operator = operator3; + address initialSigningKey = address(vm.addr(signerPk)); + address updatedSigningKey = address(vm.addr(signerPk + 1)); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with the initial signing key + vm.prank(operator); + registry.registerOperatorWithSignature( + operatorSignature, + initialSigningKey + ); + + // Prepare data for signature with initial signing key + bytes32 dataHash = keccak256("data"); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + + // Generate signature using the initial signing key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Check signatures using the initial registered signing key + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, type(uint32).max) + ); + + // Increase block number + vm.roll(block.number + 10); + + // Update operator's signing key + vm.prank(operator); + registry.updateOperatorSigningKey(updatedSigningKey); + + // Generate signature using the updated signing key + (v, r, s) = vm.sign(signerPk + 1, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Check signatures using the updated registered signing key + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, type(uint32).max) + ); + } + + function test_SuccessfulUseOfRegisteredSigningKeyInCheckSignaturesWithBlockIncreaseAndPreviousKeyCheck() + public + { + address operator = operator3; + address initialSigningKey = address(vm.addr(signerPk)); + address updatedSigningKey = address(vm.addr(signerPk + 1)); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with the initial signing key + vm.prank(operator); + registry.registerOperatorWithSignature( + operatorSignature, + initialSigningKey + ); + + // Prepare data for signature with initial signing key + bytes32 dataHash = keccak256("data"); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + + // Generate signature using the initial signing key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Increase block number + vm.roll(block.number + 10); + + // Update operator's signing key + vm.prank(operator); + registry.updateOperatorSigningKey(updatedSigningKey); + + // Check signatures using the initial registered signing key at the previous block + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, block.number - 10) + ); + } } From af090b23cda2b3dff4330f14ee689788be2617d6 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 May 2024 17:08:27 -0400 Subject: [PATCH 05/13] feat: improve event for signing key update --- src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol | 8 +++++--- src/unaudited/ECDSAStakeRegistry.sol | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 53ded372..1c4aa3c2 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -61,12 +61,14 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Emitted when an operator's signing key is updated /// @param operator The address of the operator whose signing key was updated - /// @param oldSigningKey The operator's signing key before the update + /// @param updateBlock The block number at which the signing key was updated /// @param newSigningKey The operator's signing key after the update + /// @param oldSigningKey The operator's signing key before the update event SigningKeyUpdate( address indexed operator, - address indexed oldSigningKey, - address indexed newSigningKey + uint256 indexed updateBlock, + address indexed newSigningKey, + address oldSigningKey ); /// @notice Indicates when the lengths of the signers array and signatures array do not match. error LengthMismatch(); diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index f5ffbba5..7181a9fa 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -391,7 +391,12 @@ contract ECDSAStakeRegistry is return; } _operatorSigningKeyHistory[_operator].push(uint160(_newSigningKey)); - emit SigningKeyUpdate(_operator, oldSigningKey, _newSigningKey); + emit SigningKeyUpdate( + _operator, + block.number, + _newSigningKey, + oldSigningKey + ); } /// @notice Updates the weight of an operator and returns the previous and current weights. From 06cf3bd3f2038d425ac97d6269e739c0d9ac1895 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 May 2024 17:17:16 -0400 Subject: [PATCH 06/13] chore: clean up test function names and order functions --- test/unit/ECDSAStakeRegistryUnit.t.sol | 76 ++++++++++++-------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 6e9fc32d..653cc03c 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -745,44 +745,13 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { emit log_named_uint("Gas consumed", before - gasleft()); } - function _sort( - address[] memory operators, - bytes[] memory signatures - ) internal pure returns (address[] memory, bytes[] memory) { - require( - operators.length == signatures.length, - "Operators and signatures length mismatch" - ); - - uint256 length = operators.length; - for (uint256 i = 0; i < length - 1; i++) { - uint256 minIndex = i; - for (uint256 j = i + 1; j < length; j++) { - if (operators[j] < operators[minIndex]) { - minIndex = j; - } - } - if (minIndex != i) { - // Swap operators - address tempOperator = operators[i]; - operators[i] = operators[minIndex]; - operators[minIndex] = tempOperator; - // Swap corresponding signatures - bytes memory tempSignature = signatures[i]; - signatures[i] = signatures[minIndex]; - signatures[minIndex] = tempSignature; - } - } - return (operators, signatures); - } - // Define private and public keys for operator3 and signer uint256 private operator3Pk = 3; address private operator3 = address(vm.addr(operator3Pk)); uint256 private signerPk = 4; address private signer = address(vm.addr(signerPk)); - function test_SuccessfulRegistrationOfDifferentSigningKey() public { + function test_WhenUsingSigningKey_RegierOperatorWithSignature() public { address operator = operator3; ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; @@ -802,9 +771,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); } - function test_SuccessfulUseOfRegisteredSigningKeyInCheckSignatures() - public - { + function test_WhenUsingSigningKey_CheckSignatures() public { address operator = operator3; ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; @@ -830,9 +797,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); } - function test_SuccessfulUseOfUpdatedRegisteredSigningKeyInCheckSignaturesWithBlockIncrease() - public - { + function test_WhenUsingSigningKey_CheckSignaturesAtBlock() public { address operator = operator3; address initialSigningKey = address(vm.addr(signerPk)); address updatedSigningKey = address(vm.addr(signerPk + 1)); @@ -880,9 +845,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); } - function test_SuccessfulUseOfRegisteredSigningKeyInCheckSignaturesWithBlockIncreaseAndPreviousKeyCheck() - public - { + function test_WhenUsingPriorSigningKey_CheckSignaturesAtBlock() public { address operator = operator3; address initialSigningKey = address(vm.addr(signerPk)); address updatedSigningKey = address(vm.addr(signerPk + 1)); @@ -919,4 +882,35 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { abi.encode(operators, signatures, block.number - 10) ); } + + function _sort( + address[] memory operators, + bytes[] memory signatures + ) internal pure returns (address[] memory, bytes[] memory) { + require( + operators.length == signatures.length, + "Operators and signatures length mismatch" + ); + + uint256 length = operators.length; + for (uint256 i = 0; i < length - 1; i++) { + uint256 minIndex = i; + for (uint256 j = i + 1; j < length; j++) { + if (operators[j] < operators[minIndex]) { + minIndex = j; + } + } + if (minIndex != i) { + // Swap operators + address tempOperator = operators[i]; + operators[i] = operators[minIndex]; + operators[minIndex] = tempOperator; + // Swap corresponding signatures + bytes memory tempSignature = signatures[i]; + signatures[i] = signatures[minIndex]; + signatures[minIndex] = tempSignature; + } + } + return (operators, signatures); + } } From 7a890614d8b22be9402f3260cabaf3fd3839f0b4 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 22 May 2024 11:26:22 -0400 Subject: [PATCH 07/13] fix: storage layout gap --- src/unaudited/ECDSAStakeRegistryStorage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index 1d5589e4..c285999c 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -54,5 +54,5 @@ abstract contract ECDSAStakeRegistryStorage is // slither-disable-next-line shadowing-state /// @dev Reserves storage slots for future upgrades // solhint-disable-next-line - uint256[42] private __gap; + uint256[39] private __gap; } From f6942c73880c3ce5441ff47b4abc3561d2a70722 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 22 May 2024 11:26:38 -0400 Subject: [PATCH 08/13] feat: prevent signing at current block --- .../IECDSAStakeRegistryEventsAndErrors.sol | 3 + src/unaudited/ECDSAStakeRegistry.sol | 45 ++++++------- test/unit/ECDSAStakeRegistryUnit.t.sol | 65 +++++++++++++++---- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 1c4aa3c2..445db814 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -85,6 +85,9 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Thrown when missing operators in an update error MustUpdateAllOperators(); + /// @notice Reference blocks must be for blocks that have already been confirmed + error InvalidReferenceBlock(); + /// @notice Indicates operator weights were out of sync and the signed weight exceed the total error InvalidSignedWeight(); diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index 7181a9fa..061da1db 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -553,21 +553,17 @@ contract ECDSAStakeRegistry is address _operator, uint32 _referenceBlock ) internal view returns (address) { - if (_referenceBlock == type(uint32).max) { - return - address( - uint160(_operatorSigningKeyHistory[_operator].latest()) - ); - } else { - return - address( - uint160( - _operatorSigningKeyHistory[_operator].getAtBlock( - _referenceBlock - ) - ) - ); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _referenceBlock + ) + ) + ); } /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. @@ -578,11 +574,10 @@ contract ECDSAStakeRegistry is address _signer, uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _operatorWeightHistory[_signer].latest(); - } else { - return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); } /// @notice Retrieve the total stake weight at a specific block or the latest if not specified. @@ -592,11 +587,10 @@ contract ECDSAStakeRegistry is function _getTotalWeight( uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _totalWeightHistory.latest(); - } else { - return _totalWeightHistory.getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _totalWeightHistory.getAtBlock(_referenceBlock); } /// @notice Retrieves the threshold stake for a given reference block. @@ -606,11 +600,10 @@ contract ECDSAStakeRegistry is function _getThresholdStake( uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _thresholdWeightHistory.latest(); - } else { - return _thresholdWeightHistory.getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _thresholdWeightHistory.getAtBlock(_referenceBlock); } /// @notice Validates that the cumulative stake of signed messages meets or exceeds the required threshold. diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 653cc03c..74e3cd76 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -78,6 +78,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.registerOperatorWithSignature(operatorSignature, operator1); vm.prank(operator2); registry.registerOperatorWithSignature(operatorSignature, operator2); + vm.roll(block.number + 1); } function test_UpdateQuorumConfig() public { @@ -471,7 +472,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.isValidSignature( msgHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -488,7 +489,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); registry.isValidSignature( msgHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -502,7 +503,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); registry.isValidSignature( dataHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -520,7 +521,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); registry.isValidSignature( msgHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -541,7 +542,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); registry.isValidSignature( msgHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -557,7 +558,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); registry.isValidSignature( dataHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -591,7 +592,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); registry.isValidSignature( msgHash, - abi.encode(signers, signatures, type(uint32).max) + abi.encode(signers, signatures, block.number - 1) ); } @@ -736,10 +737,12 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } (operators, signatures) = _sort(operators, signatures); registry.updateOperators(operators); + vm.roll(block.number + 1); vm.resumeGasMetering(); + registry.isValidSignature( msgHash, - abi.encode(operators, signatures, type(uint32).max) + abi.encode(operators, signatures, block.number - 1) ); emit log_named_uint("Gas consumed", before - gasleft()); @@ -771,6 +774,36 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); } + function test_Twice_RegierOperatorWithSignature() public { + address operator = operator3; + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with a different signing key + vm.prank(operator); + registry.registerOperatorWithSignature(operatorSignature, signer); + + /// Register a second time + vm.prank(operator); + registry.updateOperatorSigningKey(address(420)); + + // Verify that the signing key has been successfully registered for the operator + address registeredSigningKey = registry.getLastestOperatorSigningKey( + operator + ); + + vm.roll(block.number + 1); + registeredSigningKey = registry.getLastestOperatorSigningKeyAtBlock( + operator, + uint32(block.number - 1) + ); + assertEq( + registeredSigningKey, + address(420), + "The registered signing key does not match the provided signing key" + ); + } + function test_WhenUsingSigningKey_CheckSignatures() public { address operator = operator3; @@ -779,6 +812,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Register operator with a different signing key vm.prank(operator); registry.registerOperatorWithSignature(operatorSignature, signer); + vm.roll(block.number + 1); // Prepare data for signature bytes32 dataHash = keccak256("data"); @@ -793,7 +827,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Check signatures using the registered signing key registry.isValidSignature( dataHash, - abi.encode(operators, signatures, type(uint32).max) + abi.encode(operators, signatures, block.number - 1) ); } @@ -810,6 +844,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { operatorSignature, initialSigningKey ); + vm.roll(block.number + 1); // Prepare data for signature with initial signing key bytes32 dataHash = keccak256("data"); @@ -824,7 +859,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Check signatures using the initial registered signing key registry.isValidSignature( dataHash, - abi.encode(operators, signatures, type(uint32).max) + abi.encode(operators, signatures, block.number - 1) ); // Increase block number @@ -833,6 +868,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Update operator's signing key vm.prank(operator); registry.updateOperatorSigningKey(updatedSigningKey); + vm.roll(block.number + 1); // Generate signature using the updated signing key (v, r, s) = vm.sign(signerPk + 1, dataHash); @@ -841,7 +877,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Check signatures using the updated registered signing key registry.isValidSignature( dataHash, - abi.encode(operators, signatures, type(uint32).max) + abi.encode(operators, signatures, block.number - 1) ); } @@ -858,6 +894,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { operatorSignature, initialSigningKey ); + vm.roll(block.number + 1); // Prepare data for signature with initial signing key bytes32 dataHash = keccak256("data"); @@ -883,6 +920,12 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); } + function test_RevertsWhen_SigningCurrentBlock_IsValidSignature() public {} + + function test_RevertsWhen_SigningKeyNotValidAtBlock_IsValidSignature() + public + {} + function _sort( address[] memory operators, bytes[] memory signatures From b3c0372d9a863d36db944c33339e97d6a5bf704a Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 22 May 2024 17:25:21 -0400 Subject: [PATCH 09/13] test: add two more test cases for RBN --- test/unit/ECDSAStakeRegistryUnit.t.sol | 45 ++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 74e3cd76..07372f3d 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -920,11 +920,52 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); } - function test_RevertsWhen_SigningCurrentBlock_IsValidSignature() public {} + function test_RevertsWhen_SigningCurrentBlock_IsValidSignature() public { + address operator = operator1; + address signingKey = address(vm.addr(signerPk)); + bytes32 dataHash = keccak256(abi.encodePacked("test data")); + uint256 currentBlock = block.number; + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + bytes memory signature = abi.encodePacked(r, s, v); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + signatures[0] = signature; + + vm.expectRevert(abi.encodeWithSignature("InvalidReferenceBlock()")); + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, currentBlock) + ); + } function test_RevertsWhen_SigningKeyNotValidAtBlock_IsValidSignature() public - {} + { + address operator = operator1; + uint256 invalidSignerPk = signerPk + 1; + address updatedSigningKey = address(vm.addr(invalidSignerPk)); /// Different key to simulate invalid signing key + bytes32 dataHash = keccak256(abi.encodePacked("test data")); + uint256 referenceBlock = block.number; /// Past reference block where the signer update won't be valid + vm.roll(block.number + 1); + + vm.prank(operator); + registry.updateOperatorSigningKey(address(updatedSigningKey)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(invalidSignerPk, dataHash); + bytes memory signature = abi.encodePacked(r, s, v); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + signatures[0] = signature; + + vm.expectRevert(abi.encodeWithSignature("InvalidSignature()")); + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, referenceBlock) + ); + } function _sort( address[] memory operators, From 8854cf983444167b64d9e7a484c5a532e9e2b88a Mon Sep 17 00:00:00 2001 From: steven nevins Date: Wed, 22 May 2024 20:50:29 -0400 Subject: [PATCH 10/13] fix: typo in function signature --- src/unaudited/ECDSAStakeRegistry.sol | 4 +-- test/unit/ECDSAStakeRegistryUnit.t.sol | 36 ++++++++++++++------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index 061da1db..ab4bdbeb 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -157,7 +157,7 @@ contract ECDSAStakeRegistry is * @param _blockNumber The block number to get the operator's signing key. * @return The signing key of the operator at the given block. */ - function getLastestOperatorSigningKeyAtBlock( + function getOperatorSigningKeyAtBlock( address _operator, uint256 _blockNumber ) external view returns (address) { @@ -408,7 +408,7 @@ contract ECDSAStakeRegistry is uint256 newWeight; uint256 oldWeight = _operatorWeightHistory[_operator].latest(); if (!_operatorRegistered[_operator]) { - delta -= int(oldWeight); + delta -= int256(oldWeight); if (delta == 0) { return delta; } diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 07372f3d..13cf6ce9 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -67,7 +67,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); quorum.strategies[0] = StrategyParams({ strategy: mockStrategy, - multiplier: 10000 + multiplier: 10_000 }); registry = new ECDSAStakeRegistry( IDelegationManager(address(mockDelegationManager)) @@ -88,7 +88,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { Quorum memory newQuorum = Quorum({strategies: new StrategyParams[](1)}); newQuorum.strategies[0] = StrategyParams({ strategy: mockStrategy, - multiplier: 10000 + multiplier: 10_000 }); address[] memory operators = new address[](2); operators[0] = operator1; @@ -125,7 +125,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { }); validQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), - multiplier: 10000 + multiplier: 10_000 }); address[] memory operators = new address[](2); @@ -155,7 +155,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { }); validQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), - multiplier: 5_000 + multiplier: 5000 }); address[] memory operators = new address[](2); operators[0] = operator1; @@ -163,7 +163,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { validQuorum.strategies[1] = StrategyParams({ strategy: IStrategy(address(420)), - multiplier: 5_000 + multiplier: 5000 }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); registry.updateQuorumConfig(validQuorum, operators); @@ -175,7 +175,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { }); validQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), - multiplier: 5_000 + multiplier: 5000 }); address[] memory operators = new address[](2); operators[0] = operator1; @@ -183,7 +183,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { validQuorum.strategies[1] = StrategyParams({ strategy: IStrategy(address(419)), - multiplier: 5_000 + multiplier: 5000 }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); registry.updateQuorumConfig(validQuorum, operators); @@ -195,7 +195,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { }); validQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), - multiplier: 10001 + multiplier: 10_001 }); address[] memory operators = new address[](2); operators[0] = operator1; @@ -353,11 +353,11 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); quorum.strategies[0] = StrategyParams({ strategy: mockStrategy, - multiplier: 5_000 + multiplier: 5000 }); quorum.strategies[1] = StrategyParams({ strategy: mockStrategy2, - multiplier: 5_000 + multiplier: 5000 }); address[] memory operators = new address[](2); @@ -447,13 +447,13 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function testUpdateThresholdStake_UpdateThresholdStake() public { - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); } function test_RevertsWhen_NotOwner_UpdateThresholdStake() public { - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; address notOwner = address(0x123); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); @@ -573,7 +573,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); vm.roll(block.number + 1); @@ -662,7 +662,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); vm.roll(referenceBlock + 1); @@ -793,7 +793,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ); vm.roll(block.number + 1); - registeredSigningKey = registry.getLastestOperatorSigningKeyAtBlock( + registeredSigningKey = registry.getOperatorSigningKeyAtBlock( operator, uint32(block.number - 1) ); @@ -945,9 +945,11 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { { address operator = operator1; uint256 invalidSignerPk = signerPk + 1; - address updatedSigningKey = address(vm.addr(invalidSignerPk)); /// Different key to simulate invalid signing key + address updatedSigningKey = address(vm.addr(invalidSignerPk)); + /// Different key to simulate invalid signing key bytes32 dataHash = keccak256(abi.encodePacked("test data")); - uint256 referenceBlock = block.number; /// Past reference block where the signer update won't be valid + uint256 referenceBlock = block.number; + /// Past reference block where the signer update won't be valid vm.roll(block.number + 1); vm.prank(operator); From f2bfdc4a721792de94d3681ed85e1c9f9398a2d1 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 23 May 2024 11:12:20 -0400 Subject: [PATCH 11/13] chore: remove unnecessary test contract --- test/unit/ECDSAStakeRegistryUnit.t.sol | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 13cf6ce9..56833c8f 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -40,6 +40,7 @@ contract MockDelegationManager { contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { MockDelegationManager public mockDelegationManager; MockServiceManager public mockServiceManager; + ECDSAStakeRegistry public registry; address internal operator1; address internal operator2; uint256 internal operator1Pk; @@ -55,14 +56,6 @@ contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { (operator2, operator2Pk) = makeAddrAndKey("Signer 2"); mockDelegationManager = new MockDelegationManager(); mockServiceManager = new MockServiceManager(); - } -} - -contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { - ECDSAStakeRegistry public registry; - - function setUp() public virtual override { - super.setUp(); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); quorum.strategies[0] = StrategyParams({ @@ -80,7 +73,9 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.registerOperatorWithSignature(operatorSignature, operator2); vm.roll(block.number + 1); } +} +contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { function test_UpdateQuorumConfig() public { IStrategy mockStrategy = IStrategy(address(420)); From 8150eb80123dbaeb47feb613b4b91c690496fd7b Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 23 May 2024 11:19:16 -0400 Subject: [PATCH 12/13] fix: typo for invalid quorum --- test/unit/ECDSAStakeRegistryUnit.t.sol | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 56833c8f..a3626842 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -115,10 +115,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_NotOwner_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({ + Quorum memory invalidQuorum = Quorum({ strategies: new StrategyParams[](1) }); - validQuorum.strategies[0] = StrategyParams({ + invalidQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), multiplier: 10_000 }); @@ -131,7 +131,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.prank(nonOwner); vm.expectRevert("Ownable: caller is not the owner"); - registry.updateQuorumConfig(validQuorum, operators); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertsWhen_SameQuorum_UpdateQuorumConfig() public { @@ -145,10 +145,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertSWhen_Duplicate_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({ + Quorum memory invalidQuorum = Quorum({ strategies: new StrategyParams[](2) }); - validQuorum.strategies[0] = StrategyParams({ + invalidQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), multiplier: 5000 }); @@ -156,19 +156,19 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { operators[0] = operator1; operators[1] = operator2; - validQuorum.strategies[1] = StrategyParams({ + invalidQuorum.strategies[1] = StrategyParams({ strategy: IStrategy(address(420)), multiplier: 5000 }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.updateQuorumConfig(validQuorum, operators); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertSWhen_NotSorted_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({ + Quorum memory invalidQuorum = Quorum({ strategies: new StrategyParams[](2) }); - validQuorum.strategies[0] = StrategyParams({ + invalidQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), multiplier: 5000 }); @@ -176,19 +176,19 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { operators[0] = operator1; operators[1] = operator2; - validQuorum.strategies[1] = StrategyParams({ + invalidQuorum.strategies[1] = StrategyParams({ strategy: IStrategy(address(419)), multiplier: 5000 }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.updateQuorumConfig(validQuorum, operators); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertSWhen_OverMultiplierTotal_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({ + Quorum memory invalidQuorum = Quorum({ strategies: new StrategyParams[](1) }); - validQuorum.strategies[0] = StrategyParams({ + invalidQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), multiplier: 10_001 }); @@ -199,7 +199,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.expectRevert( ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector ); - registry.updateQuorumConfig(validQuorum, operators); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RegisterOperatorWithSignature() public { From 84535cf3c4828fe997bbdba0c7823a0b0610714e Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 23 May 2024 11:21:14 -0400 Subject: [PATCH 13/13] fix: invalidQuorum -> validQuorum for NotOwner test --- test/unit/ECDSAStakeRegistryUnit.t.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index a3626842..d374144d 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -115,10 +115,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_NotOwner_UpdateQuorumConfig() public { - Quorum memory invalidQuorum = Quorum({ + Quorum memory validQuorum = Quorum({ strategies: new StrategyParams[](1) }); - invalidQuorum.strategies[0] = StrategyParams({ + validQuorum.strategies[0] = StrategyParams({ strategy: IStrategy(address(420)), multiplier: 10_000 }); @@ -131,7 +131,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.prank(nonOwner); vm.expectRevert("Ownable: caller is not the owner"); - registry.updateQuorumConfig(invalidQuorum, operators); + registry.updateQuorumConfig(validQuorum, operators); } function test_RevertsWhen_SameQuorum_UpdateQuorumConfig() public {