Skip to content

Commit

Permalink
Merge f273632 into 59f72d0
Browse files Browse the repository at this point in the history
  • Loading branch information
ensi321 authored Jul 29, 2024
2 parents 59f72d0 + f273632 commit 0517efa
Show file tree
Hide file tree
Showing 28 changed files with 249 additions and 62 deletions.
12 changes: 6 additions & 6 deletions packages/api/src/beacon/routes/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,13 @@ export type Endpoints = {
/** The validator's randao reveal value */
randaoReveal: BLSSignature;
/** Arbitrary data validator wants to include in block */
graffiti: string;
graffiti?: string;
} & Omit<ExtraProduceBlockOpts, "blindedLocal">,
{
params: {slot: number};
query: {
randao_reveal: string;
graffiti: string;
graffiti?: string;
fee_recipient?: string;
builder_selection?: string;
strict_fee_recipient_check?: boolean;
Expand All @@ -333,15 +333,15 @@ export type Endpoints = {
/** The validator's randao reveal value */
randaoReveal: BLSSignature;
/** Arbitrary data validator wants to include in block */
graffiti: string;
graffiti?: string;
skipRandaoVerification?: boolean;
builderBoostFactor?: UintBn64;
} & ExtraProduceBlockOpts,
{
params: {slot: number};
query: {
randao_reveal: string;
graffiti: string;
graffiti?: string;
skip_randao_verification?: string;
fee_recipient?: string;
builder_selection?: string;
Expand All @@ -359,9 +359,9 @@ export type Endpoints = {
{
slot: Slot;
randaoReveal: BLSSignature;
graffiti: string;
graffiti?: string;
},
{params: {slot: number}; query: {randao_reveal: string; graffiti: string}},
{params: {slot: number}; query: {randao_reveal: string; graffiti?: string}},
BlindedBeaconBlock,
VersionMeta
>;
Expand Down
11 changes: 9 additions & 2 deletions packages/api/src/utils/serdes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ export function fromValidatorIdsStr(ids?: string[]): (string | number)[] | undef

const GRAFFITI_HEX_LENGTH = 66;

export function toGraffitiHex(utf8: string): string {
export function toGraffitiHex(utf8?: string): string | undefined {
if (utf8 === undefined) {
return undefined;
}

const hex = toHexString(new TextEncoder().encode(utf8));

if (hex.length > GRAFFITI_HEX_LENGTH) {
Expand All @@ -93,7 +97,10 @@ export function toGraffitiHex(utf8: string): string {
return hex;
}

export function fromGraffitiHex(hex: string): string {
export function fromGraffitiHex(hex?: string): string | undefined {
if (hex === undefined) {
return undefined;
}
try {
return new TextDecoder("utf8").decode(fromHexString(hex));
} catch {
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/api/impl/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ export function getApi(opts: ApiOptions, modules: ApiModules): BeaconApiMethods
lodestar: getLodestarApi(modules),
node: getNodeApi(opts, modules),
proof: getProofApi(opts, modules),
validator: getValidatorApi(modules),
validator: getValidatorApi(opts, modules),
};
}
34 changes: 19 additions & 15 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
import {ZERO_HASH} from "../../../constants/index.js";
import {SyncState} from "../../../sync/index.js";
import {isOptimisticBlock} from "../../../util/forkChoice.js";
import {toGraffitiBuffer} from "../../../util/graffiti.js";
import {getDefaultGraffiti, toGraffitiBuffer} from "../../../util/graffiti.js";
import {ApiError, NodeIsSyncing, OnlySupportedByDVT} from "../errors.js";
import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
import {CommitteeSubscription} from "../../../network/subnets/index.js";
Expand All @@ -63,6 +63,8 @@ import {getValidatorStatus} from "../beacon/state/utils.js";
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
import {SCHEDULER_LOOKAHEAD_FACTOR} from "../../../chain/prepareNextSlot.js";
import {ChainEvent, CheckpointHex, CommonBlockBody} from "../../../chain/index.js";
import {ApiOptions} from "../../options.js";
import {getLodestarClientVersion} from "../../../util/metadata.js";
import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource} from "./utils.js";

/**
Expand Down Expand Up @@ -110,14 +112,10 @@ type ProduceFullOrBlindedBlockOrContentsRes = {executionPayloadSource: ProducedB
* Server implementation for handling validator duties.
* See `@lodestar/validator/src/api` for the client implementation).
*/
export function getValidatorApi({
chain,
config,
logger,
metrics,
network,
sync,
}: ApiModules): ApplicationMethods<routes.validator.Endpoints> {
export function getValidatorApi(
opts: ApiOptions,
{chain, config, logger, metrics, network, sync}: ApiModules
): ApplicationMethods<routes.validator.Endpoints> {
let genesisBlockRoot: Root | null = null;

/**
Expand Down Expand Up @@ -348,7 +346,7 @@ export function getValidatorApi({
async function produceBuilderBlindedBlock(
slot: Slot,
randaoReveal: BLSSignature,
graffiti: string,
graffiti?: string,
// as of now fee recipient checks can not be performed because builder does not return bid recipient
{
skipHeadChecksAndUpdate,
Expand Down Expand Up @@ -406,7 +404,9 @@ export function getValidatorApi({
slot,
parentBlockRoot,
randaoReveal,
graffiti: toGraffitiBuffer(graffiti || ""),
graffiti: toGraffitiBuffer(
graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
),
commonBlockBody,
});

Expand All @@ -432,7 +432,7 @@ export function getValidatorApi({
async function produceEngineFullBlockOrContents(
slot: Slot,
randaoReveal: BLSSignature,
graffiti: string,
graffiti?: string,
{
feeRecipient,
strictFeeRecipientCheck,
Expand Down Expand Up @@ -474,7 +474,9 @@ export function getValidatorApi({
slot,
parentBlockRoot,
randaoReveal,
graffiti: toGraffitiBuffer(graffiti || ""),
graffiti: toGraffitiBuffer(
graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
),
feeRecipient,
commonBlockBody,
});
Expand Down Expand Up @@ -522,7 +524,7 @@ export function getValidatorApi({
async function produceEngineOrBuilderBlock(
slot: Slot,
randaoReveal: BLSSignature,
graffiti: string,
graffiti?: string,
// TODO deneb: skip randao verification
_skipRandaoVerification?: boolean,
builderBoostFactor?: bigint,
Expand Down Expand Up @@ -585,7 +587,9 @@ export function getValidatorApi({
slot,
parentBlockRoot,
randaoReveal,
graffiti: toGraffitiBuffer(graffiti || ""),
graffiti: toGraffitiBuffer(
graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
),
});
logger.debug("Produced common block body", loggerContext);

Expand Down
3 changes: 3 additions & 0 deletions packages/beacon-node/src/api/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import {beaconRestApiServerOpts, BeaconRestApiServerOpts} from "./rest/index.js"
export type ApiOptions = {
maxGindicesInProof?: number;
rest: BeaconRestApiServerOpts;
commit?: string;
version?: string;
private?: boolean;
};

export const defaultApiOptions: ApiOptions = {
maxGindicesInProof: 512,
rest: beaconRestApiServerOpts,
version: "dev",
private: false,
};
51 changes: 50 additions & 1 deletion packages/beacon-node/src/execution/engine/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {Metrics} from "../../metrics/index.js";
import {JobItemQueue} from "../../util/queue/index.js";
import {EPOCHS_PER_BATCH} from "../../sync/constants.js";
import {numToQuantity} from "../../eth1/provider/utils.js";
import {getLodestarClientVersion} from "../../util/metadata.js";
import {
ExecutionPayloadStatus,
ExecutePayloadResponse,
Expand All @@ -21,6 +22,8 @@ import {
BlobsBundle,
VersionedHashes,
ExecutionEngineState,
ClientVersion,
ClientCode,
} from "./interface.js";
import {PayloadIdCache} from "./payloadIdCache.js";
import {
Expand Down Expand Up @@ -63,6 +66,14 @@ export type ExecutionEngineHttpOpts = {
* A version string that will be set in `clv` field of jwt claims
*/
jwtVersion?: string;
/**
* Lodestar version to be used for `ClientVersion`
*/
version?: string;
/**
* Lodestar commit to be used for `ClientVersion`
*/
commit?: string;
};

export const defaultExecutionEngineHttpOpts: ExecutionEngineHttpOpts = {
Expand Down Expand Up @@ -105,6 +116,9 @@ export class ExecutionEngineHttp implements IExecutionEngine {
// It's safer to to avoid false positives and assume that the EL is syncing until we receive the first payload
state: ExecutionEngineState = ExecutionEngineState.ONLINE;

/** Cached EL client version from the latest getClientVersion call */
clientVersion?: ClientVersion | null;

readonly payloadIdCache = new PayloadIdCache();
/**
* A queue to serialize the fcUs and newPayloads calls:
Expand All @@ -126,7 +140,8 @@ export class ExecutionEngineHttp implements IExecutionEngine {

constructor(
private readonly rpc: IJsonRpcHttpClient,
{metrics, signal, logger}: ExecutionEngineModules
{metrics, signal, logger}: ExecutionEngineModules,
private readonly opts?: ExecutionEngineHttpOpts
) {
this.rpcFetchQueue = new JobItemQueue<[EngineRequest], EngineResponse>(
this.jobQueueProcessor,
Expand All @@ -140,6 +155,13 @@ export class ExecutionEngineHttp implements IExecutionEngine {
});

this.rpc.emitter.on(JsonRpcHttpClientEvent.RESPONSE, () => {
if (this.clientVersion === undefined) {
this.clientVersion = null;
// This statement should only be called first time receiving response after startup
this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
this.logger.debug("Unable to get execution client version", {}, e);
});
}
this.updateEngineState(getExecutionEngineState({targetState: ExecutionEngineState.ONLINE, oldState: this.state}));
});
}
Expand Down Expand Up @@ -417,6 +439,29 @@ export class ExecutionEngineHttp implements IExecutionEngine {
return response.map(deserializeExecutionPayloadBody);
}

private async getClientVersion(clientVersion: ClientVersion): Promise<ClientVersion[]> {
const method = "engine_getClientVersionV1";

const response = await this.rpc.fetchWithRetries<
EngineApiRpcReturnTypes[typeof method],
EngineApiRpcParamTypes[typeof method]
>({method, params: [clientVersion]});

const clientVersions = response.map((cv) => {
const code = cv.code in ClientCode ? ClientCode[cv.code as keyof typeof ClientCode] : ClientCode.XX;
return {code, name: cv.name, version: cv.version, commit: cv.commit};
});

if (clientVersions.length === 0) {
throw Error("Received empty client versions array");
}

this.clientVersion = clientVersions[0];
this.logger.debug("Execution client version updated", this.clientVersion);

return clientVersions;
}

private updateEngineState(newState: ExecutionEngineState): void {
const oldState = this.state;

Expand All @@ -425,6 +470,10 @@ export class ExecutionEngineHttp implements IExecutionEngine {
switch (newState) {
case ExecutionEngineState.ONLINE:
this.logger.info("Execution client became online", {oldState, newState});
this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
this.logger.debug("Unable to get execution client version", {}, e);
this.clientVersion = null;
});
break;
case ExecutionEngineState.OFFLINE:
this.logger.error("Execution client went offline", {oldState, newState});
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/execution/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function getExecutionEngineHttp(
jwtVersion: opts.jwtVersion,
});
modules.logger.info("Execution client", {urls: opts.urls.map(toSafePrintableUrl).toString()});
return new ExecutionEngineHttp(rpc, modules);
return new ExecutionEngineHttp(rpc, modules, opts);
}

export function initializeExecutionEngine(
Expand Down
29 changes: 29 additions & 0 deletions packages/beacon-node/src/execution/engine/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ export enum ExecutionEngineState {
AUTH_FAILED = "AUTH_FAILED",
}

/**
* Client code as defined in https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.4/src/engine/identification.md#clientcode
* ClientCode.XX is dedicated to other clients which do not have their own code
*/
export enum ClientCode {
BU = "BU", // besu
EJ = "EJ", // ethereumJS
EG = "EG", // erigon
GE = "GE", // go-ethereum
GR = "GR", // grandine
LH = "LH", // lighthouse
LS = "LS", // lodestar
NM = "NM", // nethermind
NB = "NB", // nimbus
TK = "TK", // teku
PM = "PM", // prysm
RH = "RH", // reth
XX = "XX", // unknown
}

export type ExecutePayloadResponse =
| {
status: ExecutionPayloadStatus.SYNCING | ExecutionPayloadStatus.ACCEPTED;
Expand Down Expand Up @@ -80,6 +100,13 @@ export type BlobsBundle = {
proofs: KZGProof[];
};

export type ClientVersion = {
code: ClientCode;
name: string;
version: string;
commit: string;
};

export type VersionedHashes = Uint8Array[];

/**
Expand All @@ -91,6 +118,8 @@ export type VersionedHashes = Uint8Array[];
export interface IExecutionEngine {
readonly state: ExecutionEngineState;

readonly clientVersion?: ClientVersion | null;

payloadIdCache: PayloadIdCache;
/**
* A state transition function which applies changes to the self.execution_state.
Expand Down
9 changes: 8 additions & 1 deletion packages/beacon-node/src/execution/engine/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
BlobsBundleRpc,
ExecutionPayloadBodyRpc,
} from "./types.js";
import {ExecutionPayloadStatus, PayloadIdCache} from "./interface.js";
import {ClientCode, ExecutionPayloadStatus, PayloadIdCache} from "./interface.js";
import {JsonRpcBackend} from "./utils.js";

const INTEROP_GAS_LIMIT = 30e6;
Expand Down Expand Up @@ -96,6 +96,7 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
engine_getPayloadV3: this.getPayload.bind(this),
engine_getPayloadBodiesByHashV1: this.getPayloadBodiesByHash.bind(this),
engine_getPayloadBodiesByRangeV1: this.getPayloadBodiesByRange.bind(this),
engine_getClientVersionV1: this.getClientVersionV1.bind(this),
};
}

Expand Down Expand Up @@ -386,6 +387,12 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
return payload.executionPayload;
}

private getClientVersionV1(
_clientVersion: EngineApiRpcParamTypes["engine_getClientVersionV1"][0]
): EngineApiRpcReturnTypes["engine_getClientVersionV1"] {
return [{code: ClientCode.XX, name: "mock", version: "", commit: ""}];
}

private timestampToFork(timestamp: number): ForkExecution {
if (timestamp > (this.opts.denebForkTimestamp ?? Infinity)) return ForkName.deneb;
if (timestamp > (this.opts.capellaForkTimestamp ?? Infinity)) return ForkName.capella;
Expand Down
Loading

0 comments on commit 0517efa

Please sign in to comment.