feat: serve data columns by root for finalized epochs (#8250)

**Motivation**

As per spec https://github.com/ethereum/consensus-specs/pull/4394 we
should be able to serve data columns by root for finalized epochs.

**Description**

Serve data columns by root for finalized epochs

Closes https://github.com/ChainSafe/lodestar/issues/7960
This commit is contained in:
Nico Flaig
2025-09-02 13:39:15 +01:00
committed by GitHub
parent 7b42620eba
commit 684db9fff1
3 changed files with 26 additions and 15 deletions

View File

@@ -6,7 +6,7 @@ import {fulu} from "@lodestar/types";
import {fromHex} from "@lodestar/utils";
import {IBeaconChain} from "../../../chain/index.js";
import {IBeaconDb} from "../../../db/index.js";
import {validateRequestedDataColumns} from "../utils/dataColumnResponseValidaiton.js";
import {validateRequestedDataColumns} from "../utils/dataColumnResponseValidation.js";
export async function* onDataColumnSidecarsByRange(
request: fulu.DataColumnSidecarsByRangeRequest,

View File

@@ -1,21 +1,19 @@
import {ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {fromHex, toHex} from "@lodestar/utils";
import {toRootHex} from "@lodestar/utils";
import {IBeaconChain} from "../../../chain/index.js";
import {IBeaconDb} from "../../../db/index.js";
import {DataColumnSidecarsByRootRequest} from "../../../util/types.js";
import {validateRequestedDataColumns} from "../utils/dataColumnResponseValidaiton.js";
import {validateRequestedDataColumns} from "../utils/dataColumnResponseValidation.js";
export async function* onDataColumnSidecarsByRoot(
requestBody: DataColumnSidecarsByRootRequest,
chain: IBeaconChain,
db: IBeaconDb
): AsyncIterable<ResponseOutgoing> {
// SPEC: minimum_request_epoch = max(finalized_epoch, current_epoch - MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS, FULU_FORK_EPOCH)
const finalizedEpoch = chain.forkChoice.getFinalizedCheckpoint().epoch;
// SPEC: minimum_request_epoch = max(current_epoch - MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS, FULU_FORK_EPOCH)
const currentEpoch = chain.clock.currentEpoch;
const minimumRequestEpoch = Math.max(
finalizedEpoch,
currentEpoch - chain.config.MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS,
chain.config.FULU_FORK_EPOCH
);
@@ -27,24 +25,37 @@ export async function* onDataColumnSidecarsByRoot(
return;
}
const blockRootHex = toHex(blockRoot);
const blockRootHex = toRootHex(blockRoot);
const block = chain.forkChoice.getBlockHex(blockRootHex);
// If the block is not in fork choice, it may be finalized. Attempt to find its slot in block archive
const slot = block ? block.slot : await db.blockArchive.getSlotByRoot(blockRoot);
// NOTE: Only support non-finalized blocks.
// SPEC: Clients MUST support requesting sidecars since minimum_request_epoch.
// If any root in the request content references a block earlier than minimum_request_epoch, peers MAY respond with
// error code 3: ResourceUnavailable or not include the data column sidecar in the response.
// https://github.com/ethereum/consensus-specs/blob/1937aff86b41b5171a9bc3972515986f1bbbf303/specs/fulu/p2p-interface.md#datacolumnsidecarsbyroot-v1
if (!block || computeEpochAtSlot(block.slot) < minimumRequestEpoch) {
if (slot === null) {
// We haven't seen the block
continue;
}
const dataColumns = await db.dataColumnSidecar.getManyBinary(fromHex(block.blockRoot), availableColumns);
const requestedEpoch = computeEpochAtSlot(slot);
// SPEC: Clients MUST support requesting sidecars since minimum_request_epoch.
// If any root in the request content references a block earlier than minimum_request_epoch, peers MAY respond with
// error code 3: ResourceUnavailable or not include the data column sidecar in the response.
// https://github.com/ethereum/consensus-specs/blob/v1.6.0-alpha.5/specs/fulu/p2p-interface.md#datacolumnsidecarsbyroot-v1
if (requestedEpoch < minimumRequestEpoch) {
continue;
}
const dataColumns = block
? // Non-finalized sidecars are stored by block root
await db.dataColumnSidecar.getManyBinary(blockRoot, availableColumns)
: // Finalized sidecars are archived and stored by slot
await db.dataColumnSidecarArchive.getManyBinary(slot, availableColumns);
for (const dataColumnBytes of dataColumns) {
if (dataColumnBytes) {
yield {
data: dataColumnBytes,
boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(block.slot)),
boundary: chain.config.getForkBoundaryAtEpoch(requestedEpoch),
};
}