diff --git a/.openzeppelin/unknown-17000.json b/.openzeppelin/unknown-17000.json index 7d22a840..b7d411c4 100644 --- a/.openzeppelin/unknown-17000.json +++ b/.openzeppelin/unknown-17000.json @@ -939,115 +939,6 @@ }, "namespaces": {} } - }, - "cace716c99b7c3331ef14bea41153e9b79ee38508d038ed764ebfa2f3addf8d6": { - "address": "0xEFccE07BeC6418e32e72a28aFf0cdf442AEc14ea", - "txHash": "0x3da46b8992ed89a293c740d4e6a724eb0f24fc7b8fd2257bcd8af434ca1f956c", - "layout": { - "solcVersion": "0.8.18", - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC1967UpgradeUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" - }, - { - "label": "__gap", - "offset": 0, - "slot": "51", - "type": "t_array(t_uint256)50_storage", - "contract": "UUPSUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_pendingOwner", - "offset": 0, - "slot": "201", - "type": "t_address", - "contract": "Ownable2StepUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:27" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "Ownable2StepUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:70" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - }, - "namespaces": {} - } } } } diff --git a/.solcover.js b/.solcover.js new file mode 100644 index 00000000..5fcbba04 --- /dev/null +++ b/.solcover.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const path = require('path'); + +module.exports = { + coverageContractDepth: 10, + gasLimit: 10000000, + skipFiles: getSkipFiles(['deprecated', 'test', 'upgrades']), +}; + +function getSkipFiles(folders) { + const skipFiles = []; + const contractsPath = __dirname + '/contracts/'; + folders.forEach(folderName => { + const testFolderPath = path.join(contractsPath, folderName); + function readFilesRecursively(folderPath) { + const files = fs.readdirSync(folderPath); + files.forEach(file => { + const filePath = path.join(folderPath, file); + const stats = fs.statSync(filePath); + if (stats.isDirectory()) { + readFilesRecursively(filePath); + } else if (stats.isFile() && path.extname(filePath) === '.sol') { + skipFiles.push(filePath.replace(contractsPath, '')); + } + }); + } + readFilesRecursively(testFolderPath); + }); + + return skipFiles; +} diff --git a/test/helpers/contract-helpers.ts b/test/helpers/contract-helpers.ts index bf4516a6..730cb6ce 100644 --- a/test/helpers/contract-helpers.ts +++ b/test/helpers/contract-helpers.ts @@ -50,7 +50,7 @@ export const DataGenerator = { if (validators.length > 0) { return validators[0].publicKey; } - return `0x${id.toString(16).padStart(48, '0')}`; + return `0x${id.toString(16).padStart(96, '0')}`; }, shares: async (ownerId: number, validatorId: number, operatorCount: number) => { let shared: any; @@ -107,7 +107,7 @@ export const getClusterForValidator = ( return { validatorCount, networkFeeIndex, index, balance, active }; }; -export const initializeContract = async () => { +export const initializeContract = async (validatorsPerOperatorLimit = 500) => { CONFIG = { initialVersion: 'v1.1.0', operatorMaxFeeIncrease: 1000, @@ -116,7 +116,7 @@ export const initializeContract = async () => { minimalOperatorFee: 100000000, minimalBlocksBeforeLiquidation: 100800, minimumLiquidationCollateral: 200000000, - validatorsPerOperatorLimit: 500, + validatorsPerOperatorLimit: validatorsPerOperatorLimit, maximumOperatorFee: 76528650000000, }; diff --git a/test/validators/register.ts b/test/validators/register.ts index 1f8e95dd..4aa3cf98 100644 --- a/test/validators/register.ts +++ b/test/validators/register.ts @@ -185,34 +185,32 @@ describe('Register Validator Tests', () => { it('Bulk register 10 validators with 4 operators into the same cluster', async () => { await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); - const { eventsByName } = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).bulkRegisterValidator( - [helpers.DataGenerator.publicKey(11)], - [1, 2, 3, 4], - [helpers.DataGenerator.shares(1,11,4)], - minDepositAmount, - { - validatorCount: 0, - networkFeeIndex: 0, - index: 0, - balance: 0, - active: true - } - )); + const { eventsByName } = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .bulkRegisterValidator( + [helpers.DataGenerator.publicKey(11)], + [1, 2, 3, 4], + [helpers.DataGenerator.shares(1, 11, 4)], + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true, + }, + ), + ); const args = eventsByName.ValidatorAdded[0].args; - await helpers.bulkRegisterValidators( - 1, - 10, - [1, 2, 3, 4], - minDepositAmount, - args.cluster, - [GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_4], - ); + await helpers.bulkRegisterValidators(1, 10, [1, 2, 3, 4], minDepositAmount, args.cluster, [ + GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_4, + ]); }); it('Bulk register 10 validators with 4 operators new cluster', async () => { - await helpers.bulkRegisterValidators( 1, 10, @@ -224,18 +222,18 @@ describe('Register Validator Tests', () => { networkFeeIndex: 0, index: 0, balance: 0, - active: true - + active: true, }, [GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_4], ); - }); // 7 operators it('Register validator with 7 operators gas limit', async () => { - await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(helpers.DB.ssvNetwork.contract.address, minDepositAmount); + await helpers.DB.ssvToken + .connect(helpers.DB.owners[1]) + .approve(helpers.DB.ssvNetwork.contract.address, minDepositAmount); await trackGas( ssvNetworkContract .connect(helpers.DB.owners[1]) @@ -364,30 +362,29 @@ describe('Register Validator Tests', () => { const operatorIds = [1, 2, 3, 4, 5, 6, 7]; await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); - const { eventsByName } = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).bulkRegisterValidator( - [helpers.DataGenerator.publicKey(11)], - operatorIds, - [helpers.DataGenerator.shares(1,11,operatorIds.length)], - minDepositAmount, - { - validatorCount: 0, - networkFeeIndex: 0, - index: 0, - balance: 0, - active: true - } - )); + const { eventsByName } = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .bulkRegisterValidator( + [helpers.DataGenerator.publicKey(11)], + operatorIds, + [helpers.DataGenerator.shares(1, 11, operatorIds.length)], + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true, + }, + ), + ); const args = eventsByName.ValidatorAdded[0].args; - await helpers.bulkRegisterValidators( - 1, - 10, - operatorIds, - minDepositAmount, - args.cluster, - [GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_7], - ); + await helpers.bulkRegisterValidators(1, 10, operatorIds, minDepositAmount, args.cluster, [ + GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_7, + ]); }); it('Bulk register 10 validators with 7 operators new cluster', async () => { @@ -402,11 +399,10 @@ describe('Register Validator Tests', () => { networkFeeIndex: 0, index: 0, balance: 0, - active: true + active: true, }, [GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_7], ); - }); // 10 operators @@ -541,31 +537,29 @@ describe('Register Validator Tests', () => { const operatorIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); - const { eventsByName } = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).bulkRegisterValidator( - [helpers.DataGenerator.publicKey(11)], - operatorIds, - [helpers.DataGenerator.shares(1,10,operatorIds.length)], - minDepositAmount, - { - validatorCount: 0, - networkFeeIndex: 0, - index: 0, - balance: 0, - active: true - } - )); + const { eventsByName } = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .bulkRegisterValidator( + [helpers.DataGenerator.publicKey(11)], + operatorIds, + [helpers.DataGenerator.shares(1, 10, operatorIds.length)], + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true, + }, + ), + ); const args = eventsByName.ValidatorAdded[0].args; - - await helpers.bulkRegisterValidators( - 1, - 10, - operatorIds, - minDepositAmount, - args.cluster, - [GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_10], - ); + await helpers.bulkRegisterValidators(1, 10, operatorIds, minDepositAmount, args.cluster, [ + GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_10, + ]); }); it('Bulk register 10 validators with 10 operators new cluster', async () => { @@ -580,12 +574,10 @@ describe('Register Validator Tests', () => { networkFeeIndex: 0, index: 0, balance: 0, - active: true - + active: true, }, [GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_10], ); - }); // 13 operators @@ -718,31 +710,29 @@ describe('Register Validator Tests', () => { const operatorIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); - const { eventsByName } = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).bulkRegisterValidator( - [helpers.DataGenerator.publicKey(11)], - operatorIds, - [helpers.DataGenerator.shares(1, 11, operatorIds.length)], - minDepositAmount, - { - validatorCount: 0, - networkFeeIndex: 0, - index: 0, - balance: 0, - active: true - } - )); + const { eventsByName } = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .bulkRegisterValidator( + [helpers.DataGenerator.publicKey(11)], + operatorIds, + [helpers.DataGenerator.shares(1, 11, operatorIds.length)], + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true, + }, + ), + ); const args = eventsByName.ValidatorAdded[0].args; - - await helpers.bulkRegisterValidators( - 1, - 10, - operatorIds, - minDepositAmount, - args.cluster, - [GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_13], - ); + await helpers.bulkRegisterValidators(1, 10, operatorIds, minDepositAmount, args.cluster, [ + GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_13, + ]); }); it('Bulk register 10 validators with 13 operators new cluster', async () => { @@ -757,12 +747,10 @@ describe('Register Validator Tests', () => { networkFeeIndex: 0, index: 0, balance: 0, - active: true - + active: true, }, [GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_13], ); - }); it('Get cluster burn rate', async () => { @@ -831,7 +819,7 @@ describe('Register Validator Tests', () => { minDepositAmount, { validatorCount: 2, - networkFeeIndex: 10, + networkFeeIndex: 0, index: 0, balance: 0, active: true, @@ -840,10 +828,11 @@ describe('Register Validator Tests', () => { ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectClusterState'); }); - it('Register validator in a new cluster with incorrect input data reverts "IncorrectClusterState"', async () => { + it('Register validator with invalid network fee index reverts "IncorrectClusterState"', async () => { await helpers.DB.ssvToken .connect(helpers.DB.owners[1]) .approve(ssvNetworkContract.address, `${minDepositAmount * 2}`); + await expect( ssvNetworkContract .connect(helpers.DB.owners[1]) @@ -853,10 +842,57 @@ describe('Register Validator Tests', () => { helpers.DataGenerator.shares(1, 3, 4), minDepositAmount, { - validatorCount: 2, - networkFee: 10, + validatorCount: 0, networkFeeIndex: 10, + index: 0, + balance: 0, + active: true, + }, + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectClusterState'); + }); + + it('Register validator with invalid cluster index reverts "IncorrectClusterState"', async () => { + await helpers.DB.ssvToken + .connect(helpers.DB.owners[1]) + .approve(ssvNetworkContract.address, `${minDepositAmount * 2}`); + + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3), + [1, 2, 3, 4], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, index: 10, + balance: 0, + active: true, + }, + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectClusterState'); + }); + + it('Register validator in a new cluster with incorrect balance reverts "IncorrectClusterState"', async () => { + await helpers.DB.ssvToken + .connect(helpers.DB.owners[1]) + .approve(ssvNetworkContract.address, `${minDepositAmount * 2}`); + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3), + [1, 2, 3, 4], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + { + validatorCount: 0, + networkFee: 0, + networkFeeIndex: 0, + index: 0, balance: 10, active: false, }, @@ -864,6 +900,51 @@ describe('Register Validator Tests', () => { ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectClusterState'); }); + it('Register validator in a new cluster with incorrect active reverts "IncorrectClusterState"', async () => { + await helpers.DB.ssvToken + .connect(helpers.DB.owners[1]) + .approve(ssvNetworkContract.address, `${minDepositAmount * 2}`); + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3), + [1, 2, 3, 4], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + { + validatorCount: 0, + networkFee: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: false, + }, + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectClusterState'); + }); + + it('Bulk register validator with mismatched input data reverts "PublicKeysSharesLengthMismatch"', async () => { + await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .bulkRegisterValidator( + [helpers.DataGenerator.publicKey(11), helpers.DataGenerator.publicKey(12)], + [1, 2, 3, 4], + [helpers.DataGenerator.shares(1, 11, 4)], + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true, + }, + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'PublicKeysSharesLengthMismatch'); + }); + it('Register validator when an operator does not exist in the cluster reverts "OperatorDoesNotExist"', async () => { await expect( ssvNetworkContract.registerValidator( @@ -1012,8 +1093,9 @@ describe('Register Validator Tests', () => { minDepositAmount, helpers.getClusterForValidator(0, 0, 0, 0, true), ), - ).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorAlreadyExistsWithData') - .withArgs(helpers.DataGenerator.publicKey(1)); + ) + .to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorAlreadyExistsWithData') + .withArgs(helpers.DataGenerator.publicKey(1)); }); it('Register an existing validator with different operators setup reverts "ValidatorAlreadyExistsWithData"', async () => { @@ -1030,8 +1112,9 @@ describe('Register Validator Tests', () => { minDepositAmount, helpers.getClusterForValidator(0, 0, 0, 0, true), ), - ).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorAlreadyExistsWithData') - .withArgs(helpers.DataGenerator.publicKey(1)); + ) + .to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorAlreadyExistsWithData') + .withArgs(helpers.DataGenerator.publicKey(1)); }); it('Register whitelisted validator in 1 operator with 4 operators emits "ValidatorAdded"', async () => { diff --git a/test/validators/remove.ts b/test/validators/remove.ts index 1765e5a7..36102870 100644 --- a/test/validators/remove.ts +++ b/test/validators/remove.ts @@ -53,18 +53,17 @@ describe('Remove Validator Tests', () => { ); await expect( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster), + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), ).to.emit(ssvNetworkContract, 'ValidatorRemoved'); }); it('Remove validator after cluster liquidation period emits "ValidatorRemoved"', async () => { await utils.progressBlocks(helpers.CONFIG.minimalBlocksBeforeLiquidation + 10); - await expect(ssvNetworkContract - .connect(helpers.DB.owners[1]) - .removeValidator(helpers.DataGenerator.publicKey(1), firstCluster.operatorIds, firstCluster.cluster), + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .removeValidator(helpers.DataGenerator.publicKey(1), firstCluster.operatorIds, firstCluster.cluster), ).to.emit(ssvNetworkContract, 'ValidatorRemoved'); }); @@ -87,9 +86,7 @@ describe('Remove Validator Tests', () => { ); await trackGas( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster), + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), [GasGroup.BULK_REMOVE_10_VALIDATOR_4], ); }); @@ -125,9 +122,7 @@ describe('Remove Validator Tests', () => { ); await trackGas( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster), + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), [GasGroup.BULK_REMOVE_10_VALIDATOR_7], ); }); @@ -163,9 +158,7 @@ describe('Remove Validator Tests', () => { ); await trackGas( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster), + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), [GasGroup.BULK_REMOVE_10_VALIDATOR_10], ); }); @@ -201,9 +194,7 @@ describe('Remove Validator Tests', () => { ); await trackGas( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster), + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), [GasGroup.BULK_REMOVE_10_VALIDATOR_13], ); }); @@ -324,6 +315,8 @@ describe('Remove Validator Tests', () => { ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectClusterState'); }); + it('Remove validator with invalid publicKey and OperatorIds reverts "IncorrectValidatorStateWithData"', async () => {}); + it('Bulk Remove validator that does not exist in a valid cluster reverts "IncorrectValidatorStateWithData"', async () => { const { args, pks } = await helpers.bulkRegisterValidators( 2, @@ -333,13 +326,12 @@ describe('Remove Validator Tests', () => { helpers.getClusterForValidator(0, 0, 0, 0, true), ); - pks[2] = "0xabcd1234"; + pks[2] = '0xabcd1234'; await expect( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster), - ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorStateWithData') + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), + ) + .to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorStateWithData') .withArgs(pks[2]); }); @@ -353,9 +345,7 @@ describe('Remove Validator Tests', () => { ); await expect( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, [1, 2, 3, 5], args.cluster), + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, [1, 2, 3, 5], args.cluster), ).to.be.revertedWithCustomError(ssvNetworkContract, 'ClusterDoesNotExists'); }); @@ -369,19 +359,16 @@ describe('Remove Validator Tests', () => { ); const result = await trackGas( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, args.operatorIds, args.cluster) + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, args.operatorIds, args.cluster), ); const removed = result.eventsByName.ValidatorRemoved[0].args; // Remove validator again await expect( - ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks, removed.operatorIds, removed.cluster), - ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorStateWithData') + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(pks, removed.operatorIds, removed.cluster), + ) + .to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorStateWithData') .withArgs(pks[0]); }); @@ -396,16 +383,16 @@ describe('Remove Validator Tests', () => { await utils.progressBlocks(helpers.CONFIG.minimalBlocksBeforeLiquidation - 2); - let result = await trackGas(ssvNetworkContract - .connect(helpers.DB.owners[1]) - .liquidate(args.owner, args.operatorIds, args.cluster) + let result = await trackGas( + ssvNetworkContract.connect(helpers.DB.owners[1]).liquidate(args.owner, args.operatorIds, args.cluster), ); const liquidated = result.eventsByName.ClusterLiquidated[0].args; - result = await trackGas(ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(pks.slice(0, 5), liquidated.operatorIds, liquidated.cluster) + result = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[2]) + .bulkRemoveValidator(pks.slice(0, 5), liquidated.operatorIds, liquidated.cluster), ); const removed = result.eventsByName.ValidatorRemoved[0].args; @@ -430,18 +417,18 @@ describe('Remove Validator Tests', () => { const keys = [pks[0], pks[1], pks[2], pks[3], pks[2], pks[5], pks[2], pks[7], pks[2], pks[8]]; - - await expect(ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator(keys, args.operatorIds, args.cluster) - ).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorStateWithData') + await expect( + ssvNetworkContract.connect(helpers.DB.owners[2]).bulkRemoveValidator(keys, args.operatorIds, args.cluster), + ) + .to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorStateWithData') .withArgs(pks[2]); }); it('Bulk remove 10 validator with empty public keys reverts "IncorrectValidatorStateWithData"', async () => { - await expect(ssvNetworkContract - .connect(helpers.DB.owners[2]) - .bulkRemoveValidator([], firstCluster.operatorIds, firstCluster.cluster) + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[2]) + .bulkRemoveValidator([], firstCluster.operatorIds, firstCluster.cluster), ).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorDoesNotExist'); }); }); diff --git a/test/validators/revert.ts b/test/validators/revert.ts new file mode 100644 index 00000000..0de02306 --- /dev/null +++ b/test/validators/revert.ts @@ -0,0 +1,144 @@ +// Declare imports +import * as helpers from '../helpers/contract-helpers'; +import * as utils from '../helpers/utils'; +import { expect } from 'chai'; +import { trackGas, GasGroup } from '../helpers/gas-usage'; +import { progressBlocks } from '../helpers/utils'; + +let ssvNetworkContract: any, ssvViews: any, ssvToken: any, minDepositAmount: any, cluster1: any; +const exceedValidatorsLimit = 5; + +describe('Revert Validator Tests', () => { + beforeEach(async () => { + // Initialize contract + const metadata = await helpers.initializeContract(exceedValidatorsLimit); + ssvNetworkContract = metadata.contract; + ssvToken = metadata.ssvToken; + ssvViews = metadata.ssvViews; + + // Register operators + await helpers.registerOperators(0, 14, helpers.CONFIG.minimalOperatorFee); + + minDepositAmount = (helpers.CONFIG.minimalBlocksBeforeLiquidation + 2) * helpers.CONFIG.minimalOperatorFee * 13; + + await helpers.DB.ssvToken + .connect(helpers.DB.owners[1]) + .approve(helpers.DB.ssvNetwork.contract.address, '1000000000000000'); + + cluster1 = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(exceedValidatorsLimit + 1), + [1, 2, 3, 4], + helpers.DataGenerator.shares(1, exceedValidatorsLimit + 1, 4), + '1000000000000000', + helpers.getClusterForValidator(0, 0, 0, 0, true), + ), + ); + }); + + it('Register validators with exceed limit for an operator reverts "ExceedValidatorLimit"', async () => { + const args = cluster1.eventsByName.ValidatorAdded[0].args; + await expect( + helpers.bulkRegisterValidators(1, exceedValidatorsLimit, [1, 2, 3, 4], minDepositAmount, args.cluster), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'ExceedValidatorLimit'); + }); + + it('Reactivate a cluster with exceed limit for an operator reverts "ExceedValidatorLimit"', async () => { + // liquidate the 1st validator + const firstCluster = cluster1.eventsByName.ValidatorAdded[0].args; + await progressBlocks(helpers.CONFIG.minimalBlocksBeforeLiquidation); + const liquidatedCluster = await trackGas( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .liquidate(firstCluster.owner, firstCluster.operatorIds, firstCluster.cluster), + [GasGroup.LIQUIDATE_CLUSTER_4], + ); + let updatedCluster = liquidatedCluster.eventsByName.ClusterLiquidated[0].args; + + // check the operator's validatorCount is decreased. + expect(await ssvViews.getOperatorById(1)).to.deep.equal([ + helpers.DB.owners[0].address, // owner + helpers.CONFIG.minimalOperatorFee, // fee + 0, // validatorCount + ethers.constants.AddressZero, // whitelisted + false, // isPrivate + true, // active + ]); + + // register validators with maximum limit count + await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); + await helpers.bulkRegisterValidators(2, exceedValidatorsLimit, [1, 2, 3, 4], minDepositAmount, { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true, + }); + + // when the 1st validator will be reactive, revert with ExceedValidatorLimit + await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .reactivate(updatedCluster.operatorIds, minDepositAmount, updatedCluster.cluster), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'ExceedValidatorLimit'); + }); + + it('Register validators with invalid count 3 of operators reverts "InvalidOperatorIdsLength"', async () => { + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3), + [1, 2, 3], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + helpers.getClusterForValidator(0, 0, 0, 0, true), + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'InvalidOperatorIdsLength'); + }); + + it('Register validators with invalid count 15 of operators reverts "InvalidOperatorIdsLength"', async () => { + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + helpers.getClusterForValidator(0, 0, 0, 0, true), + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'InvalidOperatorIdsLength'); + }); + + it('Register validators with invalid count 6 of operators reverts "InvalidOperatorIdsLength"', async () => { + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3), + [1, 2, 3, 4, 5, 6], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + helpers.getClusterForValidator(0, 0, 0, 0, true), + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'InvalidOperatorIdsLength'); + }); + + it('Bulk register validators with invalid length of pubkeys reverts "InvalidPublicKeyLength"', async () => { + await expect( + ssvNetworkContract + .connect(helpers.DB.owners[1]) + .registerValidator( + helpers.DataGenerator.publicKey(3).substring(0, 48), + [1, 2, 3, 4], + helpers.DataGenerator.shares(1, 3, 4), + minDepositAmount, + helpers.getClusterForValidator(0, 0, 0, 0, true), + ), + ).to.be.revertedWithCustomError(ssvNetworkContract, 'InvalidPublicKeyLength'); + }); +});