UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

91 lines 4.12 kB
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