diff --git a/mainnet-contracts/script/SetupAccess.s.sol b/mainnet-contracts/script/SetupAccess.s.sol index 10334034..2770233d 100644 --- a/mainnet-contracts/script/SetupAccess.s.sol +++ b/mainnet-contracts/script/SetupAccess.s.sol @@ -307,12 +307,14 @@ contract SetupAccess is BaseScript { function _setupPufferProtocolRoles() internal view returns (bytes[] memory) { bytes[] memory calldatas = new bytes[](3); - bytes4[] memory selectors = new bytes4[](5); + bytes4[] memory selectors = new bytes4[](7); selectors[0] = PufferProtocol.createPufferModule.selector; selectors[1] = PufferProtocol.setModuleWeights.selector; - selectors[2] = PufferProtocol.setValidatorLimitPerModule.selector; + selectors[2] = PufferProtocol.setBatchesLimitPerModule.selector; selectors[3] = PufferProtocol.changeMinimumVTAmount.selector; selectors[4] = PufferProtocol.setVTPenalty.selector; + selectors[5] = PufferProtocol.setPufferProtocolLogic.selector; + selectors[6] = PufferProtocol.setCurrentNumBatches.selector; calldatas[0] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, diff --git a/mainnet-contracts/src/PufferProtocol.sol b/mainnet-contracts/src/PufferProtocol.sol index 45a51c1a..35f90876 100644 --- a/mainnet-contracts/src/PufferProtocol.sol +++ b/mainnet-contracts/src/PufferProtocol.sol @@ -252,8 +252,8 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad /** * @dev Restricted to the DAO */ - function setValidatorLimitPerModule(bytes32 moduleName, uint128 limit) external restricted { - _setValidatorLimitPerModule(moduleName, limit); + function setBatchesLimitPerModule(bytes32 moduleName, uint128 limit) external restricted { + _setBatchesLimitPerModule(moduleName, limit); } /** @@ -270,6 +270,21 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad _setPufferProtocolLogic(newPufferProtocolLogic); } + /** + * @dev Restricted to the DAO + */ + function setCurrentNumBatches(bytes32[] calldata moduleNames, uint128[] calldata newCurrentNumBatches) + external + restricted + { + require(moduleNames.length == newCurrentNumBatches.length, InputArrayLengthMismatch()); + ProtocolStorage storage $ = _getPufferProtocolStorage(); + for (uint256 i = 0; i < moduleNames.length; i++) { + $.moduleLimits[moduleNames[i]].numberOfRegisteredBatches = newCurrentNumBatches[i]; + } + emit CurrentNumBatchesSet(moduleNames, newCurrentNumBatches); + } + /** * @inheritdoc IPufferProtocol */ @@ -440,9 +455,9 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad */ function revertIfPaused() external restricted { } - function _setValidatorLimitPerModule(bytes32 moduleName, uint128 limit) internal { + function _setBatchesLimitPerModule(bytes32 moduleName, uint128 limit) internal { ProtocolStorage storage $ = _getPufferProtocolStorage(); - require($.moduleLimits[moduleName].numberOfRegisteredValidators <= limit, ValidatorLimitForModuleReached()); + require($.moduleLimits[moduleName].numberOfRegisteredBatches <= limit, NumBatchesLimitForModuleReached()); emit ValidatorLimitPerModuleChanged($.moduleLimits[moduleName].allowedLimit, limit); $.moduleLimits[moduleName].allowedLimit = limit; } @@ -468,7 +483,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad $.moduleWeights.push(moduleName); bytes32 withdrawalCredentials = bytes32(module.getWithdrawalCredentials()); emit NewPufferModuleCreated(address(module), moduleName, withdrawalCredentials); - _setValidatorLimitPerModule(moduleName, 500); + _setBatchesLimitPerModule(moduleName, _DEFAULT_MAX_BATCHES_PER_MODULE); return address(module); } diff --git a/mainnet-contracts/src/PufferProtocolBase.sol b/mainnet-contracts/src/PufferProtocolBase.sol index 40a3d8fe..72b05232 100644 --- a/mainnet-contracts/src/PufferProtocolBase.sol +++ b/mainnet-contracts/src/PufferProtocolBase.sol @@ -39,9 +39,9 @@ abstract contract PufferProtocolBase is PufferProtocolStorage, ProtocolSignature /** * @notice Thrown when the new validators tires to register to a module, but the validator limit for that module is already reached - * @dev Signature "0xb75c5781" + * @dev Signature "0x7cb4ca32" */ - error ValidatorLimitForModuleReached(); + error NumBatchesLimitForModuleReached(); /** * @notice Thrown when the BLS public key is not valid @@ -158,6 +158,11 @@ abstract contract PufferProtocolBase is PufferProtocolStorage, ProtocolSignature */ uint256 internal constant _32_ETH_GWEI = 32 * 10 ** 9; + /** + * @dev Default maximum number of batches per module + */ + uint128 internal constant _DEFAULT_MAX_BATCHES_PER_MODULE = 500; + IGuardianModule internal immutable _GUARDIAN_MODULE; ValidatorTicket internal immutable _VALIDATOR_TICKET; diff --git a/mainnet-contracts/src/PufferProtocolLogic.sol b/mainnet-contracts/src/PufferProtocolLogic.sol index b56669bd..8458f137 100644 --- a/mainnet-contracts/src/PufferProtocolLogic.sol +++ b/mainnet-contracts/src/PufferProtocolLogic.sol @@ -196,12 +196,12 @@ contract PufferProtocolLogic is PufferProtocolBase, IPufferProtocolLogic { $.nodeOperatorInfo[msg.sender].validationTime += (msg.value - bondAmountEth); ++$.nodeOperatorInfo[msg.sender].pendingValidatorCount; ++$.pendingValidatorIndices[moduleName]; - ++$.moduleLimits[moduleName].numberOfRegisteredValidators; + $.moduleLimits[moduleName].numberOfRegisteredBatches += numBatches; } - emit NumberOfRegisteredValidatorsChanged({ + emit NumberOfRegisteredBatchesChanged({ moduleName: moduleName, - newNumberOfRegisteredValidators: $.moduleLimits[moduleName].numberOfRegisteredValidators + numberOfRegisteredBatches: $.moduleLimits[moduleName].numberOfRegisteredBatches }); emit ValidatorKeyRegistered({ pubKey: data.blsPubKey, @@ -283,7 +283,7 @@ contract PufferProtocolLogic is PufferProtocolBase, IPufferProtocolLogic { // slither-disable-next-line unchecked-transfer _PUFFER_VAULT.transfer(node, $.validators[moduleName][skippedIndex].bond); - _decreaseNumberOfRegisteredValidators($, moduleName); + _decreaseNumberOfRegisteredBatches($, moduleName, $.validators[moduleName][skippedIndex].numBatches); unchecked { ++$.nextToBeProvisioned[moduleName]; } @@ -585,7 +585,7 @@ contract PufferProtocolLogic is PufferProtocolBase, IPufferProtocolLogic { bondBurnAmount = _getBondBurnAmount({ validatorInfo: validatorInfo, validatorBondAmount: bondAmount, - numBatches: validator.numBatches + numBatches: numBatches }); emit ValidatorExited({ @@ -597,7 +597,7 @@ contract PufferProtocolLogic is PufferProtocolBase, IPufferProtocolLogic { }); // Decrease the number of registered validators for that module - _decreaseNumberOfRegisteredValidators($, validatorInfo.moduleName); + _decreaseNumberOfRegisteredBatches($, validatorInfo.moduleName, validator.numBatches); // Storage VT and the active validator count update for the Node Operator // nosemgrep basic-arithmetic-underflow @@ -611,9 +611,11 @@ contract PufferProtocolLogic is PufferProtocolBase, IPufferProtocolLogic { return (bondBurnAmount, bondAmount - bondBurnAmount, numBatches); } - function _decreaseNumberOfRegisteredValidators(ProtocolStorage storage $, bytes32 moduleName) internal { - --$.moduleLimits[moduleName].numberOfRegisteredValidators; - emit NumberOfRegisteredValidatorsChanged(moduleName, $.moduleLimits[moduleName].numberOfRegisteredValidators); + function _decreaseNumberOfRegisteredBatches(ProtocolStorage storage $, bytes32 moduleName, uint128 numBatches) + internal + { + $.moduleLimits[moduleName].numberOfRegisteredBatches -= numBatches; + emit NumberOfRegisteredBatchesChanged(moduleName, $.moduleLimits[moduleName].numberOfRegisteredBatches); } function _getBondBurnAmount( @@ -648,10 +650,11 @@ contract PufferProtocolLogic is PufferProtocolBase, IPufferProtocolLogic { require(0 < data.numBatches && data.numBatches < 65, InvalidNumberOfBatches()); // This acts as a validation if the module is existent - // +1 is to validate the current transaction registration + // +numBatches is to validate the current transaction registration require( - ($.moduleLimits[moduleName].numberOfRegisteredValidators + 1) <= $.moduleLimits[moduleName].allowedLimit, - ValidatorLimitForModuleReached() + ($.moduleLimits[moduleName].numberOfRegisteredBatches + data.numBatches) + <= $.moduleLimits[moduleName].allowedLimit, + NumBatchesLimitForModuleReached() ); require(data.blsPubKey.length == _BLS_PUB_KEY_LENGTH, InvalidBLSPubKey()); diff --git a/mainnet-contracts/src/interface/IPufferProtocolEvents.sol b/mainnet-contracts/src/interface/IPufferProtocolEvents.sol index b52ddb86..e65c6eb2 100644 --- a/mainnet-contracts/src/interface/IPufferProtocolEvents.sol +++ b/mainnet-contracts/src/interface/IPufferProtocolEvents.sol @@ -8,10 +8,10 @@ pragma solidity >=0.8.0 <0.9.0; */ interface IPufferProtocolEvents { /** - * @notice Emitted when the number of active validators changes - * @dev Signature "0xc06afc2b3c88873a9be580de9bbbcc7fea3027ef0c25fd75d5411ed3195abcec" + * @notice Emitted when the number of active batches changes + * @dev Signature "0xe0be9dc31510a09f13dba14d047e637a5bee5976426ec076ebf3aa3884099d5e" */ - event NumberOfRegisteredValidatorsChanged(bytes32 indexed moduleName, uint256 newNumberOfRegisteredValidators); + event NumberOfRegisteredBatchesChanged(bytes32 indexed moduleName, uint256 numberOfRegisteredBatches); /** * @notice Emitted when the validation time is deposited @@ -158,4 +158,10 @@ interface IPufferProtocolEvents { * @dev Signature "0xe271f36954242c619ce9d0f727a7d3b5f4db04666752aaeb20bca6d52098792a" */ event PufferProtocolLogicSet(address oldPufferProtocolLogic, address newPufferProtocolLogic); + + /** + * @notice Emitted when the current number of batches is set + * @dev Signature "0x4d193bcf314540fd194820a44042b5d498e3f07ab578fcd422f1023a4123f32b" + */ + event CurrentNumBatchesSet(bytes32[] moduleNames, uint128[] newCurrentNumBatches); } diff --git a/mainnet-contracts/src/interface/IPufferProtocolManagement.sol b/mainnet-contracts/src/interface/IPufferProtocolManagement.sol index a85a9dba..cd28994d 100644 --- a/mainnet-contracts/src/interface/IPufferProtocolManagement.sol +++ b/mainnet-contracts/src/interface/IPufferProtocolManagement.sol @@ -8,27 +8,48 @@ pragma solidity >=0.8.0 <0.9.0; */ interface IPufferProtocolManagement { /** + * @notice Change the minimum amount of VT that can be deposited + * @param newMinimumVTAmount The new minimum amount of VT that can be deposited * @dev Restricted to the DAO */ function changeMinimumVTAmount(uint256 newMinimumVTAmount) external; /** + * @notice Change the weights of the modules + * @param newModuleWeights The new weights of the modules * @dev Restricted to the DAO */ function setModuleWeights(bytes32[] calldata newModuleWeights) external; /** + * @notice Change the number of batches that can be registered for a module + * @param moduleName The name of the module + * @param limit The new number of batches that can be registered for a module * @dev Restricted to the DAO */ - function setValidatorLimitPerModule(bytes32 moduleName, uint128 limit) external; + function setBatchesLimitPerModule(bytes32 moduleName, uint128 limit) external; /** + * @notice Change the penalty amount for a module + * @param newPenaltyAmount The new penalty amount for a module * @dev Restricted to the DAO */ function setVTPenalty(uint256 newPenaltyAmount) external; /** + * @notice Change the PufferProtocolLogic contract + * @param newPufferProtocolLogic The new PufferProtocolLogic contract * @dev Restricted to the DAO */ function setPufferProtocolLogic(address newPufferProtocolLogic) external; + + /** + * @notice Set the number of batches that are currently active for a module + * @param moduleNames The names of the modules + * @param newCurrentNumBatches The new number of batches that are currently active for a module + * @dev Restricted to the DAO + * @dev This function should only be called once, after the PufferProtocol contract is upgraded to the Pectra version. + * This is because the limit used to be the number of validators, and now it is the number of batches. + */ + function setCurrentNumBatches(bytes32[] calldata moduleNames, uint128[] calldata newCurrentNumBatches) external; } diff --git a/mainnet-contracts/src/struct/ProtocolStorage.sol b/mainnet-contracts/src/struct/ProtocolStorage.sol index a1671f09..dbfe7cf2 100644 --- a/mainnet-contracts/src/struct/ProtocolStorage.sol +++ b/mainnet-contracts/src/struct/ProtocolStorage.sol @@ -75,5 +75,5 @@ struct ProtocolStorage { struct ModuleLimit { uint128 allowedLimit; - uint128 numberOfRegisteredValidators; + uint128 numberOfRegisteredBatches; } diff --git a/mainnet-contracts/test/unit/PufferProtocol.t.sol b/mainnet-contracts/test/unit/PufferProtocol.t.sol index 3e88c036..4d472f66 100644 --- a/mainnet-contracts/test/unit/PufferProtocol.t.sol +++ b/mainnet-contracts/test/unit/PufferProtocol.t.sol @@ -181,7 +181,7 @@ contract PufferProtocolTest is UnitTestHelper { ModuleLimit memory moduleLimit = pufferProtocol.getModuleLimitInformation(PUFFER_MODULE_0); - assertEq(moduleLimit.numberOfRegisteredValidators, 2, "2 active validators"); + assertEq(moduleLimit.numberOfRegisteredBatches, 2, "2 active batches"); vm.expectEmit(true, true, true, true); emit IPufferProtocolEvents.ValidatorSkipped(_getPubKey(bytes32("alice")), 0, PUFFER_MODULE_0); @@ -189,7 +189,7 @@ contract PufferProtocolTest is UnitTestHelper { moduleLimit = pufferProtocol.getModuleLimitInformation(PUFFER_MODULE_0); - assertEq(moduleLimit.numberOfRegisteredValidators, 1, "1 active validator"); + assertEq(moduleLimit.numberOfRegisteredBatches, 1, "1 active batches"); // This contract should receive pufETH because of the skipProvisioning assertTrue(pufferVault.balanceOf(address(this)) != 0, "non zero pufETH"); @@ -221,7 +221,7 @@ contract PufferProtocolTest is UnitTestHelper { uint256 smoothingCommitment = pufferOracle.getValidatorTicketPrice() * 30; bytes memory pubKey = _getPubKey(bytes32("charlie")); ValidatorKeyData memory validatorKeyData = _getMockValidatorKeyData(pubKey, PUFFER_MODULE_0); - vm.expectRevert(PufferProtocolBase.ValidatorLimitForModuleReached.selector); + vm.expectRevert(PufferProtocolBase.NumBatchesLimitForModuleReached.selector); pufferProtocol.registerValidatorKey{ value: smoothingCommitment }( validatorKeyData, bytes32("imaginary module"), 0, new bytes[](0), block.timestamp + 1 days ); @@ -617,14 +617,14 @@ contract PufferProtocolTest is UnitTestHelper { vm.expectEmit(true, true, true, true); emit IPufferProtocolEvents.ValidatorLimitPerModuleChanged(500, 1); - pufferProtocol.setValidatorLimitPerModule(PUFFER_MODULE_0, 1); + pufferProtocol.setBatchesLimitPerModule(PUFFER_MODULE_0, 1); // Revert if the registration will be over the limit uint256 smoothingCommitment = pufferOracle.getValidatorTicketPrice(); bytes memory pubKey = _getPubKey(bytes32("bob")); ValidatorKeyData memory validatorKeyData = _getMockValidatorKeyData(pubKey, PUFFER_MODULE_0); - vm.expectRevert(PufferProtocolBase.ValidatorLimitForModuleReached.selector); + vm.expectRevert(PufferProtocolBase.NumBatchesLimitForModuleReached.selector); pufferProtocol.registerValidatorKey{ value: (smoothingCommitment + BOND) }( validatorKeyData, PUFFER_MODULE_0, 0, new bytes[](0), block.timestamp + 1 days ); @@ -787,7 +787,7 @@ contract PufferProtocolTest is UnitTestHelper { vm.stopPrank(); vm.expectEmit(true, true, true, true); - emit IPufferProtocolEvents.NumberOfRegisteredValidatorsChanged(PUFFER_MODULE_0, 0); + emit IPufferProtocolEvents.NumberOfRegisteredBatchesChanged(PUFFER_MODULE_0, 0); pufferProtocol.skipProvisioning(PUFFER_MODULE_0, _getGuardianSignaturesForSkipping()); assertApproxEqRel( @@ -1981,7 +1981,7 @@ contract PufferProtocolTest is UnitTestHelper { function _createModules() internal { // Create EIGEN_DA module pufferProtocol.createPufferModule(EIGEN_DA); - pufferProtocol.setValidatorLimitPerModule(EIGEN_DA, 15); + pufferProtocol.setBatchesLimitPerModule(EIGEN_DA, 15); // Include the EIGEN_DA in module selection bytes32[] memory newWeights = new bytes32[](4);