Skip to content

Commit f4d2598

Browse files
committed
remove validator tests added
1 parent 6246f77 commit f4d2598

3 files changed

Lines changed: 180 additions & 0 deletions

File tree

test/common/errors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ export const Errors = {
33
INVALID_PUBLIC_KEYS_LENGTH: "InvalidPublicKeyLength",
44
PUBLIC_KEYS_SHARES_LENGTH_MISMATCH: "PublicKeysSharesLengthMismatch",
55
VALIDATOR_ALREADY_EXISTS_WITH_DATA: "ValidatorAlreadyExistsWithData",
6+
VALIDATOR_DOES_NOT_EXIST: "ValidatorDoesNotExist",
67
INVALID_OPERATOR_IDS_LENGTH: "InvalidOperatorIdsLength",
78
UNSORTED_OPERATORS_LIST: "UnsortedOperatorsList",
89
OPERATORS_LIST_NOT_UNIQUE: "OperatorsListNotUnique",
910
CLUSTER_IS_LIQUIDATED: "ClusterIsLiquidated",
11+
CLUSTER_DOES_NOT_EXISTS: "ClusterDoesNotExists",
1012
INCORRECT_CLUSTER_VERSION: "IncorrectClusterVersion",
1113
INCORRECT_CLUSTER_STATE: "IncorrectClusterState",
1214
CALLER_NOT_WHITELISTED: "CallerNotWhitelistedWithData",

test/common/events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const Events = {
22
VALIDATOR_ADDED: "ValidatorAdded",
3+
VALIDATOR_REMOVED: "ValidatorRemoved",
34
OPERATOR_ADDED: "OperatorAdded",
45
OPERATOR_PRIVACY_STATUS_UPDATED: "OperatorPrivacyStatusUpdated",
56
OPERATOR_REMOVED: "OperatorRemoved",
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { expect } from "chai";
2+
import type { NetworkConnection } from "hardhat/types/network";
3+
import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/types";
4+
import { getTestConnection } from "../../setup/connection.ts";
5+
import { ssvClustersHarnessFixture } from "../../setup/fixtures.ts";
6+
import type { NetworkHelpersType } from "../../common/types.ts";
7+
import { makePublicKey } from "../../common/helpers.ts";
8+
import { DEFAULT_ETH_REGISTER_VALUE, DEFAULT_SHARES, EMPTY_CLUSTER } from "../../common/constants.ts";
9+
import { Events } from "../../common/events.ts";
10+
import { Errors } from "../../common/errors.ts";
11+
12+
type ClusterType = typeof EMPTY_CLUSTER;
13+
14+
const createCluster = (overrides: Partial<ClusterType> = {}): ClusterType => ({
15+
...EMPTY_CLUSTER,
16+
active: true,
17+
...overrides,
18+
});
19+
20+
const parseClusterFromEvent = (contract: any, receipt: any, eventName: string): ClusterType => {
21+
for (const log of receipt.logs ?? []) {
22+
let parsed;
23+
try {
24+
parsed = contract.interface.parseLog(log);
25+
} catch {
26+
continue;
27+
}
28+
29+
if (parsed?.name === eventName) {
30+
const clusterTuple = parsed.args[parsed.args.length - 1];
31+
const [validatorCount, networkFeeIndex, index, active, balance] = clusterTuple;
32+
33+
return {
34+
validatorCount: BigInt(validatorCount),
35+
networkFeeIndex: BigInt(networkFeeIndex),
36+
index: BigInt(index),
37+
active,
38+
balance: BigInt(balance),
39+
};
40+
}
41+
}
42+
43+
throw new Error(`Event ${eventName} not found`);
44+
};
45+
46+
describe("SSVClusters function `removeValidator()`", async () => {
47+
let connection: NetworkConnection<"generic">;
48+
let networkHelpers: NetworkHelpersType;
49+
50+
let clusterOwner: HardhatEthersSigner;
51+
52+
before(async function () {
53+
({ connection, networkHelpers } = await getTestConnection());
54+
55+
[clusterOwner] = await connection.ethers.getSigners();
56+
});
57+
58+
const deploySSVClustersAndPrepareOperatorsFixture = async () => {
59+
return ssvClustersHarnessFixture(connection);
60+
};
61+
62+
it("Removes an existing validator, updates cluster state and emits correct events", async function () {
63+
const { clusters, operatorIds } =
64+
await networkHelpers.loadFixture(deploySSVClustersAndPrepareOperatorsFixture);
65+
66+
const publicKey = makePublicKey(1);
67+
68+
const registerTx = await clusters.registerValidator(
69+
publicKey,
70+
operatorIds,
71+
DEFAULT_SHARES,
72+
0,
73+
createCluster(),
74+
{ value: DEFAULT_ETH_REGISTER_VALUE }
75+
);
76+
const registerReceipt = await registerTx.wait();
77+
const clusterAfterRegister = parseClusterFromEvent(clusters, registerReceipt, Events.VALIDATOR_ADDED);
78+
79+
const removeTx = await clusters.removeValidator(publicKey, operatorIds, clusterAfterRegister);
80+
const removeReceipt = await removeTx.wait();
81+
const clusterAfterRemove = parseClusterFromEvent(clusters, removeReceipt, Events.VALIDATOR_REMOVED);
82+
83+
await expect(removeTx).to.emit(clusters, Events.VALIDATOR_REMOVED);
84+
expect(clusterAfterRemove.validatorCount).to.equal(0n);
85+
expect(clusterAfterRemove.active).to.equal(true);
86+
});
87+
88+
it("Is reverted with 'ValidatorDoesNotExist' when validator was not registered", async function () {
89+
const { clusters, operatorIds } =
90+
await networkHelpers.loadFixture(deploySSVClustersAndPrepareOperatorsFixture);
91+
92+
const registeredKey = makePublicKey(1);
93+
const registerTx = await clusters.registerValidator(
94+
registeredKey,
95+
operatorIds,
96+
DEFAULT_SHARES,
97+
0,
98+
createCluster(),
99+
{ value: DEFAULT_ETH_REGISTER_VALUE }
100+
);
101+
const registerReceipt = await registerTx.wait();
102+
const clusterAfterRegister = parseClusterFromEvent(clusters, registerReceipt, Events.VALIDATOR_ADDED);
103+
104+
const nonExistingKey = makePublicKey(2);
105+
await expect(clusters.removeValidator(
106+
nonExistingKey,
107+
operatorIds,
108+
clusterAfterRegister
109+
)).to.be.revertedWithCustomError(clusters, Errors.VALIDATOR_DOES_NOT_EXIST);
110+
});
111+
112+
it("Is reverted with 'IncorrectClusterState' when provided cluster data is stale or mismatched", async function () {
113+
const { clusters, operatorIds } =
114+
await networkHelpers.loadFixture(deploySSVClustersAndPrepareOperatorsFixture);
115+
116+
const publicKey = makePublicKey(1);
117+
const registerTx = await clusters.registerValidator(
118+
publicKey,
119+
operatorIds,
120+
DEFAULT_SHARES,
121+
0,
122+
createCluster(),
123+
{ value: DEFAULT_ETH_REGISTER_VALUE }
124+
);
125+
const registerReceipt = await registerTx.wait();
126+
const clusterAfterRegister = parseClusterFromEvent(clusters, registerReceipt, Events.VALIDATOR_ADDED);
127+
128+
const mismatchedCluster = {
129+
...clusterAfterRegister,
130+
balance: clusterAfterRegister.balance + 1n,
131+
};
132+
133+
await expect(clusters.removeValidator(
134+
publicKey,
135+
operatorIds,
136+
mismatchedCluster
137+
)).to.be.revertedWithCustomError(clusters, Errors.INCORRECT_CLUSTER_STATE);
138+
});
139+
140+
it("Is reverted with 'ClusterDoesNotExists' when attempting to remove from a missing cluster", async function () {
141+
const { clusters, operatorIds } =
142+
await networkHelpers.loadFixture(deploySSVClustersAndPrepareOperatorsFixture);
143+
144+
await expect(clusters.removeValidator(
145+
makePublicKey(1),
146+
operatorIds,
147+
createCluster()
148+
)).to.be.revertedWithCustomError(clusters, Errors.CLUSTER_DOES_NOT_EXISTS);
149+
});
150+
151+
it("Is reverted with 'ValidatorDoesNotExist' when removing a validator twice", async function () {
152+
const { clusters, operatorIds } =
153+
await networkHelpers.loadFixture(deploySSVClustersAndPrepareOperatorsFixture);
154+
155+
const publicKey = makePublicKey(1);
156+
const registerTx = await clusters.registerValidator(
157+
publicKey,
158+
operatorIds,
159+
DEFAULT_SHARES,
160+
0,
161+
createCluster(),
162+
{ value: DEFAULT_ETH_REGISTER_VALUE }
163+
);
164+
const registerReceipt = await registerTx.wait();
165+
const clusterAfterRegister = parseClusterFromEvent(clusters, registerReceipt, Events.VALIDATOR_ADDED);
166+
167+
const removeTx = await clusters.removeValidator(publicKey, operatorIds, clusterAfterRegister);
168+
const removeReceipt = await removeTx.wait();
169+
const clusterAfterRemove = parseClusterFromEvent(clusters, removeReceipt, Events.VALIDATOR_REMOVED);
170+
171+
await expect(clusters.removeValidator(
172+
publicKey,
173+
operatorIds,
174+
clusterAfterRemove
175+
)).to.be.revertedWithCustomError(clusters, Errors.VALIDATOR_DOES_NOT_EXIST);
176+
});
177+
});

0 commit comments

Comments
 (0)