From e06ff0a76f6c49dfe6d8fbcc2ac98efc6731218d Mon Sep 17 00:00:00 2001 From: solanaXpeter Date: Wed, 6 May 2026 21:44:24 +0800 Subject: [PATCH] fix: preserve repeated blob matches in getBlobs --- .../src/api/impl/beacon/blocks/index.ts | 23 +++------------- .../src/api/impl/beacon/blocks/utils.ts | 26 +++++++++++++++++++ .../unit/api/impl/beacon/blocks/utils.test.ts | 12 +++++++++ 3 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 packages/beacon-node/test/unit/api/impl/beacon/blocks/utils.test.ts diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index 5b6aca40d50a..e936e088efdb 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -68,7 +68,7 @@ import {kzg} from "../../../../util/kzg.js"; import {promiseAllMaybeAsync} from "../../../../util/promises.js"; import {ApiModules} from "../../types.js"; import {assertUniqueItems} from "../../utils.js"; -import {getBlockResponse, toBeaconHeaderResponse} from "./utils.js"; +import {getBlobIndicesByVersionedHashes, getBlockResponse, toBeaconHeaderResponse} from "./utils.js"; type PublishBlockOpts = ImportBlockOpts; @@ -983,15 +983,7 @@ export function getBeaconBlockApi({ const blockVersionedHashes = blobKzgCommitments.map((commitment) => toHex(kzgCommitmentToVersionedHash(commitment)) ); - indicesToReconstruct = []; - for (const requestedHash of versionedHashes) { - const index = blockVersionedHashes.findIndex((hash) => hash === requestedHash); - if (index === -1) { - throw new ApiError(400, `Versioned hash ${requestedHash} not found in block`); - } - indicesToReconstruct.push(index); - } - indicesToReconstruct.sort((a, b) => a - b); + indicesToReconstruct = getBlobIndicesByVersionedHashes(blockVersionedHashes, versionedHashes); } else { indicesToReconstruct = Array.from({length: blobCount}, (_, i) => i); } @@ -1022,16 +1014,9 @@ export function getBeaconBlockApi({ toHex(kzgCommitmentToVersionedHash(commitment)) ); - const requestedIndices: number[] = []; - for (const requestedHash of versionedHashes) { - const index = blockVersionedHashes.findIndex((hash) => hash === requestedHash); - if (index === -1) { - throw new ApiError(400, `Versioned hash ${requestedHash} not found in block`); - } - requestedIndices.push(index); - } + const requestedIndices = getBlobIndicesByVersionedHashes(blockVersionedHashes, versionedHashes); - blobs = requestedIndices.sort((a, b) => a - b).map((index) => blobs[index]); + blobs = requestedIndices.map((index) => blobs[index]); } } else { blobs = []; diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts b/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts index b27979dc4149..0004a64b2ef7 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts @@ -73,3 +73,29 @@ export async function getBlockResponse( return res; } + +export function getBlobIndicesByVersionedHashes(blockVersionedHashes: string[], requestedHashes: string[]): number[] { + const indicesByHash = new Map(); + + for (const [index, hash] of blockVersionedHashes.entries()) { + const indices = indicesByHash.get(hash); + if (indices) { + indices.push(index); + } else { + indicesByHash.set(hash, [index]); + } + } + + const requestedIndices: number[] = []; + + for (const requestedHash of requestedHashes) { + const indices = indicesByHash.get(requestedHash); + if (!indices?.length) { + throw new ApiError(400, `Versioned hash ${requestedHash} not found in block`); + } + + requestedIndices.push(...indices); + } + + return requestedIndices; +} diff --git a/packages/beacon-node/test/unit/api/impl/beacon/blocks/utils.test.ts b/packages/beacon-node/test/unit/api/impl/beacon/blocks/utils.test.ts new file mode 100644 index 000000000000..0e4cabb2b4c7 --- /dev/null +++ b/packages/beacon-node/test/unit/api/impl/beacon/blocks/utils.test.ts @@ -0,0 +1,12 @@ +import {describe, expect, it} from "vitest"; +import {getBlobIndicesByVersionedHashes} from "../../../../../../src/api/impl/beacon/blocks/utils.js"; + +describe("api - beacon - blocks utils", () => { + it("returns every matching blob index in request order", () => { + expect(getBlobIndicesByVersionedHashes(["0xaa", "0xbb", "0xaa", "0xcc"], ["0xbb", "0xaa"])).toEqual([1, 0, 2]); + }); + + it("throws when a requested versioned hash is missing", () => { + expect(() => getBlobIndicesByVersionedHashes(["0xaa"], ["0xbb"])).toThrow("Versioned hash 0xbb not found in block"); + }); +});