Skip to content

Commit 42599e4

Browse files
nflaigphilknows
authored andcommitted
refactor: move validator status type and util to @lodestar/types (#7140)
1 parent d15b387 commit 42599e4

File tree

8 files changed

+155
-153
lines changed

8 files changed

+155
-153
lines changed

packages/api/src/beacon/routes/beacon/state.ts

+2-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import {ContainerType, ValueOf} from "@chainsafe/ssz";
33
import {ChainForkConfig} from "@lodestar/config";
44
import {MAX_VALIDATORS_PER_COMMITTEE} from "@lodestar/params";
5-
import {phase0, CommitteeIndex, Slot, Epoch, ssz, RootHex, StringType} from "@lodestar/types";
5+
import {phase0, CommitteeIndex, Slot, Epoch, ssz, RootHex, StringType, ValidatorStatus} from "@lodestar/types";
66
import {Endpoint, RequestCodec, RouteDefinitions, Schema} from "../../../utils/index.js";
77
import {ArrayOf, JsonOnlyReq} from "../../../utils/codecs.js";
88
import {ExecutionOptimisticAndFinalizedCodec, ExecutionOptimisticAndFinalizedMeta} from "../../../utils/metadata.js";
@@ -24,17 +24,7 @@ export type StateArgs = {
2424

2525
export type ValidatorId = string | number;
2626

27-
export type ValidatorStatus =
28-
| "active"
29-
| "pending_initialized"
30-
| "pending_queued"
31-
| "active_ongoing"
32-
| "active_exiting"
33-
| "active_slashed"
34-
| "exited_unslashed"
35-
| "exited_slashed"
36-
| "withdrawal_possible"
37-
| "withdrawal_done";
27+
export type {ValidatorStatus};
3828

3929
export const RandaoResponseType = new ContainerType({
4030
randao: ssz.Root,

packages/beacon-node/src/api/impl/beacon/state/index.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,11 @@ import {
99
getRandaoMix,
1010
} from "@lodestar/state-transition";
1111
import {EPOCHS_PER_HISTORICAL_VECTOR} from "@lodestar/params";
12+
import {getValidatorStatus} from "@lodestar/types";
1213
import {fromHex} from "@lodestar/utils";
1314
import {ApiError} from "../../errors.js";
1415
import {ApiModules} from "../../types.js";
15-
import {
16-
filterStateValidatorsByStatus,
17-
getStateValidatorIndex,
18-
getValidatorStatus,
19-
getStateResponse,
20-
toValidatorResponse,
21-
} from "./utils.js";
16+
import {filterStateValidatorsByStatus, getStateValidatorIndex, getStateResponse, toValidatorResponse} from "./utils.js";
2217

2318
export function getBeaconStateApi({
2419
chain,

packages/beacon-node/src/api/impl/beacon/state/utils.ts

+2-34
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
22
import {routes} from "@lodestar/api";
3-
import {FAR_FUTURE_EPOCH, GENESIS_SLOT} from "@lodestar/params";
3+
import {GENESIS_SLOT} from "@lodestar/params";
44
import {BeaconStateAllForks} from "@lodestar/state-transition";
5-
import {BLSPubkey, Epoch, phase0, RootHex, Slot, ValidatorIndex} from "@lodestar/types";
5+
import {BLSPubkey, Epoch, getValidatorStatus, phase0, RootHex, Slot, ValidatorIndex} from "@lodestar/types";
66
import {fromHex} from "@lodestar/utils";
77
import {CheckpointWithHex, IForkChoice} from "@lodestar/fork-choice";
88
import {IBeaconChain} from "../../../../chain/index.js";
@@ -83,38 +83,6 @@ export async function getStateResponseWithRegen(
8383
return res;
8484
}
8585

86-
/**
87-
* Get the status of the validator
88-
* based on conditions outlined in https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ
89-
*/
90-
export function getValidatorStatus(validator: phase0.Validator, currentEpoch: Epoch): routes.beacon.ValidatorStatus {
91-
// pending
92-
if (validator.activationEpoch > currentEpoch) {
93-
if (validator.activationEligibilityEpoch === FAR_FUTURE_EPOCH) {
94-
return "pending_initialized";
95-
} else if (validator.activationEligibilityEpoch < FAR_FUTURE_EPOCH) {
96-
return "pending_queued";
97-
}
98-
}
99-
// active
100-
if (validator.activationEpoch <= currentEpoch && currentEpoch < validator.exitEpoch) {
101-
if (validator.exitEpoch === FAR_FUTURE_EPOCH) {
102-
return "active_ongoing";
103-
} else if (validator.exitEpoch < FAR_FUTURE_EPOCH) {
104-
return validator.slashed ? "active_slashed" : "active_exiting";
105-
}
106-
}
107-
// exited
108-
if (validator.exitEpoch <= currentEpoch && currentEpoch < validator.withdrawableEpoch) {
109-
return validator.slashed ? "exited_slashed" : "exited_unslashed";
110-
}
111-
// withdrawal
112-
if (validator.withdrawableEpoch <= currentEpoch) {
113-
return validator.effectiveBalance !== 0 ? "withdrawal_possible" : "withdrawal_done";
114-
}
115-
throw new Error("ValidatorStatus unknown");
116-
}
117-
11886
export function toValidatorResponse(
11987
index: ValidatorIndex,
12088
validator: phase0.Validator,

packages/beacon-node/src/api/impl/validator/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
BeaconBlock,
4242
BlockContents,
4343
BlindedBeaconBlock,
44+
getValidatorStatus,
4445
} from "@lodestar/types";
4546
import {ExecutionStatus, DataAvailabilityStatus} from "@lodestar/fork-choice";
4647
import {fromHex, toHex, resolveOrRacePromises, prettyWeiToEth, toRootHex} from "@lodestar/utils";
@@ -61,7 +62,6 @@ import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/va
6162
import {CommitteeSubscription} from "../../../network/subnets/index.js";
6263
import {ApiModules} from "../types.js";
6364
import {RegenCaller} from "../../../chain/regen/index.js";
64-
import {getValidatorStatus} from "../beacon/state/utils.js";
6565
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
6666
import {SCHEDULER_LOOKAHEAD_FACTOR} from "../../../chain/prepareNextSlot.js";
6767
import {ChainEvent, CheckpointHex, CommonBlockBody} from "../../../chain/index.js";

packages/beacon-node/test/unit/api/impl/beacon/state/utils.test.ts

+1-99
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,9 @@
11
import {describe, it, expect} from "vitest";
22
import {toHexString} from "@chainsafe/ssz";
3-
import {phase0} from "@lodestar/types";
4-
import {getValidatorStatus, getStateValidatorIndex} from "../../../../../../src/api/impl/beacon/state/utils.js";
3+
import {getStateValidatorIndex} from "../../../../../../src/api/impl/beacon/state/utils.js";
54
import {generateCachedAltairState} from "../../../../../utils/state.js";
65

76
describe("beacon state api utils", function () {
8-
describe("getValidatorStatus", function () {
9-
it("should return PENDING_INITIALIZED", function () {
10-
const validator = {
11-
activationEpoch: 1,
12-
activationEligibilityEpoch: Infinity,
13-
} as phase0.Validator;
14-
const currentEpoch = 0;
15-
const status = getValidatorStatus(validator, currentEpoch);
16-
expect(status).toBe("pending_initialized");
17-
});
18-
it("should return PENDING_QUEUED", function () {
19-
const validator = {
20-
activationEpoch: 1,
21-
activationEligibilityEpoch: 101010101101010,
22-
} as phase0.Validator;
23-
const currentEpoch = 0;
24-
const status = getValidatorStatus(validator, currentEpoch);
25-
expect(status).toBe("pending_queued");
26-
});
27-
it("should return ACTIVE_ONGOING", function () {
28-
const validator = {
29-
activationEpoch: 1,
30-
exitEpoch: Infinity,
31-
} as phase0.Validator;
32-
const currentEpoch = 1;
33-
const status = getValidatorStatus(validator, currentEpoch);
34-
expect(status).toBe("active_ongoing");
35-
});
36-
it("should return ACTIVE_SLASHED", function () {
37-
const validator = {
38-
activationEpoch: 1,
39-
exitEpoch: 101010101101010,
40-
slashed: true,
41-
} as phase0.Validator;
42-
const currentEpoch = 1;
43-
const status = getValidatorStatus(validator, currentEpoch);
44-
expect(status).toBe("active_slashed");
45-
});
46-
it("should return ACTIVE_EXITING", function () {
47-
const validator = {
48-
activationEpoch: 1,
49-
exitEpoch: 101010101101010,
50-
slashed: false,
51-
} as phase0.Validator;
52-
const currentEpoch = 1;
53-
const status = getValidatorStatus(validator, currentEpoch);
54-
expect(status).toBe("active_exiting");
55-
});
56-
it("should return EXITED_SLASHED", function () {
57-
const validator = {
58-
exitEpoch: 1,
59-
withdrawableEpoch: 3,
60-
slashed: true,
61-
} as phase0.Validator;
62-
const currentEpoch = 2;
63-
const status = getValidatorStatus(validator, currentEpoch);
64-
expect(status).toBe("exited_slashed");
65-
});
66-
it("should return EXITED_UNSLASHED", function () {
67-
const validator = {
68-
exitEpoch: 1,
69-
withdrawableEpoch: 3,
70-
slashed: false,
71-
} as phase0.Validator;
72-
const currentEpoch = 2;
73-
const status = getValidatorStatus(validator, currentEpoch);
74-
expect(status).toBe("exited_unslashed");
75-
});
76-
it("should return WITHDRAWAL_POSSIBLE", function () {
77-
const validator = {
78-
withdrawableEpoch: 1,
79-
effectiveBalance: 32,
80-
} as phase0.Validator;
81-
const currentEpoch = 1;
82-
const status = getValidatorStatus(validator, currentEpoch);
83-
expect(status).toBe("withdrawal_possible");
84-
});
85-
it("should return WITHDRAWAL_DONE", function () {
86-
const validator = {
87-
withdrawableEpoch: 1,
88-
effectiveBalance: 0,
89-
} as phase0.Validator;
90-
const currentEpoch = 1;
91-
const status = getValidatorStatus(validator, currentEpoch);
92-
expect(status).toBe("withdrawal_done");
93-
});
94-
it("should error", function () {
95-
const validator = {} as phase0.Validator;
96-
const currentEpoch = 0;
97-
try {
98-
getValidatorStatus(validator, currentEpoch);
99-
} catch (error) {
100-
expect(error).toHaveProperty("message", "ValidatorStatus unknown");
101-
}
102-
});
103-
});
104-
1057
describe("getStateValidatorIndex", () => {
1068
const state = generateCachedAltairState();
1079
const pubkey2index = state.epochCtx.pubkey2index;

packages/types/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export * from "./utils/typeguards.js";
99
export {StringType, stringType} from "./utils/stringType.js";
1010
// Container utils
1111
export * from "./utils/container.js";
12+
export * from "./utils/validatorStatus.js";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {FAR_FUTURE_EPOCH} from "@lodestar/params";
2+
import {Epoch, phase0} from "../types.js";
3+
4+
export type ValidatorStatus =
5+
| "active"
6+
| "pending_initialized"
7+
| "pending_queued"
8+
| "active_ongoing"
9+
| "active_exiting"
10+
| "active_slashed"
11+
| "exited_unslashed"
12+
| "exited_slashed"
13+
| "withdrawal_possible"
14+
| "withdrawal_done";
15+
16+
/**
17+
* Get the status of the validator
18+
* based on conditions outlined in https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ
19+
*/
20+
export function getValidatorStatus(validator: phase0.Validator, currentEpoch: Epoch): ValidatorStatus {
21+
// pending
22+
if (validator.activationEpoch > currentEpoch) {
23+
if (validator.activationEligibilityEpoch === FAR_FUTURE_EPOCH) {
24+
return "pending_initialized";
25+
} else if (validator.activationEligibilityEpoch < FAR_FUTURE_EPOCH) {
26+
return "pending_queued";
27+
}
28+
}
29+
// active
30+
if (validator.activationEpoch <= currentEpoch && currentEpoch < validator.exitEpoch) {
31+
if (validator.exitEpoch === FAR_FUTURE_EPOCH) {
32+
return "active_ongoing";
33+
} else if (validator.exitEpoch < FAR_FUTURE_EPOCH) {
34+
return validator.slashed ? "active_slashed" : "active_exiting";
35+
}
36+
}
37+
// exited
38+
if (validator.exitEpoch <= currentEpoch && currentEpoch < validator.withdrawableEpoch) {
39+
return validator.slashed ? "exited_slashed" : "exited_unslashed";
40+
}
41+
// withdrawal
42+
if (validator.withdrawableEpoch <= currentEpoch) {
43+
return validator.effectiveBalance !== 0 ? "withdrawal_possible" : "withdrawal_done";
44+
}
45+
throw new Error("ValidatorStatus unknown");
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import {describe, it, expect} from "vitest";
2+
import {getValidatorStatus} from "../../src/utils/validatorStatus.js";
3+
import {phase0} from "../../src/types.js";
4+
5+
describe("getValidatorStatus", function () {
6+
it("should return PENDING_INITIALIZED", function () {
7+
const validator = {
8+
activationEpoch: 1,
9+
activationEligibilityEpoch: Infinity,
10+
} as phase0.Validator;
11+
const currentEpoch = 0;
12+
const status = getValidatorStatus(validator, currentEpoch);
13+
expect(status).toBe("pending_initialized");
14+
});
15+
it("should return PENDING_QUEUED", function () {
16+
const validator = {
17+
activationEpoch: 1,
18+
activationEligibilityEpoch: 101010101101010,
19+
} as phase0.Validator;
20+
const currentEpoch = 0;
21+
const status = getValidatorStatus(validator, currentEpoch);
22+
expect(status).toBe("pending_queued");
23+
});
24+
it("should return ACTIVE_ONGOING", function () {
25+
const validator = {
26+
activationEpoch: 1,
27+
exitEpoch: Infinity,
28+
} as phase0.Validator;
29+
const currentEpoch = 1;
30+
const status = getValidatorStatus(validator, currentEpoch);
31+
expect(status).toBe("active_ongoing");
32+
});
33+
it("should return ACTIVE_SLASHED", function () {
34+
const validator = {
35+
activationEpoch: 1,
36+
exitEpoch: 101010101101010,
37+
slashed: true,
38+
} as phase0.Validator;
39+
const currentEpoch = 1;
40+
const status = getValidatorStatus(validator, currentEpoch);
41+
expect(status).toBe("active_slashed");
42+
});
43+
it("should return ACTIVE_EXITING", function () {
44+
const validator = {
45+
activationEpoch: 1,
46+
exitEpoch: 101010101101010,
47+
slashed: false,
48+
} as phase0.Validator;
49+
const currentEpoch = 1;
50+
const status = getValidatorStatus(validator, currentEpoch);
51+
expect(status).toBe("active_exiting");
52+
});
53+
it("should return EXITED_SLASHED", function () {
54+
const validator = {
55+
exitEpoch: 1,
56+
withdrawableEpoch: 3,
57+
slashed: true,
58+
} as phase0.Validator;
59+
const currentEpoch = 2;
60+
const status = getValidatorStatus(validator, currentEpoch);
61+
expect(status).toBe("exited_slashed");
62+
});
63+
it("should return EXITED_UNSLASHED", function () {
64+
const validator = {
65+
exitEpoch: 1,
66+
withdrawableEpoch: 3,
67+
slashed: false,
68+
} as phase0.Validator;
69+
const currentEpoch = 2;
70+
const status = getValidatorStatus(validator, currentEpoch);
71+
expect(status).toBe("exited_unslashed");
72+
});
73+
it("should return WITHDRAWAL_POSSIBLE", function () {
74+
const validator = {
75+
withdrawableEpoch: 1,
76+
effectiveBalance: 32,
77+
} as phase0.Validator;
78+
const currentEpoch = 1;
79+
const status = getValidatorStatus(validator, currentEpoch);
80+
expect(status).toBe("withdrawal_possible");
81+
});
82+
it("should return WITHDRAWAL_DONE", function () {
83+
const validator = {
84+
withdrawableEpoch: 1,
85+
effectiveBalance: 0,
86+
} as phase0.Validator;
87+
const currentEpoch = 1;
88+
const status = getValidatorStatus(validator, currentEpoch);
89+
expect(status).toBe("withdrawal_done");
90+
});
91+
it("should error", function () {
92+
const validator = {} as phase0.Validator;
93+
const currentEpoch = 0;
94+
try {
95+
getValidatorStatus(validator, currentEpoch);
96+
} catch (error) {
97+
expect(error).toHaveProperty("message", "ValidatorStatus unknown");
98+
}
99+
});
100+
});

0 commit comments

Comments
 (0)