@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
91 lines • 4.12 kB
JavaScript
import { isFetchError } from "@lodestar/utils";
import { isErrorAborted } from "@lodestar/utils";
import { ErrorJsonRpcResponse, HttpRpcError, JsonRpcHttpClientEvent, JsonRpcHttpClientEventEmitter, } from "../../eth1/provider/jsonRpcHttpClient.js";
import { isQueueErrorAborted } from "../../util/queue/errors.js";
import { ExecutionEngineState, ExecutionPayloadStatus } from "./interface.js";
export class ExecutionEngineMockJsonRpcClient {
constructor(backend) {
this.backend = backend;
this.emitter = new JsonRpcHttpClientEventEmitter();
}
async fetch(payload) {
return this.wrapWithEvents(async () => {
const handler = this.backend.handlers[payload.method];
if (handler === undefined) {
throw Error(`Unknown method ${payload.method}`);
}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
return handler(...payload.params);
}, payload);
}
fetchWithRetries(payload) {
return this.fetch(payload);
}
fetchBatch(rpcPayloadArr) {
return Promise.all(rpcPayloadArr.map((payload) => this.fetch(payload)));
}
async wrapWithEvents(func, payload) {
try {
const response = await func();
this.emitter.emit(JsonRpcHttpClientEvent.RESPONSE, { payload, response });
return response;
}
catch (error) {
this.emitter.emit(JsonRpcHttpClientEvent.ERROR, { payload, error: error });
throw error;
}
}
}
export const HTTP_FATAL_ERROR_CODES = ["ECONNREFUSED", "ENOTFOUND", "EAI_AGAIN"];
export const HTTP_CONNECTION_ERROR_CODES = ["ECONNRESET", "ECONNABORTED"];
function getExecutionEngineStateForPayloadStatus(payloadStatus) {
switch (payloadStatus) {
case ExecutionPayloadStatus.ACCEPTED:
case ExecutionPayloadStatus.VALID:
case ExecutionPayloadStatus.UNSAFE_OPTIMISTIC_STATUS:
return ExecutionEngineState.SYNCED;
case ExecutionPayloadStatus.ELERROR:
case ExecutionPayloadStatus.INVALID:
case ExecutionPayloadStatus.SYNCING:
case ExecutionPayloadStatus.INVALID_BLOCK_HASH:
return ExecutionEngineState.SYNCING;
case ExecutionPayloadStatus.UNAVAILABLE:
return ExecutionEngineState.OFFLINE;
default:
// In case we can't determine the state, we assume it stays in old state
// This assumption is better than considering offline, because the offline state may trigger some notifications
return ExecutionEngineState.ONLINE;
}
}
function getExecutionEngineStateForPayloadError(payloadError, oldState) {
if (isErrorAborted(payloadError) || isQueueErrorAborted(payloadError)) {
return oldState;
}
// Originally this case was handled with {status: ExecutePayloadStatus.ELERROR}
if (payloadError instanceof HttpRpcError || payloadError instanceof ErrorJsonRpcResponse) {
return ExecutionEngineState.SYNCING;
}
if (payloadError && isFetchError(payloadError) && HTTP_FATAL_ERROR_CODES.includes(payloadError.code)) {
return ExecutionEngineState.OFFLINE;
}
if (payloadError && isFetchError(payloadError) && HTTP_CONNECTION_ERROR_CODES.includes(payloadError.code)) {
return ExecutionEngineState.AUTH_FAILED;
}
return oldState;
}
export function getExecutionEngineState({ payloadError, payloadStatus, targetState, oldState, }) {
const newState = targetState !== undefined
? targetState
: payloadStatus === undefined
? getExecutionEngineStateForPayloadError(payloadError, oldState)
: getExecutionEngineStateForPayloadStatus(payloadStatus);
if (newState === oldState)
return oldState;
// The ONLINE is initial state and can reached from offline or auth failed error
if (newState === ExecutionEngineState.ONLINE &&
!(oldState === ExecutionEngineState.OFFLINE || oldState === ExecutionEngineState.AUTH_FAILED)) {
return oldState;
}
return newState;
}
//# sourceMappingURL=utils.js.map