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);
}
}