feat: validate blob schedule on startup (#7942)

We should validate the correctness of blob schedule in addition to its
format when parsing it.


Relevant spec ethereum/consensus-specs#4370

---------

Co-authored-by: Nico Flaig <nflaig@protonmail.com>
This commit is contained in:
NC
2025-06-11 09:25:11 +02:00
committed by GitHub
parent b2b1a6dcb2
commit 6fa9960988
4 changed files with 75 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
import {fromHex, toHex} from "@lodestar/utils";
import {validateBlobSchedule} from "../utils/validateBlobSchedule.js";
import {
BlobSchedule,
BlobScheduleEntry,
@@ -168,5 +169,7 @@ export function deserializeBlobSchedule(input: unknown): BlobSchedule {
return out;
});
validateBlobSchedule(blobSchedule);
return blobSchedule;
}

View File

@@ -149,12 +149,7 @@ export function createForkConfig(config: ChainConfig): ForkConfig {
}
// Sort by epoch in descending order to find the latest applicable value
const blobSchedule = [...config.BLOB_SCHEDULE].sort((a, b) => {
if (a.EPOCH !== b.EPOCH) {
return b.EPOCH - a.EPOCH;
}
return b.MAX_BLOBS_PER_BLOCK - a.MAX_BLOBS_PER_BLOCK;
});
const blobSchedule = [...config.BLOB_SCHEDULE].sort((a, b) => b.EPOCH - a.EPOCH);
for (const entry of blobSchedule) {
if (epoch >= entry.EPOCH) {

View File

@@ -0,0 +1,32 @@
import {MAX_BLOB_COMMITMENTS_PER_BLOCK} from "@lodestar/params";
import {BlobSchedule} from "../chainConfig/types.js";
export function validateBlobSchedule(blobSchedule: BlobSchedule): void {
if (blobSchedule.length === 0) {
return;
}
let previousEpoch: number | undefined;
for (const [i, entry] of blobSchedule.entries()) {
if (previousEpoch !== undefined) {
if (entry.EPOCH < previousEpoch) {
throw Error(
`Invalid BLOB_SCHEDULE expected entries to be sorted by EPOCH in ascending order, ${entry.EPOCH} < ${previousEpoch} at index ${i}`
);
}
if (entry.EPOCH === previousEpoch) {
throw Error(
`Invalid BLOB_SCHEDULE[${i}] entry with the same epoch value ${entry.EPOCH} as previous BLOB_SCHEDULE[${i - 1}] entry`
);
}
}
if (entry.MAX_BLOBS_PER_BLOCK > MAX_BLOB_COMMITMENTS_PER_BLOCK) {
throw Error(
`Invalid BLOB_SCHEDULE[${i}].MAX_BLOBS_PER_BLOCK value ${entry.MAX_BLOBS_PER_BLOCK} exceeds limit ${MAX_BLOB_COMMITMENTS_PER_BLOCK}`
);
}
previousEpoch = entry.EPOCH;
}
}

View File

@@ -1,3 +1,4 @@
import {MAX_BLOB_COMMITMENTS_PER_BLOCK} from "@lodestar/params";
import {describe, expect, it} from "vitest";
import {chainConfig} from "../../src/default.js";
import {BlobSchedule, chainConfigFromJson, chainConfigToJson} from "../../src/index.js";
@@ -13,9 +14,8 @@ describe("chainConfig JSON", () => {
it("Custom blob schedule", () => {
const blobSchedule: BlobSchedule = [
{EPOCH: 0, MAX_BLOBS_PER_BLOCK: 10},
{EPOCH: 10, MAX_BLOBS_PER_BLOCK: Infinity},
{EPOCH: 10, MAX_BLOBS_PER_BLOCK: 15},
{EPOCH: Infinity, MAX_BLOBS_PER_BLOCK: 20},
{EPOCH: Infinity, MAX_BLOBS_PER_BLOCK: Infinity},
];
const configWithCustomBlobSchedule = {...chainConfig, BLOB_SCHEDULE: blobSchedule};
@@ -24,4 +24,41 @@ describe("chainConfig JSON", () => {
expect(chainConfigRes).toEqual(configWithCustomBlobSchedule);
});
it("Blob schedule max blobs exceeds limit", () => {
const blobSchedule: BlobSchedule = [{EPOCH: 0, MAX_BLOBS_PER_BLOCK: MAX_BLOB_COMMITMENTS_PER_BLOCK + 1}];
const configWithCustomBlobSchedule = {...chainConfig, BLOB_SCHEDULE: blobSchedule};
const json = chainConfigToJson(configWithCustomBlobSchedule);
expect(() => chainConfigFromJson(json)).toThrow();
});
it("Blob schedule in wrong order", () => {
const blobSchedule: BlobSchedule = [
{EPOCH: 20, MAX_BLOBS_PER_BLOCK: 20},
{EPOCH: 10, MAX_BLOBS_PER_BLOCK: 15},
{EPOCH: 0, MAX_BLOBS_PER_BLOCK: 10},
];
const configWithCustomBlobSchedule = {...chainConfig, BLOB_SCHEDULE: blobSchedule};
const json = chainConfigToJson(configWithCustomBlobSchedule);
expect(() => chainConfigFromJson(json)).toThrow();
});
it("Blob schedule entries with the same epoch value", () => {
const blobSchedule: BlobSchedule = [
{EPOCH: 0, MAX_BLOBS_PER_BLOCK: 10},
{EPOCH: 10, MAX_BLOBS_PER_BLOCK: 15},
{EPOCH: 10, MAX_BLOBS_PER_BLOCK: 20},
];
const configWithCustomBlobSchedule = {...chainConfig, BLOB_SCHEDULE: blobSchedule};
const json = chainConfigToJson(configWithCustomBlobSchedule);
expect(() => chainConfigFromJson(json)).toThrow();
});
});