@lodestar/era 1.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ import { open } from "node:fs/promises";
2
+ import { basename } from "node:path";
3
+ import { PublicKey, Signature, verify } from "@chainsafe/blst";
4
+ import { createCachedGenesis } from "@lodestar/config";
5
+ import { DOMAIN_BEACON_PROPOSER, SLOTS_PER_HISTORICAL_ROOT } from "@lodestar/params";
6
+ import { ssz } from "@lodestar/types";
7
+ import { E2STORE_HEADER_SIZE, EntryType, readEntry, readVersion } from "../e2s.js";
8
+ import { snappyUncompress } from "../util.js";
9
+ import { computeEraNumberFromBlockSlot, parseEraName, readAllEraIndices, readSlotFromBeaconStateBytes, } from "./util.js";
10
+ /**
11
+ * EraReader is responsible for reading and validating ERA files.
12
+ *
13
+ * See https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md
14
+ */
15
+ export class EraReader {
16
+ config;
17
+ /** The underlying file handle */
18
+ fh;
19
+ /** The era number retrieved from the file name */
20
+ eraNumber;
21
+ /** The short historical root retrieved from the file name */
22
+ shortHistoricalRoot;
23
+ /** An array of state and block indices, one per group */
24
+ groups;
25
+ constructor(config, fh, eraNumber, shortHistoricalRoot, indices) {
26
+ this.config = config;
27
+ this.fh = fh;
28
+ this.eraNumber = eraNumber;
29
+ this.shortHistoricalRoot = shortHistoricalRoot;
30
+ this.groups = indices;
31
+ }
32
+ static async open(config, path) {
33
+ const fh = await open(path, "r");
34
+ const name = basename(path);
35
+ const { configName, eraNumber, shortHistoricalRoot } = parseEraName(name);
36
+ if (config.CONFIG_NAME !== configName) {
37
+ throw new Error(`Config name mismatch: expected ${config.CONFIG_NAME}, got ${configName}`);
38
+ }
39
+ const indices = await readAllEraIndices(fh);
40
+ return new EraReader(config, fh, eraNumber, shortHistoricalRoot, indices);
41
+ }
42
+ /**
43
+ * Close the underlying file descriptor
44
+ *
45
+ * No further actions can be taken after this operation
46
+ */
47
+ async close() {
48
+ await this.fh.close();
49
+ }
50
+ async readCompressedState(eraNumber) {
51
+ eraNumber = eraNumber ?? this.eraNumber;
52
+ const index = this.groups.at(eraNumber - this.eraNumber);
53
+ if (!index) {
54
+ throw new Error(`No index found for era number ${eraNumber}`);
55
+ }
56
+ const entry = await readEntry(this.fh, index.stateIndex.recordStart + index.stateIndex.offsets[0]);
57
+ if (entry.type !== EntryType.CompressedBeaconState) {
58
+ throw new Error(`Expected CompressedBeaconState, got ${entry.type}`);
59
+ }
60
+ return entry.data;
61
+ }
62
+ async readSerializedState(eraNumber) {
63
+ const compressed = await this.readCompressedState(eraNumber);
64
+ return snappyUncompress(compressed);
65
+ }
66
+ async readState(eraNumber) {
67
+ const serialized = await this.readSerializedState(eraNumber);
68
+ const stateSlot = readSlotFromBeaconStateBytes(serialized);
69
+ return this.config.getForkTypes(stateSlot).BeaconState.deserialize(serialized);
70
+ }
71
+ async readCompressedBlock(slot) {
72
+ const slotEra = computeEraNumberFromBlockSlot(slot);
73
+ const index = this.groups.at(slotEra - this.eraNumber);
74
+ if (!index) {
75
+ throw new Error(`Slot ${slot} is out of range`);
76
+ }
77
+ if (!index.blocksIndex) {
78
+ throw new Error(`No block index found for era number ${slotEra}`);
79
+ }
80
+ // Calculate offset within the index
81
+ const indexOffset = slot - index.blocksIndex.startSlot;
82
+ const offset = index.blocksIndex.recordStart + index.blocksIndex.offsets[indexOffset];
83
+ if (offset === 0) {
84
+ return null; // Empty slot
85
+ }
86
+ const entry = await readEntry(this.fh, offset);
87
+ if (entry.type !== EntryType.CompressedSignedBeaconBlock) {
88
+ throw new Error(`Expected CompressedSignedBeaconBlock, got ${EntryType[entry.type] ?? "unknown"}`);
89
+ }
90
+ return entry.data;
91
+ }
92
+ async readSerializedBlock(slot) {
93
+ const compressed = await this.readCompressedBlock(slot);
94
+ if (compressed === null)
95
+ return null;
96
+ return snappyUncompress(compressed);
97
+ }
98
+ async readBlock(slot) {
99
+ const serialized = await this.readSerializedBlock(slot);
100
+ if (serialized === null)
101
+ return null;
102
+ return this.config.getForkTypes(slot).SignedBeaconBlock.deserialize(serialized);
103
+ }
104
+ /**
105
+ * Validate the era file.
106
+ * - e2s format correctness
107
+ * - era range correctness
108
+ * - network correctness for state and blocks
109
+ * - block root and signature matches
110
+ */
111
+ async validate() {
112
+ for (let groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
113
+ const eraNumber = this.eraNumber + groupIndex;
114
+ const index = this.groups[groupIndex];
115
+ // validate version entry
116
+ const start = index.blocksIndex
117
+ ? index.blocksIndex.recordStart + index.blocksIndex.offsets[0] - E2STORE_HEADER_SIZE
118
+ : index.stateIndex.recordStart + index.stateIndex.offsets[0] - E2STORE_HEADER_SIZE;
119
+ await readVersion(this.fh, start);
120
+ // validate state
121
+ // the state is loadable and consistent with the given runtime configuration
122
+ const state = await this.readState(eraNumber);
123
+ const cachedGenesis = createCachedGenesis(this.config, state.genesisValidatorsRoot);
124
+ if (eraNumber === 0 && index.blocksIndex) {
125
+ throw new Error("Genesis era (era 0) should not have blocks index");
126
+ }
127
+ if (eraNumber !== 0) {
128
+ if (!index.blocksIndex) {
129
+ throw new Error(`Era ${eraNumber} is missing blocks index`);
130
+ }
131
+ // validate blocks
132
+ for (let slot = index.blocksIndex.startSlot; slot < index.blocksIndex.startSlot + index.blocksIndex.offsets.length; slot++) {
133
+ const block = await this.readBlock(slot);
134
+ if (block === null) {
135
+ if (slot === index.blocksIndex.startSlot)
136
+ continue; // first slot in the era can't be easily validated
137
+ if (Buffer.compare(state.blockRoots[(slot - 1) % SLOTS_PER_HISTORICAL_ROOT], state.blockRoots[slot % SLOTS_PER_HISTORICAL_ROOT]) !== 0) {
138
+ throw new Error(`Block root mismatch at slot ${slot} for empty slot`);
139
+ }
140
+ continue;
141
+ }
142
+ const blockRoot = this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message);
143
+ if (Buffer.compare(blockRoot, state.blockRoots[slot % SLOTS_PER_HISTORICAL_ROOT]) !== 0) {
144
+ throw new Error(`Block root mismatch at slot ${slot}`);
145
+ }
146
+ const msg = ssz.phase0.SigningData.hashTreeRoot({
147
+ objectRoot: blockRoot,
148
+ domain: cachedGenesis.getDomain(slot, DOMAIN_BEACON_PROPOSER),
149
+ });
150
+ const pk = PublicKey.fromBytes(state.validators[block.message.proposerIndex].pubkey);
151
+ const sig = Signature.fromBytes(block.signature);
152
+ if (!verify(msg, pk, sig, true, true)) {
153
+ throw new Error(`Block signature verification failed at slot ${slot}`);
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ //# sourceMappingURL=reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/era/reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { FileHandle } from "node:fs/promises";
2
+ import { ChainForkConfig } from "@lodestar/config";
3
+ import { BeaconState, Slot } from "@lodestar/types";
4
+ import { SlotIndex } from "../e2s.ts";
5
+ /**
6
+ * Parsed components of an .era file name.
7
+ * Format: <config-name>-<era-number>-<short-historical-root>.era
8
+ */
9
+ export interface EraFileName {
10
+ /** CONFIG_NAME field of runtime config (mainnet, sepolia, holesky, etc.) */
11
+ configName: string;
12
+ /** Number of the first era stored in file, 5-digit zero-padded (00000, 00001, etc.) */
13
+ eraNumber: number;
14
+ /** First 4 bytes of last historical root, lower-case hex-encoded (8 chars) */
15
+ shortHistoricalRoot: string;
16
+ }
17
+ export interface EraIndices {
18
+ stateIndex: SlotIndex;
19
+ blocksIndex?: SlotIndex;
20
+ }
21
+ /** Return true if `slot` is within the era range */
22
+ export declare function isSlotInRange(slot: Slot, eraNumber: number): boolean;
23
+ export declare function isValidEraStateSlot(slot: Slot, eraNumber: number): boolean;
24
+ export declare function computeEraNumberFromBlockSlot(slot: Slot): number;
25
+ export declare function computeStartBlockSlotFromEraNumber(eraNumber: number): Slot;
26
+ /**
27
+ * Parse era filename.
28
+ *
29
+ * Format: `<config-name>-<era-number>-<short-historical-root>.era`
30
+ */
31
+ export declare function parseEraName(filename: string): {
32
+ configName: string;
33
+ eraNumber: number;
34
+ shortHistoricalRoot: string;
35
+ };
36
+ /**
37
+ * Read all indices from an era file.
38
+ */
39
+ export declare function readAllEraIndices(fh: FileHandle): Promise<EraIndices[]>;
40
+ /**
41
+ * Read state and block SlotIndex entries from an era file and validate alignment.
42
+ */
43
+ export declare function readEraIndexes(fh: FileHandle, end: number): Promise<EraIndices>;
44
+ export declare function readSlotFromBeaconStateBytes(beaconStateBytes: Uint8Array): Slot;
45
+ export declare function getShortHistoricalRoot(config: ChainForkConfig, state: BeaconState): string;
46
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/era/util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,WAAW,EAAE,IAAI,EAAe,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAsB,SAAS,EAAgB,MAAM,WAAW,CAAC;AAGxE;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEhE;AAED,wBAAgB,kCAAkC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAK1E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAC,CAUnH;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAY7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA2BrF;AAED,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,UAAU,GAAG,IAAI,CAO/E;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAa1F"}
@@ -0,0 +1,92 @@
1
+ import { SLOTS_PER_HISTORICAL_ROOT, isForkPostCapella } from "@lodestar/params";
2
+ import { ssz } from "@lodestar/types";
3
+ import { E2STORE_HEADER_SIZE, readSlotIndex } from "../e2s.js";
4
+ import { readUint48 } from "../util.js";
5
+ /** Return true if `slot` is within the era range */
6
+ export function isSlotInRange(slot, eraNumber) {
7
+ return computeEraNumberFromBlockSlot(slot) === eraNumber;
8
+ }
9
+ export function isValidEraStateSlot(slot, eraNumber) {
10
+ return slot % SLOTS_PER_HISTORICAL_ROOT === 0 && slot / SLOTS_PER_HISTORICAL_ROOT === eraNumber;
11
+ }
12
+ export function computeEraNumberFromBlockSlot(slot) {
13
+ return Math.floor(slot / SLOTS_PER_HISTORICAL_ROOT) + 1;
14
+ }
15
+ export function computeStartBlockSlotFromEraNumber(eraNumber) {
16
+ if (eraNumber === 0) {
17
+ throw new Error("Genesis era (era 0) does not contain blocks");
18
+ }
19
+ return (eraNumber - 1) * SLOTS_PER_HISTORICAL_ROOT;
20
+ }
21
+ /**
22
+ * Parse era filename.
23
+ *
24
+ * Format: `<config-name>-<era-number>-<short-historical-root>.era`
25
+ */
26
+ export function parseEraName(filename) {
27
+ const match = filename.match(/^(.*)-(\d{5})-([0-9a-f]{8})\.era$/);
28
+ if (!match) {
29
+ throw new Error(`Invalid era filename format: ${filename}`);
30
+ }
31
+ return {
32
+ configName: match[1],
33
+ eraNumber: parseInt(match[2], 10),
34
+ shortHistoricalRoot: match[3],
35
+ };
36
+ }
37
+ /**
38
+ * Read all indices from an era file.
39
+ */
40
+ export async function readAllEraIndices(fh) {
41
+ let end = (await fh.stat()).size;
42
+ const indices = [];
43
+ while (end > E2STORE_HEADER_SIZE) {
44
+ const index = await readEraIndexes(fh, end);
45
+ indices.push(index);
46
+ end = index.blocksIndex
47
+ ? index.blocksIndex.recordStart + index.blocksIndex.offsets[0] - E2STORE_HEADER_SIZE
48
+ : index.stateIndex.recordStart + index.stateIndex.offsets[0] - E2STORE_HEADER_SIZE;
49
+ }
50
+ return indices;
51
+ }
52
+ /**
53
+ * Read state and block SlotIndex entries from an era file and validate alignment.
54
+ */
55
+ export async function readEraIndexes(fh, end) {
56
+ const stateIndex = await readSlotIndex(fh, end);
57
+ if (stateIndex.offsets.length !== 1) {
58
+ throw new Error(`State SlotIndex must have exactly one offset, got ${stateIndex.offsets.length}`);
59
+ }
60
+ // Read block index if not genesis era (era 0)
61
+ let blocksIndex;
62
+ if (stateIndex.startSlot > 0) {
63
+ blocksIndex = await readSlotIndex(fh, stateIndex.recordStart);
64
+ if (blocksIndex.offsets.length !== SLOTS_PER_HISTORICAL_ROOT) {
65
+ throw new Error(`Block SlotIndex must have exactly ${SLOTS_PER_HISTORICAL_ROOT} offsets, got ${blocksIndex.offsets.length}`);
66
+ }
67
+ // Validate block and state indices are properly aligned
68
+ const expectedBlockStartSlot = stateIndex.startSlot - SLOTS_PER_HISTORICAL_ROOT;
69
+ if (blocksIndex.startSlot !== expectedBlockStartSlot) {
70
+ throw new Error(`Block index alignment error: expected startSlot=${expectedBlockStartSlot}, ` +
71
+ `got startSlot=${blocksIndex.startSlot} (should be exactly one era before state)`);
72
+ }
73
+ }
74
+ return { stateIndex, blocksIndex };
75
+ }
76
+ export function readSlotFromBeaconStateBytes(beaconStateBytes) {
77
+ // not technically a Uint48, but for practical purposes fits within 6 bytes
78
+ return readUint48(beaconStateBytes,
79
+ // slot is at offset 40: 8 (genesisTime) + 32 (genesisValidatorsRoot)
80
+ 40);
81
+ }
82
+ export function getShortHistoricalRoot(config, state) {
83
+ return Buffer.from(state.slot === 0
84
+ ? state.genesisValidatorsRoot
85
+ : // Post-Capella, historical_roots is replaced by historical_summaries
86
+ isForkPostCapella(config.getForkName(state.slot))
87
+ ? ssz.capella.HistoricalSummary.hashTreeRoot(state.historicalSummaries.at(-1))
88
+ : state.historicalRoots.at(-1))
89
+ .subarray(0, 4)
90
+ .toString("hex");
91
+ }
92
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/era/util.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,yBAAyB,EAAE,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAA6B,GAAG,EAAC,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAC,mBAAmB,EAAa,aAAa,EAAC,MAAM,WAAW,CAAC;AACxE,OAAO,EAAC,UAAU,EAAC,MAAM,YAAY,CAAC;AAoBtC,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,IAAU,EAAE,SAAiB;IACzD,OAAO,6BAA6B,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAU,EAAE,SAAiB;IAC/D,OAAO,IAAI,GAAG,yBAAyB,KAAK,CAAC,IAAI,IAAI,GAAG,yBAAyB,KAAK,SAAS,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,IAAU;IACtD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,SAAiB;IAClE,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,yBAAyB,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAClE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;QACpB,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACjC,mBAAmB,EAAE,KAAK,CAAC,CAAC,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EAAc;IACpD,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAEjC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,OAAO,GAAG,GAAG,mBAAmB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,GAAG,GAAG,KAAK,CAAC,WAAW;YACrB,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,mBAAmB;YACpF,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC;IACvF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAc,EAAE,GAAW;IAC9D,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAChD,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpG,CAAC;IAED,8CAA8C;IAC9C,IAAI,WAAkC,CAAC;IACvC,IAAI,UAAU,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;QAC7B,WAAW,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;QAC9D,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,KAAK,yBAAyB,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CACb,qCAAqC,yBAAyB,iBAAiB,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,CAC5G,CAAC;QACJ,CAAC;QAED,wDAAwD;QACxD,MAAM,sBAAsB,GAAG,UAAU,CAAC,SAAS,GAAG,yBAAyB,CAAC;QAChF,IAAI,WAAW,CAAC,SAAS,KAAK,sBAAsB,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,mDAAmD,sBAAsB,IAAI;gBAC3E,iBAAiB,WAAW,CAAC,SAAS,2CAA2C,CACpF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAC,UAAU,EAAE,WAAW,EAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,gBAA4B;IACvE,2EAA2E;IAC3E,OAAO,UAAU,CACf,gBAAgB;IAChB,qEAAqE;IACrE,EAAE,CACH,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAuB,EAAE,KAAkB;IAChF,OAAO,MAAM,CAAC,IAAI,CAChB,KAAK,CAAC,IAAI,KAAK,CAAC;QACd,CAAC,CAAC,KAAK,CAAC,qBAAqB;QAC7B,CAAC,CAAC,qEAAqE;YACrE,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACjD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,YAAY,CACvC,KAA6B,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAkD,CAC3G;gBACH,CAAC,CAAE,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAgB,CACnD;SACE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;SACd,QAAQ,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { type FileHandle } from "node:fs/promises";
2
+ import { ChainForkConfig } from "@lodestar/config";
3
+ import { BeaconState, SignedBeaconBlock, Slot } from "@lodestar/types";
4
+ declare enum WriterStateType {
5
+ InitGroup = 0,
6
+ WriteGroup = 1,
7
+ FinishedGroup = 2
8
+ }
9
+ type WriterState = {
10
+ type: WriterStateType.InitGroup;
11
+ eraNumber: number;
12
+ currentOffset: number;
13
+ } | {
14
+ type: WriterStateType.WriteGroup;
15
+ eraNumber: number;
16
+ currentOffset: number;
17
+ blockOffsets: number[];
18
+ lastSlot: Slot;
19
+ } | {
20
+ type: WriterStateType.FinishedGroup;
21
+ eraNumber: number;
22
+ currentOffset: number;
23
+ shortHistoricalRoot: string;
24
+ };
25
+ /**
26
+ * EraWriter is responsible for writing ERA files.
27
+ *
28
+ * See https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md
29
+ */
30
+ export declare class EraWriter {
31
+ config: ChainForkConfig;
32
+ path: string;
33
+ fh: FileHandle;
34
+ eraNumber: number;
35
+ state: WriterState;
36
+ constructor(config: ChainForkConfig, path: string, fh: FileHandle, eraNumber: number);
37
+ static create(config: ChainForkConfig, path: string, eraNumber: number): Promise<EraWriter>;
38
+ finish(): Promise<string>;
39
+ writeVersion(): Promise<void>;
40
+ writeCompressedState(slot: Slot, shortHistoricalRoot: string, data: Uint8Array): Promise<void>;
41
+ writeSerializedState(slot: Slot, shortHistoricalRoot: string, data: Uint8Array): Promise<void>;
42
+ writeState(state: BeaconState): Promise<void>;
43
+ writeCompressedBlock(slot: Slot, data: Uint8Array): Promise<void>;
44
+ writeSerializedBlock(slot: Slot, data: Uint8Array): Promise<void>;
45
+ writeBlock(block: SignedBeaconBlock): Promise<void>;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/era/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,UAAU,EAAe,MAAM,kBAAkB,CAAC;AAE/D,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,WAAW,EAAE,iBAAiB,EAAE,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAUrE,aAAK,eAAe;IAClB,SAAS,IAAA;IACT,UAAU,IAAA;IACV,aAAa,IAAA;CACd;AAED,KAAK,WAAW,GACZ;IACE,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,GACD;IACE,IAAI,EAAE,eAAe,CAAC,UAAU,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,IAAI,CAAC;CAChB,GACD;IACE,IAAI,EAAE,eAAe,CAAC,aAAa,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEN;;;;GAIG;AACH,qBAAa,SAAS;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,UAAU,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;gBAEP,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM;WAYvE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAK3F,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAgBzB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB7B,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA+C9F,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9F,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjE,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;CAM1D"}
@@ -0,0 +1,163 @@
1
+ import { open, rename } from "node:fs/promises";
2
+ import { format, parse } from "node:path";
3
+ import { SLOTS_PER_HISTORICAL_ROOT } from "@lodestar/params";
4
+ import { E2STORE_HEADER_SIZE, EntryType, serializeSlotIndex, writeEntry } from "../e2s.js";
5
+ import { snappyCompress } from "../util.js";
6
+ import { computeStartBlockSlotFromEraNumber, getShortHistoricalRoot, isSlotInRange, isValidEraStateSlot, } from "./util.js";
7
+ var WriterStateType;
8
+ (function (WriterStateType) {
9
+ WriterStateType[WriterStateType["InitGroup"] = 0] = "InitGroup";
10
+ WriterStateType[WriterStateType["WriteGroup"] = 1] = "WriteGroup";
11
+ WriterStateType[WriterStateType["FinishedGroup"] = 2] = "FinishedGroup";
12
+ })(WriterStateType || (WriterStateType = {}));
13
+ /**
14
+ * EraWriter is responsible for writing ERA files.
15
+ *
16
+ * See https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md
17
+ */
18
+ export class EraWriter {
19
+ config;
20
+ path;
21
+ fh;
22
+ eraNumber;
23
+ state;
24
+ constructor(config, path, fh, eraNumber) {
25
+ this.config = config;
26
+ this.path = path;
27
+ this.fh = fh;
28
+ this.eraNumber = eraNumber;
29
+ this.state = {
30
+ type: WriterStateType.InitGroup,
31
+ eraNumber,
32
+ currentOffset: 0,
33
+ };
34
+ }
35
+ static async create(config, path, eraNumber) {
36
+ const fh = await open(path, "w");
37
+ return new EraWriter(config, path, fh, eraNumber);
38
+ }
39
+ async finish() {
40
+ if (this.state.type !== WriterStateType.FinishedGroup) {
41
+ throw new Error("Writer has not been finished");
42
+ }
43
+ await this.fh.close();
44
+ const pathParts = parse(this.path);
45
+ const newPath = format({
46
+ ...pathParts,
47
+ base: `${this.config.CONFIG_NAME}-${String(this.eraNumber).padStart(5, "0")}-${this.state.shortHistoricalRoot}.era`,
48
+ });
49
+ await rename(this.path, newPath);
50
+ return newPath;
51
+ }
52
+ async writeVersion() {
53
+ if (this.state.type === WriterStateType.FinishedGroup) {
54
+ this.state = {
55
+ type: WriterStateType.InitGroup,
56
+ eraNumber: this.state.eraNumber + 1,
57
+ currentOffset: this.state.currentOffset,
58
+ };
59
+ }
60
+ if (this.state.type !== WriterStateType.InitGroup) {
61
+ throw new Error("Writer has already been initialized");
62
+ }
63
+ await writeEntry(this.fh, this.state.currentOffset, EntryType.Version, new Uint8Array(0));
64
+ // Move to writing blocks/state
65
+ this.state = {
66
+ type: WriterStateType.WriteGroup,
67
+ eraNumber: this.state.eraNumber,
68
+ currentOffset: this.state.currentOffset + E2STORE_HEADER_SIZE,
69
+ blockOffsets: [],
70
+ lastSlot: computeStartBlockSlotFromEraNumber(this.state.eraNumber) - 1,
71
+ };
72
+ }
73
+ async writeCompressedState(slot, shortHistoricalRoot, data) {
74
+ if (this.state.type === WriterStateType.InitGroup) {
75
+ await this.writeVersion();
76
+ }
77
+ if (this.state.type !== WriterStateType.WriteGroup) {
78
+ throw new Error("unreachable");
79
+ }
80
+ const expectedSlot = this.state.eraNumber * SLOTS_PER_HISTORICAL_ROOT;
81
+ if (!isValidEraStateSlot(slot, this.state.eraNumber)) {
82
+ throw new Error(`State slot must be ${expectedSlot} for era ${this.eraNumber}, got ${slot}`);
83
+ }
84
+ for (let s = this.state.lastSlot + 1; s < slot; s++) {
85
+ this.state.blockOffsets.push(0); // Empty slot
86
+ }
87
+ const stateOffset = this.state.currentOffset;
88
+ await writeEntry(this.fh, this.state.currentOffset, EntryType.CompressedBeaconState, data);
89
+ this.state.currentOffset += E2STORE_HEADER_SIZE + data.length;
90
+ if (this.state.eraNumber !== 0) {
91
+ const blocksIndex = {
92
+ type: EntryType.SlotIndex,
93
+ startSlot: computeStartBlockSlotFromEraNumber(this.state.eraNumber),
94
+ offsets: this.state.blockOffsets.map((o) => o - this.state.currentOffset),
95
+ recordStart: this.state.currentOffset,
96
+ };
97
+ const blocksIndexPayload = serializeSlotIndex(blocksIndex);
98
+ await writeEntry(this.fh, this.state.currentOffset, EntryType.SlotIndex, blocksIndexPayload);
99
+ this.state.currentOffset += E2STORE_HEADER_SIZE + blocksIndexPayload.length;
100
+ }
101
+ const stateIndex = {
102
+ type: EntryType.SlotIndex,
103
+ startSlot: slot,
104
+ offsets: [stateOffset - this.state.currentOffset],
105
+ recordStart: this.state.currentOffset,
106
+ };
107
+ const stateIndexPayload = serializeSlotIndex(stateIndex);
108
+ await writeEntry(this.fh, this.state.currentOffset, EntryType.SlotIndex, stateIndexPayload);
109
+ this.state.currentOffset += E2STORE_HEADER_SIZE + stateIndexPayload.length;
110
+ this.state = {
111
+ type: WriterStateType.FinishedGroup,
112
+ eraNumber: this.state.eraNumber,
113
+ currentOffset: this.state.currentOffset,
114
+ shortHistoricalRoot,
115
+ };
116
+ }
117
+ async writeSerializedState(slot, shortHistoricalRoot, data) {
118
+ const compressed = await snappyCompress(data);
119
+ await this.writeCompressedState(slot, shortHistoricalRoot, compressed);
120
+ }
121
+ async writeState(state) {
122
+ const slot = state.slot;
123
+ const shortHistoricalRoot = getShortHistoricalRoot(this.config, state);
124
+ const ssz = this.config.getForkTypes(slot).BeaconState.serialize(state);
125
+ await this.writeSerializedState(slot, shortHistoricalRoot, ssz);
126
+ }
127
+ async writeCompressedBlock(slot, data) {
128
+ if (this.state.type === WriterStateType.InitGroup) {
129
+ await this.writeVersion();
130
+ }
131
+ if (this.state.type !== WriterStateType.WriteGroup) {
132
+ throw new Error("Cannot write blocks after writing canonical state");
133
+ }
134
+ if (this.eraNumber === 0) {
135
+ throw new Error("Genesis era (era 0) does not contain blocks");
136
+ }
137
+ const blockEra = this.state.eraNumber;
138
+ if (!isSlotInRange(slot, blockEra)) {
139
+ throw new Error(`Slot ${slot} is not in valid block range for era ${blockEra}`);
140
+ }
141
+ if (slot <= this.state.lastSlot) {
142
+ throw new Error(`Slots must be written in ascending order. Last slot: ${this.state.lastSlot}, got: ${slot}`);
143
+ }
144
+ for (let s = this.state.lastSlot + 1; s < slot; s++) {
145
+ this.state.blockOffsets.push(0); // Empty slot
146
+ }
147
+ await writeEntry(this.fh, this.state.currentOffset, EntryType.CompressedSignedBeaconBlock, data);
148
+ this.state.blockOffsets.push(this.state.currentOffset);
149
+ this.state.currentOffset += E2STORE_HEADER_SIZE + data.length;
150
+ this.state.lastSlot = slot;
151
+ }
152
+ async writeSerializedBlock(slot, data) {
153
+ const compressed = await snappyCompress(data);
154
+ await this.writeCompressedBlock(slot, compressed);
155
+ }
156
+ async writeBlock(block) {
157
+ const slot = block.message.slot;
158
+ const types = this.config.getForkTypes(slot);
159
+ const ssz = types.SignedBeaconBlock.serialize(block);
160
+ await this.writeSerializedBlock(slot, ssz);
161
+ }
162
+ }
163
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/era/writer.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * as e2s from "./e2s.js";
2
+ export * as era from "./era/index.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * as e2s from "./e2s.js";
2
+ export * as era from "./era/index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC"}
package/lib/util.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /** Read 48-bit signed integer (little-endian) at offset. */
2
+ export declare function readInt48(bytes: Uint8Array, offset: number): number;
3
+ /** Read 48-bit unsigned integer (little-endian) at offset. */
4
+ export declare function readUint48(bytes: Uint8Array, offset: number): number;
5
+ /** Read 16-bit unsigned integer (little-endian) at offset. */
6
+ export declare function readUint16(bytes: Uint8Array, offset: number): number;
7
+ /** Read 32-bit unsigned integer (little-endian) at offset. */
8
+ export declare function readUint32(bytes: Uint8Array, offset: number): number;
9
+ /** Write 48-bit signed integer (little-endian) into target at offset. */
10
+ export declare function writeInt48(target: Uint8Array, offset: number, v: number): void;
11
+ /** Write 16-bit unsigned integer (little-endian) into target at offset. */
12
+ export declare function writeUint16(target: Uint8Array, offset: number, v: number): void;
13
+ /** Write 32-bit unsigned integer (little-endian) into target at offset. */
14
+ export declare function writeUint32(target: Uint8Array, offset: number, v: number): void;
15
+ /** Decompress snappy-framed data */
16
+ export declare function snappyUncompress(compressedData: Uint8Array): Uint8Array;
17
+ /** Compress data using snappy framing */
18
+ export declare function snappyCompress(data: Uint8Array): Promise<Uint8Array>;
19
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAGA,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAE9E;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,qCAAqC;AACrC,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,UAAU,GAAG,UAAU,CAWvE;AAED,yCAAyC;AACzC,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAM1E"}
package/lib/util.js ADDED
@@ -0,0 +1,49 @@
1
+ import { Uint8ArrayList } from "uint8arraylist";
2
+ import { SnappyFramesUncompress, encodeSnappy } from "@lodestar/reqresp/utils";
3
+ /** Read 48-bit signed integer (little-endian) at offset. */
4
+ export function readInt48(bytes, offset) {
5
+ return Buffer.prototype.readIntLE.call(bytes, offset, 6);
6
+ }
7
+ /** Read 48-bit unsigned integer (little-endian) at offset. */
8
+ export function readUint48(bytes, offset) {
9
+ return Buffer.prototype.readUintLE.call(bytes, offset, 6);
10
+ }
11
+ /** Read 16-bit unsigned integer (little-endian) at offset. */
12
+ export function readUint16(bytes, offset) {
13
+ return Buffer.prototype.readUint16LE.call(bytes, offset);
14
+ }
15
+ /** Read 32-bit unsigned integer (little-endian) at offset. */
16
+ export function readUint32(bytes, offset) {
17
+ return Buffer.prototype.readUint32LE.call(bytes, offset);
18
+ }
19
+ /** Write 48-bit signed integer (little-endian) into target at offset. */
20
+ export function writeInt48(target, offset, v) {
21
+ Buffer.prototype.writeIntLE.call(target, v, offset, 6);
22
+ }
23
+ /** Write 16-bit unsigned integer (little-endian) into target at offset. */
24
+ export function writeUint16(target, offset, v) {
25
+ Buffer.prototype.writeUint16LE.call(target, v, offset);
26
+ }
27
+ /** Write 32-bit unsigned integer (little-endian) into target at offset. */
28
+ export function writeUint32(target, offset, v) {
29
+ Buffer.prototype.writeUint32LE.call(target, v, offset);
30
+ }
31
+ /** Decompress snappy-framed data */
32
+ export function snappyUncompress(compressedData) {
33
+ const decompressor = new SnappyFramesUncompress();
34
+ const input = new Uint8ArrayList(compressedData);
35
+ const result = decompressor.uncompress(input);
36
+ if (result === null) {
37
+ throw new Error("Snappy decompression failed - no data returned");
38
+ }
39
+ return result.subarray();
40
+ }
41
+ /** Compress data using snappy framing */
42
+ export async function snappyCompress(data) {
43
+ const buffers = [];
44
+ for await (const chunk of encodeSnappy(Buffer.from(data.buffer, data.byteOffset, data.byteLength))) {
45
+ buffers.push(chunk);
46
+ }
47
+ return Buffer.concat(buffers);
48
+ }
49
+ //# sourceMappingURL=util.js.map