import {Logger} from "@lodestar/logger"; import {VERIFICATION_FAILED_RESPONSE_CODE} from "../constants.js"; import { JsonRpcBatchRequest, JsonRpcBatchResponse, JsonRpcErrorPayload, JsonRpcNotificationPayload, JsonRpcRequest, JsonRpcRequestOrBatch, JsonRpcRequestPayload, JsonRpcResponse, JsonRpcResponseOrBatch, JsonRpcResponseWithErrorPayload, JsonRpcResponseWithResultPayload, } from "../types.js"; import {isNullish} from "./validation.js"; export function getResponseForRequest( payload: JsonRpcRequest

, res?: R, error?: JsonRpcErrorPayload ): JsonRpcResponse { // If it's a notification if (!isRequest(payload)) { throw new Error("Cannot generate response for notification"); } if (!isNullish(res) && isNullish(error)) { return { jsonrpc: payload.jsonrpc, id: payload.id, result: res, }; } if (!isNullish(error)) { return { jsonrpc: payload.jsonrpc, id: payload.id, error, }; } throw new Error("Either result or error must be defined."); } export function getVerificationFailedMessage(method: string): string { return `verification for '${method}' request failed.`; } export function isVerificationFailedError

(payload: JsonRpcResponseWithErrorPayload

): boolean { return !isValidResponsePayload(payload) && payload.error.code === VERIFICATION_FAILED_RESPONSE_CODE; } export function getErrorResponseForRequestWithFailedVerification( payload: JsonRpcRequest

, message: string, data?: D ): JsonRpcResponseWithErrorPayload { return isNullish(data) ? (getResponseForRequest(payload, undefined, { code: VERIFICATION_FAILED_RESPONSE_CODE, message, }) as JsonRpcResponseWithErrorPayload) : (getResponseForRequest(payload, undefined, { code: VERIFICATION_FAILED_RESPONSE_CODE, message, data, }) as JsonRpcResponseWithErrorPayload); } function isValidResponsePayload( response: JsonRpcResponse | undefined ): response is JsonRpcResponseWithResultPayload { return !isNullish(response) && isNullish(response.error); } export function isValidResponse( response: JsonRpcResponseOrBatch | undefined ): response is JsonRpcResponseWithResultPayload | JsonRpcResponseWithResultPayload[] { return Array.isArray(response) ? response.every(isValidResponsePayload) : isValidResponsePayload(response); } export function isValidBatchResponse( payload: JsonRpcBatchRequest, response: JsonRpcBatchResponse ): response is JsonRpcBatchResponse | JsonRpcResponseWithResultPayload[] { for (const [index, req] of payload.entries()) { if (isRequest(req) && (response[index].id !== req.id || !isValidResponse(response[index]))) return false; } return true; } export function mergeBatchReqResp( payload: JsonRpcBatchRequest, response: JsonRpcBatchResponse ): {request: JsonRpcRequest; response: JsonRpcResponse}[] { const result = []; for (const [index, req] of payload.entries()) { if (isRequest(req)) { // Some providers return raw json-rpc response, some return only result // we need to just merge the result back based on the provider result.push({request: req, response: response[index]}); } } return result; } export function isNotification

(payload: JsonRpcRequest

): payload is JsonRpcNotificationPayload

{ return !("id" in payload); } export function isRequest

(payload: JsonRpcRequest

): payload is JsonRpcRequestPayload

{ return "id" in payload; } export function isBatchRequest

(payload: JsonRpcRequestOrBatch

): payload is JsonRpcBatchRequest

{ return Array.isArray(payload); } export function isBatchResponse(response: JsonRpcResponseOrBatch): response is JsonRpcBatchResponse { return Array.isArray(response); } function logRequestPayload(payload: JsonRpcRequest, logger: Logger): void { logger.debug("PR -> EL", { id: isRequest(payload) ? payload.id : "notification", method: payload.method, params: JSON.stringify(payload.params), }); } export function logRequest(payload: JsonRpcRequestOrBatch | undefined | null, logger: Logger): void { if (payload === undefined || payload === null) { return; } for (const p of isBatchRequest(payload) ? payload : [payload]) { logRequestPayload(p, logger); } } function logResponsePayload(response: JsonRpcResponse | null | undefined, logger: Logger): void { if (response === undefined || response === null) { logger.debug("PR <- EL (empty response)"); return; } if (isValidResponse(response)) { logger.debug("PR <- EL", { id: response.id, result: JSON.stringify(response.result), }); } else { logger.debug("PR <- E:", { id: response.id, error: JSON.stringify(response.error), }); } } export function logResponse(response: JsonRpcResponseOrBatch | undefined, logger: Logger): void { if (response === undefined || response === null) { return; } for (const p of isBatchResponse(response) ? response : [response]) { logResponsePayload(p, logger); } }