diff --git a/packages/beacon-node/src/network/reqresp/handlers/index.ts b/packages/beacon-node/src/network/reqresp/handlers/index.ts index 4035aa508009..72e1f02fc7f2 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/index.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/index.ts @@ -1,4 +1,5 @@ -import {ProtocolHandler} from "@lodestar/reqresp"; +import type {Type} from "@chainsafe/ssz"; +import {ProtocolHandler, RespStatus, ResponseError} from "@lodestar/reqresp"; import {ssz} from "@lodestar/types"; import {IBeaconChain} from "../../../chain/index.js"; import {IBeaconDb} from "../../../db/index.js"; @@ -29,6 +30,14 @@ function notImplemented(method: ReqRespMethod): ProtocolHandler { }; } +function deserializeRequestBody(type: Type, data: Uint8Array): T { + try { + return type.deserialize(data); + } catch (e) { + throw new ResponseError(RespStatus.INVALID_REQUEST, e instanceof Error ? e.message : String(e)); + } +} + /** * The ReqRespHandler module handles app-level requests / responses from other peers, * fetching state from the chain and database as needed. @@ -40,51 +49,51 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh [ReqRespMethod.Ping]: notImplemented(ReqRespMethod.Ping), [ReqRespMethod.Metadata]: notImplemented(ReqRespMethod.Metadata), [ReqRespMethod.BeaconBlocksByRange]: (req, peerId, peerClient) => { - const body = ssz.phase0.BeaconBlocksByRangeRequest.deserialize(req.data); + const body = deserializeRequestBody(ssz.phase0.BeaconBlocksByRangeRequest, req.data); return onBeaconBlocksByRange(body, chain, db, peerId, peerClient); }, [ReqRespMethod.BeaconBlocksByRoot]: (req) => { const fork = chain.config.getForkName(chain.clock.currentSlot); - const body = BeaconBlocksByRootRequestType(fork, chain.config).deserialize(req.data); + const body = deserializeRequestBody(BeaconBlocksByRootRequestType(fork, chain.config), req.data); return onBeaconBlocksByRoot(body, chain); }, [ReqRespMethod.BeaconBlocksByHead]: (req, peerId, peerClient) => { - const body = ssz.fulu.BeaconBlocksByHeadRequest.deserialize(req.data); + const body = deserializeRequestBody(ssz.fulu.BeaconBlocksByHeadRequest, req.data); return onBeaconBlocksByHead(body, chain, peerId, peerClient); }, [ReqRespMethod.BlobSidecarsByRoot]: (req) => { const fork = chain.config.getForkName(chain.clock.currentSlot); - const body = BlobSidecarsByRootRequestType(fork, chain.config).deserialize(req.data); + const body = deserializeRequestBody(BlobSidecarsByRootRequestType(fork, chain.config), req.data); return onBlobSidecarsByRoot(body, chain); }, [ReqRespMethod.BlobSidecarsByRange]: (req) => { - const body = ssz.deneb.BlobSidecarsByRangeRequest.deserialize(req.data); + const body = deserializeRequestBody(ssz.deneb.BlobSidecarsByRangeRequest, req.data); return onBlobSidecarsByRange(body, chain, db); }, [ReqRespMethod.DataColumnSidecarsByRange]: (req, peerId, peerClient) => { - const body = ssz.fulu.DataColumnSidecarsByRangeRequest.deserialize(req.data); + const body = deserializeRequestBody(ssz.fulu.DataColumnSidecarsByRangeRequest, req.data); return onDataColumnSidecarsByRange(body, chain, db, peerId, peerClient); }, [ReqRespMethod.DataColumnSidecarsByRoot]: (req, peerId, peerClient) => { - const body = DataColumnSidecarsByRootRequestType(chain.config).deserialize(req.data); + const body = deserializeRequestBody(DataColumnSidecarsByRootRequestType(chain.config), req.data); return onDataColumnSidecarsByRoot(body, chain, db, peerId, peerClient); }, [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: (req, peerId, peerClient) => { - const body = ExecutionPayloadEnvelopesByRootRequestType(chain.config).deserialize(req.data); + const body = deserializeRequestBody(ExecutionPayloadEnvelopesByRootRequestType(chain.config), req.data); return onExecutionPayloadEnvelopesByRoot(body, chain, db, peerId, peerClient); }, [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req, peerId, peerClient) => { - const body = ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest.deserialize(req.data); + const body = deserializeRequestBody(ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest, req.data); return onExecutionPayloadEnvelopesByRange(body, chain, db, peerId, peerClient); }, [ReqRespMethod.LightClientBootstrap]: (req) => { - const body = ssz.Root.deserialize(req.data); + const body = deserializeRequestBody(ssz.Root, req.data); return onLightClientBootstrap(body, chain); }, [ReqRespMethod.LightClientUpdatesByRange]: (req) => { - const body = ssz.altair.LightClientUpdatesByRange.deserialize(req.data); + const body = deserializeRequestBody(ssz.altair.LightClientUpdatesByRange, req.data); return onLightClientUpdatesByRange(body, chain); }, [ReqRespMethod.LightClientFinalityUpdate]: () => onLightClientFinalityUpdate(chain), diff --git a/packages/beacon-node/test/unit/network/reqresp/handlers/index.test.ts b/packages/beacon-node/test/unit/network/reqresp/handlers/index.test.ts new file mode 100644 index 000000000000..7744332d441b --- /dev/null +++ b/packages/beacon-node/test/unit/network/reqresp/handlers/index.test.ts @@ -0,0 +1,24 @@ +import {describe, expect, it} from "vitest"; +import {config} from "@lodestar/config/default"; +import {RespStatus, ResponseError} from "@lodestar/reqresp"; +import {IBeaconChain} from "../../../../../src/chain/index.js"; +import {IBeaconDb} from "../../../../../src/db/index.js"; +import {getReqRespHandlers} from "../../../../../src/network/reqresp/handlers/index.js"; +import {ReqRespMethod} from "../../../../../src/network/reqresp/types.js"; + +describe("network / reqresp / handlers", () => { + it("rejects malformed DataColumnSidecarsByRoot request bytes as invalid request", () => { + const handler = getReqRespHandlers({ + chain: {config} as unknown as IBeaconChain, + db: {} as IBeaconDb, + })(ReqRespMethod.DataColumnSidecarsByRoot); + + try { + handler({data: Buffer.alloc(20), version: 1}, undefined as never, "test"); + expect.fail("expected malformed request bytes to be rejected"); + } catch (e) { + expect(e).toBeInstanceOf(ResponseError); + expect((e as ResponseError).status).toBe(RespStatus.INVALID_REQUEST); + } + }); +});