Skip to content

Commit

Permalink
bulk exit validator
Browse files Browse the repository at this point in the history
  • Loading branch information
mtabasco committed Jan 16, 2024
1 parent de9484f commit 2af125a
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 11 deletions.
68 changes: 68 additions & 0 deletions abis/SSVNetwork.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,24 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes[]",
"name": "publicKeys",
"type": "bytes[]"
},
{
"internalType": "uint64[]",
"name": "operatorIds",
"type": "uint64[]"
}
],
"name": "bulkExitValidator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -997,6 +1015,56 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes[]",
"name": "publicKeys",
"type": "bytes[]"
},
{
"internalType": "uint64[]",
"name": "operatorIds",
"type": "uint64[]"
},
{
"components": [
{
"internalType": "uint32",
"name": "validatorCount",
"type": "uint32"
},
{
"internalType": "uint64",
"name": "networkFeeIndex",
"type": "uint64"
},
{
"internalType": "uint64",
"name": "index",
"type": "uint64"
},
{
"internalType": "bool",
"name": "active",
"type": "bool"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
}
],
"internalType": "struct ISSVNetworkCore.Cluster",
"name": "cluster",
"type": "tuple"
}
],
"name": "bulkRemoveValidator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
4 changes: 4 additions & 0 deletions contracts/SSVNetwork.sol
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ contract SSVNetwork is
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS]);
}

function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external override {
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS]);
}

