Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validator status ssz type #6059

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/api/src/beacon/routes/beacon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export type {AttestationFilters} from "./pool.js";
export type {
StateId,
ValidatorId,
ValidatorStatus,
ValidatorFilters,
CommitteesFilters,
FinalityCheckpoints,
Expand Down
27 changes: 13 additions & 14 deletions packages/api/src/beacon/routes/beacon/state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import {ContainerType} from "@chainsafe/ssz";
import {phase0, CommitteeIndex, Slot, ValidatorIndex, Epoch, Root, ssz, StringType, RootHex} from "@lodestar/types";
import {
phase0,
CommitteeIndex,
Slot,
ValidatorIndex,
Epoch,
Root,
ssz,
RootHex,
ValidatorStatus,
validatorStatusType,
} from "@lodestar/types";
import {ApiClientResponse} from "../../../interfaces.js";
import {HttpStatusCode} from "../../../utils/client/httpStatusCode.js";
import {
Expand All @@ -23,18 +34,6 @@ export type ValidatorId = string | number;
*/
export type ExecutionOptimistic = boolean;

export type ValidatorStatus =
| "active"
| "pending_initialized"
| "pending_queued"
| "active_ongoing"
| "active_exiting"
| "active_slashed"
| "exited_unslashed"
| "exited_slashed"
| "withdrawal_possible"
| "withdrawal_done";

export type ValidatorFilters = {
id?: ValidatorId[];
status?: ValidatorStatus[];
Expand Down Expand Up @@ -312,7 +311,7 @@ export function getReturnTypes(): ReturnTypes<Api> {
{
index: ssz.ValidatorIndex,
balance: ssz.UintNum64,
status: new StringType<ValidatorStatus>(),
status: validatorStatusType,
validator: ssz.phase0.Validator,
},
{jsonCase: "eth2"}
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/api/impl/beacon/state/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {fromHexString} from "@chainsafe/ssz";
import {routes} from "@lodestar/api";
import {FAR_FUTURE_EPOCH, GENESIS_SLOT} from "@lodestar/params";
import {BeaconStateAllForks, PubkeyIndexMap} from "@lodestar/state-transition";
import {BLSPubkey, phase0} from "@lodestar/types";
import {BLSPubkey, ValidatorStatus, phase0} from "@lodestar/types";
import {Epoch, ValidatorIndex} from "@lodestar/types";
import {IBeaconChain, StateGetOpts} from "../../../../chain/index.js";
import {ApiError, ValidationError} from "../../errors.js";
Expand Down Expand Up @@ -66,7 +66,7 @@ async function resolveStateIdOrNull(
* Get the status of the validator
* based on conditions outlined in https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ
*/
export function getValidatorStatus(validator: phase0.Validator, currentEpoch: Epoch): routes.beacon.ValidatorStatus {
export function getValidatorStatus(validator: phase0.Validator, currentEpoch: Epoch): ValidatorStatus {
// pending
if (validator.activationEpoch > currentEpoch) {
if (validator.activationEligibilityEpoch === FAR_FUTURE_EPOCH) {
Expand Down
1 change: 0 additions & 1 deletion packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,6 @@ export function getValidatorApi({
const validator = headState.validators.getReadonly(validatorIndex);
const status = getValidatorStatus(validator, currentEpoch);
return (
status === "active" ||
status === "active_exiting" ||
status === "active_ongoing" ||
status === "active_slashed" ||
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * as ssz from "./sszTypes.js";
export * from "./utils/typeguards.js";
// String type
export {StringType, stringType} from "./utils/StringType.js";
// Validator status type
export {ValidatorStatusType, validatorStatusType} from "./utils/ValidatorStatusType.js";
14 changes: 14 additions & 0 deletions packages/types/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,17 @@ export enum ProducedBlockSource {

export type SlotRootHex = {slot: Slot; root: RootHex};
export type SlotOptionalRoot = {slot: Slot; root?: RootHex};

/**
* [Validator status specification](https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ)
*/
export type ValidatorStatus =
| "pending_initialized"
| "pending_queued"
| "active_ongoing"
| "active_exiting"
| "active_slashed"
| "exited_unslashed"
| "exited_slashed"
| "withdrawal_possible"
| "withdrawal_done";
89 changes: 89 additions & 0 deletions packages/types/src/utils/ValidatorStatusType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {BasicType} from "@chainsafe/ssz";
import {ValidatorStatus} from "../types.js";

// TODO: add spec reference once defined
const statusToByteMapping: Record<ValidatorStatus, number> = {
pending_initialized: 0x01,
pending_queued: 0x02,
active_ongoing: 0x03,
active_exiting: 0x04,
active_slashed: 0x05,
exited_unslashed: 0x06,
exited_slashed: 0x07,
withdrawal_possible: 0x08,
withdrawal_done: 0x09,
};

const byteToStatusMapping = Object.fromEntries(
Object.entries(statusToByteMapping).map(([key, value]) => [value, key])
) as Record<number, ValidatorStatus>;

export class ValidatorStatusType extends BasicType<ValidatorStatus> {
// TODO: review if those parameters are correct
readonly typeName = "ValidatorStatus";
readonly byteLength = 1;
readonly fixedSize = 1;
readonly minSize = 1;
readonly maxSize = 1;

defaultValue(): ValidatorStatus {
return "" as ValidatorStatus;
}

// Serialization + deserialization

value_serializeToBytes(output: ByteViews, offset: number, value: ValidatorStatus): number {
output.uint8Array[offset] = statusToByteMapping[value];
return offset + 1;
}
value_deserializeFromBytes(data: ByteViews, start: number, end: number): ValidatorStatus {
this.assertValidSize(end - start);

const status = byteToStatusMapping[data.uint8Array[start]];

if (status === undefined) {
throw Error(`ValidatorStatus: invalid value: ${data.uint8Array[start]}`);
}

return status;
}
tree_serializeToBytes(): number {
throw Error("Not supported in ValidatorStatus type");
}
tree_deserializeFromBytes(): never {
throw Error("Not supported in ValidatorStatus type");
}

// Fast tree opts

tree_getFromNode(): ValidatorStatus {
throw Error("Not supported in ValidatorStatus type");
}
tree_setToNode(): void {
throw Error("Not supported in ValidatorStatus type");
}
tree_getFromPackedNode(): ValidatorStatus {
throw Error("Not supported in ValidatorStatus type");
}
tree_setToPackedNode(): void {
throw Error("Not supported in ValidatorStatus type");
}

// JSON

fromJson(json: unknown): ValidatorStatus {
return json as ValidatorStatus;
}

toJson(value: ValidatorStatus): ValidatorStatus {
return value;
}
}

// TODO: export from ssz / or move type to ssz?
type ByteViews = {
uint8Array: Uint8Array;
dataView: DataView;
};

export const validatorStatusType = new ValidatorStatusType();
7 changes: 3 additions & 4 deletions packages/validator/src/services/indices.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {toHexString} from "@chainsafe/ssz";
import {ValidatorIndex} from "@lodestar/types";
import {ValidatorIndex, ValidatorStatus} from "@lodestar/types";
import {Logger, MapDef} from "@lodestar/utils";
import {Api, ApiError, routes} from "@lodestar/api";
import {Api, ApiError} from "@lodestar/api";
import {batchItems} from "../util/index.js";
import {Metrics} from "../metrics.js";

Expand All @@ -17,9 +17,8 @@ type PubkeyHex = string;
// To assist with logging statuses, we only log the statuses that are not active_exiting or withdrawal_possible
type SimpleValidatorStatus = "pending" | "active" | "exited" | "withdrawn";

const statusToSimpleStatusMapping = (status: routes.beacon.ValidatorStatus): SimpleValidatorStatus => {
const statusToSimpleStatusMapping = (status: ValidatorStatus): SimpleValidatorStatus => {
switch (status) {
case "active":
case "active_exiting":
case "active_slashed":
case "active_ongoing":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("AttestationDutiesService", function () {
const defaultValidator: routes.beacon.ValidatorResponse = {
index,
balance: 32e9,
status: "active",
status: "active_ongoing",
validator: ssz.phase0.Validator.defaultValue(),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("SyncCommitteeDutiesService", function () {
const defaultValidator: routes.beacon.ValidatorResponse = {
index: indices[0],
balance: 32e9,
status: "active",
status: "active_ongoing",
validator: ssz.phase0.Validator.defaultValue(),
};

Expand Down