import {ForkName} from "@lodestar/params"; import {writeEncodedPayload} from "../encodingStrategies/index.js"; import {encodeErrorMessage} from "../utils/index.js"; import { ContextBytesType, ContextBytesFactory, ProtocolDefinition, EncodedPayload, EncodedPayloadType, } from "../types.js"; import {RespStatus, RpcResponseStatusError} from "../interface.js"; /** * Yields byte chunks for a `` with a zero response code `` * ```bnf * response ::= * * response_chunk ::= | | | * result ::= "0" * ``` * Note: `response` has zero or more chunks (denoted by `<>*`) */ export function responseEncodeSuccess( protocol: ProtocolDefinition ): (source: AsyncIterable>) => AsyncIterable { return async function* responseEncodeSuccessTransform(source) { for await (const chunk of source) { // yield Buffer.from([RespStatus.SUCCESS]); // - from altair const contextBytes = getContextBytes(protocol.contextBytes, chunk); if (contextBytes) { yield contextBytes as Buffer; } // | const forkName = getForkNameFromContextBytes(protocol.contextBytes, chunk); const respType = protocol.responseType(forkName); yield* writeEncodedPayload(chunk, protocol.encoding, respType); } }; } /** * Yields byte chunks for a `` with a non-zero response code `` * denoted as `` * ```bnf * error_response ::= | ? * result ::= "1" | "2" | ["128" ... "255"] * ``` * Only the last `` is allowed to have a non-zero error code, so this * fn yields exactly one `` and afterwards the stream must be terminated */ export async function* responseEncodeError( protocol: Pick, status: RpcResponseStatusError, errorMessage: string ): AsyncGenerator { // yield Buffer.from([status]); // ? is optional if (errorMessage) { yield* encodeErrorMessage(errorMessage, protocol.encoding); } } /** * Yields byte chunks for a ``. See `ContextBytesType` for possible types. * This item is mandatory but may be empty. */ function getContextBytes( contextBytes: ContextBytesFactory, chunk: EncodedPayload ): Uint8Array | null { switch (contextBytes.type) { // Yield nothing case ContextBytesType.Empty: return null; // Yield a fixed-width 4 byte chunk, set to the `ForkDigest` case ContextBytesType.ForkDigest: switch (chunk.type) { case EncodedPayloadType.ssz: return contextBytes.forkDigestContext.forkName2ForkDigest( contextBytes.forkFromResponse(chunk.data) ) as Buffer; case EncodedPayloadType.bytes: if (chunk.contextBytes.type !== ContextBytesType.ForkDigest) { throw Error(`Expected context bytes ForkDigest but got ${chunk.contextBytes.type}`); } return contextBytes.forkDigestContext.forkName2ForkDigest( contextBytes.forkDigestContext.getForkName(chunk.contextBytes.forkSlot) ) as Buffer; } } } function getForkNameFromContextBytes( contextBytes: ContextBytesFactory, chunk: EncodedPayload ): ForkName { switch (contextBytes.type) { case ContextBytesType.Empty: return ForkName.phase0; // Yield a fixed-width 4 byte chunk, set to the `ForkDigest` case ContextBytesType.ForkDigest: switch (chunk.type) { case EncodedPayloadType.ssz: return contextBytes.forkFromResponse(chunk.data); case EncodedPayloadType.bytes: if (chunk.contextBytes.type !== ContextBytesType.ForkDigest) { throw Error(`Expected context bytes ForkDigest but got ${chunk.contextBytes.type}`); } return contextBytes.forkDigestContext.getForkName(chunk.contextBytes.forkSlot); } } }