function updateNetworkFee(uint256 fee) external override onlyOwner {
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_DAO]);
}
Expand Down
5 changes: 5 additions & 0 deletions contracts/interfaces/ISSVClusters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ interface ISSVClusters is ISSVNetworkCore {
/// @param operatorIds Array of IDs of operators managing the validator
function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external;

/// @notice Fires the exit event for a set of validators
/// @param publicKeys The public keys of the validators to be exited
/// @param operatorIds Array of IDs of operators managing the validators
function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external;

/**
* @dev Emitted when the validator has been added.
* @param publicKey The public key of a validator.
Expand Down
15 changes: 10 additions & 5 deletions contracts/modules/SSVClusters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,11 @@ contract SSVClusters is ISSVClusters {
CoreLib.deposit(amount);
}

for (uint i; i < validatorsLength; ) {
for (uint i; i < validatorsLength; ++i) {
bytes memory pk = publicKeys[i];
bytes memory sh = sharesData[i];
emit ValidatorAdded(msg.sender, operatorIds, pk, sh, cluster);

unchecked {
++i;
}
emit ValidatorAdded(msg.sender, operatorIds, pk, sh, cluster);
}
}

Expand Down Expand Up @@ -326,4 +323,12 @@ contract SSVClusters is ISSVClusters {

emit ValidatorExited(msg.sender, operatorIds, publicKey);
}

function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external override {
ValidatorLib.validateStates(publicKeys, operatorIds, SSVStorage.load());

for (uint i; i < publicKeys.length; ++i) {
emit ValidatorExited(msg.sender, operatorIds, publicKeys[i]);
}
}
}
7 changes: 7 additions & 0 deletions contracts/test/SSVNetworkUpgrade.sol
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,13 @@ contract SSVNetworkUpgrade is
);
}

function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external override {
_delegateCall(
SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS],
abi.encodeWithSignature("bulkExitValidator(bytes[],uint64[]))", publicKeys, operatorIds)
);
}

function updateNetworkFee(uint256 fee) external override onlyOwner {
_delegateCall(
SSVStorage.load().ssvContracts[SSVModules.SSV_DAO],
Expand Down
20 changes: 14 additions & 6 deletions test/helpers/gas-usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export enum GasGroup {
WITHDRAW_CLUSTER_BALANCE,
WITHDRAW_OPERATOR_BALANCE,
VALIDATOR_EXIT,
BULK_EXIT_10_VALIDATOR_4,
BULK_EXIT_10_VALIDATOR_7,
BULK_EXIT_10_VALIDATOR_10,
BULK_EXIT_10_VALIDATOR_13,

LIQUIDATE_CLUSTER_4,
LIQUIDATE_CLUSTER_7,
Expand Down Expand Up @@ -92,22 +96,22 @@ const MAX_GAS_PER_GROUP: any = {
[GasGroup.REGISTER_VALIDATOR_NEW_STATE_7]: 289000,
[GasGroup.REGISTER_VALIDATOR_WITHOUT_DEPOSIT_7]: 251600,

[GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_7]: 1085000,
[GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_7]: 1068000,
[GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_7]: 1085500,
[GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_7]: 1068500,

[GasGroup.REGISTER_VALIDATOR_EXISTING_CLUSTER_10]: 342700,
[GasGroup.REGISTER_VALIDATOR_NEW_STATE_10]: 359500,
[GasGroup.REGISTER_VALIDATOR_WITHOUT_DEPOSIT_10]: 322200,

[GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_10]: 1364500,
[GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_10]: 1348000,
[GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_10]: 1365000,
[GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_10]: 1348200,

[GasGroup.REGISTER_VALIDATOR_EXISTING_CLUSTER_13]: 413700,
[GasGroup.REGISTER_VALIDATOR_NEW_STATE_13]: 430500,
[GasGroup.REGISTER_VALIDATOR_WITHOUT_DEPOSIT_13]: 393200,

[GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_13]: 1649600,
[GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_13]: 1633000,
[GasGroup.BULK_REGISTER_10_VALIDATOR_NEW_STATE_13]: 1650200,
[GasGroup.BULK_REGISTER_10_VALIDATOR_EXISTING_CLUSTER_13]: 1633500,

[GasGroup.REMOVE_VALIDATOR]: 115000,
[GasGroup.BULK_REMOVE_10_VALIDATOR_4]: 196000,
Expand All @@ -125,6 +129,10 @@ const MAX_GAS_PER_GROUP: any = {
[GasGroup.WITHDRAW_CLUSTER_BALANCE]: 94500,
[GasGroup.WITHDRAW_OPERATOR_BALANCE]: 64900,
[GasGroup.VALIDATOR_EXIT]: 43000,
[GasGroup.BULK_EXIT_10_VALIDATOR_4]: 126200,
[GasGroup.BULK_EXIT_10_VALIDATOR_7]: 139500,
[GasGroup.BULK_EXIT_10_VALIDATOR_10]: 152500,
[GasGroup.BULK_EXIT_10_VALIDATOR_13]: 165500,

[GasGroup.LIQUIDATE_CLUSTER_4]: 129300,
[GasGroup.LIQUIDATE_CLUSTER_7]: 170500,
Expand Down
115 changes: 115 additions & 0 deletions test/validators/others.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,119 @@ describe('Other Validator Tests', () => {
ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator(helpers.DataGenerator.publicKey(1), [1, 2, 3, 5]),
).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorState');
});

it('Bulk exiting a validator emits "ValidatorExited"', async () => {
const { args, pks } = await helpers.bulkRegisterValidators(
2,
10,
helpers.DEFAULT_OPERATOR_IDS[4],
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0,
active: true,
},
);

await expect(
ssvNetworkContract
.connect(helpers.DB.owners[2])
.bulkExitValidator(pks, args.operatorIds),
)
.to.emit(ssvNetworkContract, 'ValidatorExited');
});

it('Bulk exiting 10 validator (4 operators cluster) gas limit', async () => {
const { args, pks } = await helpers.bulkRegisterValidators(
2,
10,
helpers.DEFAULT_OPERATOR_IDS[4],
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0,
active: true,
},
);

await trackGas(
ssvNetworkContract
.connect(helpers.DB.owners[2])
.bulkExitValidator(pks, args.operatorIds),
[GasGroup.BULK_EXIT_10_VALIDATOR_4],
);
});

it('Bulk exiting 10 validator (7 operators cluster) gas limit', async () => {
const { args, pks } = await helpers.bulkRegisterValidators(
2,
10,
helpers.DEFAULT_OPERATOR_IDS[7],
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0,
active: true,
},
);

await trackGas(
ssvNetworkContract
.connect(helpers.DB.owners[2])
.bulkExitValidator(pks, args.operatorIds),
[GasGroup.BULK_EXIT_10_VALIDATOR_7],
);
});

it('Bulk exiting 10 validator (10 operators cluster) gas limit', async () => {
const { args, pks } = await helpers.bulkRegisterValidators(
2,
10,
helpers.DEFAULT_OPERATOR_IDS[10],
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0,
active: true,
},
);

await trackGas(
ssvNetworkContract
.connect(helpers.DB.owners[2])
.bulkExitValidator(pks, args.operatorIds),
[GasGroup.BULK_EXIT_10_VALIDATOR_10],
);
});

it('Bulk exiting 10 validator (13 operators cluster) gas limit', async () => {
const { args, pks } = await helpers.bulkRegisterValidators(
2,
10,
helpers.DEFAULT_OPERATOR_IDS[13],
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0,
active: true,
},
);

await trackGas(
ssvNetworkContract
.connect(helpers.DB.owners[2])
.bulkExitValidator(pks, args.operatorIds),
[GasGroup.BULK_EXIT_10_VALIDATOR_13],
);
});
});

0 comments on commit 2af125a

Please sign in to comment.