Skip to content

Commit 33ca095

Browse files
authored
fix: add special handling for blob schedule when asserting equal params (#7859)
**Motivation** Follow up to #7729, we need to handle blob schedule separately from generic string comparison of values, otherwise what we compare are shallow stringified arrays. It does not validate the object properties and on length mismatch we just get the following error ``` ✖ Error: Local and remote configs are different BLOB_SCHEDULE different value: [object object] != [object object],[object object] ``` **Description** Add special handling for blob schedule when asserting equal params - deserialize and sort blob schedules - compare length of local and remote blob schedule - then check each entry by comparing `EPOCH` and `MAX_BLOBS_PER_BLOCK`
1 parent fa41ea4 commit 33ca095

File tree

3 files changed

+75
-41
lines changed

3 files changed

+75
-41
lines changed

packages/config/src/chainConfig/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {ACTIVE_PRESET} from "@lodestar/params";
22
import {defaultChainConfig} from "./default.js";
33
import {ChainConfig} from "./types.js";
44

5-
export {chainConfigToJson, chainConfigFromJson, specValuesToJson} from "./json.js";
5+
export {chainConfigToJson, chainConfigFromJson, specValuesToJson, deserializeBlobSchedule} from "./json.js";
66
export * from "./types.js";
77
export * from "./default.js";
88

packages/config/src/chainConfig/json.ts

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {fromHex, toHex} from "@lodestar/utils";
22
import {
3+
BlobSchedule,
34
BlobScheduleEntry,
45
ChainConfig,
56
SpecJson,
@@ -103,45 +104,7 @@ export function serializeSpecValue(
103104

104105
export function deserializeSpecValue(valueStr: unknown, typeName: SpecValueTypeName, keyName: string): SpecValue {
105106
if (typeName === "blob_schedule") {
106-
if (!Array.isArray(valueStr)) {
107-
throw Error(`Invalid BLOB_SCHEDULE value ${valueStr} expected array`);
108-
}
109-
110-
const blobSchedule = valueStr.map((entry, i) => {
111-
if (typeof entry !== "object" || entry === null) {
112-
throw Error(`Invalid BLOB_SCHEDULE[${i}] entry ${entry} expected object`);
113-
}
114-
115-
const out = {} as BlobScheduleEntry;
116-
117-
for (const key of ["EPOCH", "MAX_BLOBS_PER_BLOCK"] as Array<keyof BlobScheduleEntry>) {
118-
const value = entry[key];
119-
120-
if (value === undefined) {
121-
throw Error(`Invalid BLOB_SCHEDULE[${i}] entry ${JSON.stringify(entry)} missing ${key}`);
122-
}
123-
124-
if (typeof value !== "string") {
125-
throw Error(`Invalid BLOB_SCHEDULE[${i}].${key} value ${value} expected string`);
126-
}
127-
128-
if (value === MAX_UINT64_JSON) {
129-
out[key] = Infinity;
130-
} else {
131-
const parsed = parseInt(value, 10);
132-
133-
if (Number.isNaN(parsed)) {
134-
throw Error(`Invalid BLOB_SCHEDULE[${i}].${key} value ${value} expected number`);
135-
}
136-
137-
out[key] = parsed;
138-
}
139-
}
140-
141-
return out;
142-
});
143-
144-
return blobSchedule;
107+
return deserializeBlobSchedule(valueStr);
145108
}
146109

147110
if (typeof valueStr !== "string") {
@@ -165,3 +128,45 @@ export function deserializeSpecValue(valueStr: unknown, typeName: SpecValueTypeN
165128
return valueStr;
166129
}
167130
}
131+
132+
export function deserializeBlobSchedule(input: unknown): BlobSchedule {
133+
if (!Array.isArray(input)) {
134+
throw Error(`Invalid BLOB_SCHEDULE value ${input} expected array`);
135+
}
136+
137+
const blobSchedule = input.map((entry, i) => {
138+
if (typeof entry !== "object" || entry === null) {
139+
throw Error(`Invalid BLOB_SCHEDULE[${i}] entry ${entry} expected object`);
140+
}
141+
142+
const out = {} as BlobScheduleEntry;
143+
144+
for (const key of ["EPOCH", "MAX_BLOBS_PER_BLOCK"] as Array<keyof BlobScheduleEntry>) {
145+
const value = entry[key];
146+
147+
if (value === undefined) {
148+
throw Error(`Invalid BLOB_SCHEDULE[${i}] entry ${JSON.stringify(entry)} missing ${key}`);
149+
}
150+
151+
if (typeof value !== "string") {
152+
throw Error(`Invalid BLOB_SCHEDULE[${i}].${key} value ${value} expected string`);
153+
}
154+
155+
if (value === MAX_UINT64_JSON) {
156+
out[key] = Infinity;
157+
} else {
158+
const parsed = parseInt(value, 10);
159+
160+
if (Number.isNaN(parsed)) {
161+
throw Error(`Invalid BLOB_SCHEDULE[${i}].${key} value ${value} expected number`);
162+
}
163+
164+
out[key] = parsed;
165+
}
166+
}
167+
168+
return out;
169+
});
170+
171+
return blobSchedule;
172+
}

packages/validator/src/util/params.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ChainConfig, SpecJson, chainConfigToJson} from "@lodestar/config";
1+
import {BlobScheduleEntry, ChainConfig, SpecJson, chainConfigToJson, deserializeBlobSchedule} from "@lodestar/config";
22
import {BeaconPreset, activePreset, presetToJson} from "@lodestar/params";
33

44
export class NotEqualParamsError extends Error {}
@@ -52,6 +52,35 @@ export function assertEqualParams(localConfig: ChainConfig, externalSpecJson: Sp
5252
continue;
5353
}
5454

55+
if (key === "BLOB_SCHEDULE") {
56+
const localBlobSchedule = deserializeBlobSchedule(localSpecJson[key]).sort((a, b) => a.EPOCH - b.EPOCH);
57+
const remoteBlobSchedule = deserializeBlobSchedule(externalSpecJson[key]).sort((a, b) => a.EPOCH - b.EPOCH);
58+
59+
if (localBlobSchedule.length !== remoteBlobSchedule.length) {
60+
errors.push(`BLOB_SCHEDULE different length: ${localBlobSchedule.length} != ${remoteBlobSchedule.length}`);
61+
62+
// Skip per entry comparison
63+
continue;
64+
}
65+
66+
for (let i = 0; i < localBlobSchedule.length; i++) {
67+
const localEntry = localBlobSchedule[i];
68+
const remoteEntry = remoteBlobSchedule[i];
69+
70+
for (const entryKey of ["EPOCH", "MAX_BLOBS_PER_BLOCK"] as Array<keyof BlobScheduleEntry>) {
71+
const localValue = String(localEntry[entryKey]);
72+
const remoteValue = String(remoteEntry[entryKey]);
73+
74+
if (localValue !== remoteValue) {
75+
errors.push(`BLOB_SCHEDULE[${i}].${entryKey} different value: ${localValue} != ${remoteValue}`);
76+
}
77+
}
78+
}
79+
80+
// Skip generic string comparison
81+
continue;
82+
}
83+
5584
// Must compare JSON serialized specs, to ensure all strings are rendered in the same way
5685
// Must compare as lowercase to ensure checksum addresses and names have same capilatization
5786
const localValue = String(localSpecJson[key]).toLocaleLowerCase();

0 commit comments

Comments
 (0)