@lodestar/beacon-node 1.50.0-dev.0af9fb2c02 → 1.50.0-dev.0edec865c8
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.
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js +13 -0
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +23 -3
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +26 -18
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +6 -0
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +13 -23
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/chain.d.ts +4 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +20 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +1 -0
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +1 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/db/beacon.d.ts +2 -0
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +3 -0
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +2 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +1 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +2 -0
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
- package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
- package/lib/db/repositories/earliestAvailableSlot.js +19 -0
- package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
- package/lib/node/nodejs.d.ts +3 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -1
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +2 -0
- package/lib/sync/range/chain.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/index.ts +15 -0
- package/src/api/impl/beacon/state/index.ts +31 -3
- package/src/api/impl/debug/index.ts +28 -19
- package/src/api/impl/validator/index.ts +8 -0
- package/src/chain/archiveStore/archiveStore.ts +18 -24
- package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
- package/src/chain/chain.ts +32 -2
- package/src/chain/interface.ts +1 -0
- package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
- package/src/chain/options.ts +1 -1
- package/src/db/beacon.ts +3 -0
- package/src/db/buckets.ts +1 -0
- package/src/db/interface.ts +2 -0
- package/src/db/repositories/earliestAvailableSlot.ts +19 -0
- package/src/node/nodejs.ts +4 -0
- package/src/sync/range/chain.ts +3 -0
|
@@ -113,7 +113,9 @@ export class ArchiveStore {
|
|
|
113
113
|
this.logger,
|
|
114
114
|
this.metrics,
|
|
115
115
|
this.opts.anchorState.finalizedCheckpoint.epoch,
|
|
116
|
-
this.chain.clock.currentEpoch
|
|
116
|
+
this.chain.clock.currentEpoch,
|
|
117
|
+
this.statePruneFromSlot,
|
|
118
|
+
this.advanceEarliestAvailableSlot
|
|
117
119
|
),
|
|
118
120
|
() => this.logger.info("Still pruning historical data, please wait..."),
|
|
119
121
|
30_000,
|
|
@@ -122,26 +124,6 @@ export class ArchiveStore {
|
|
|
122
124
|
this.statePruneFromSlot = stateCutoffSlot;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
// Initialize earliestAvailableSlot from the earliest block actually retained (after any pruning
|
|
126
|
-
// above), rather than the anchor state slot set in the constructor. On an in-place restart the DB
|
|
127
|
-
// still holds finalized history below the anchor; leaving the value at the anchor makes the node
|
|
128
|
-
// reject by_range requests (beacon_blocks, execution_payload_envelopes and data_column_sidecars
|
|
129
|
-
// all gate on this slot) for data it still retains. An empty archive (e.g. a fresh checkpoint
|
|
130
|
-
// sync with nothing below the anchor) leaves the constructor's anchor slot in place.
|
|
131
|
-
const earliestArchivedBlockSlot = await this.db.blockArchive.firstKey();
|
|
132
|
-
if (earliestArchivedBlockSlot != null) {
|
|
133
|
-
const oldEarliestAvailableSlot = this.chain.earliestAvailableSlot;
|
|
134
|
-
this.chain.earliestAvailableSlot = earliestArchivedBlockSlot;
|
|
135
|
-
this.logger.verbose("Initialized earliestAvailableSlot from retained block archive", {
|
|
136
|
-
oldEarliestAvailableSlot,
|
|
137
|
-
newEarliestAvailableSlot: earliestArchivedBlockSlot,
|
|
138
|
-
});
|
|
139
|
-
} else {
|
|
140
|
-
this.logger.verbose("Empty block archive on init, keeping anchor earliestAvailableSlot", {
|
|
141
|
-
earliestAvailableSlot: this.chain.earliestAvailableSlot,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
127
|
if (this.opts.serveHistoricalState) {
|
|
146
128
|
this.historicalStateRegen = await HistoricalStateRegen.init({
|
|
147
129
|
opts: {
|
|
@@ -158,6 +140,18 @@ export class ArchiveStore {
|
|
|
158
140
|
}
|
|
159
141
|
}
|
|
160
142
|
|
|
143
|
+
/**
|
|
144
|
+
* The CLI owns the initial value; this only advances it during pruneHistory.
|
|
145
|
+
* Persisted before any deletion so a crash mid-prune never leaves the floor below deleted data.
|
|
146
|
+
*/
|
|
147
|
+
private advanceEarliestAvailableSlot = async (blockCutoffSlot: Slot): Promise<void> => {
|
|
148
|
+
const earliestAvailableSlot = Math.max(this.chain.earliestAvailableSlot, blockCutoffSlot);
|
|
149
|
+
if (earliestAvailableSlot !== this.chain.earliestAvailableSlot) {
|
|
150
|
+
await this.db.earliestAvailableSlot.set(earliestAvailableSlot);
|
|
151
|
+
this.chain.earliestAvailableSlot = earliestAvailableSlot;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
161
155
|
async close(): Promise<void> {
|
|
162
156
|
await this.historicalStateRegen?.close();
|
|
163
157
|
}
|
|
@@ -259,17 +253,17 @@ export class ArchiveStore {
|
|
|
259
253
|
|
|
260
254
|
if (this.opts.pruneHistory) {
|
|
261
255
|
timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
|
|
262
|
-
const {
|
|
256
|
+
const {stateCutoffSlot} = await pruneHistory(
|
|
263
257
|
this.chain.config,
|
|
264
258
|
this.db,
|
|
265
259
|
this.logger,
|
|
266
260
|
this.metrics,
|
|
267
261
|
finalizedEpoch,
|
|
268
262
|
this.chain.clock.currentEpoch,
|
|
269
|
-
this.statePruneFromSlot
|
|
263
|
+
this.statePruneFromSlot,
|
|
264
|
+
this.advanceEarliestAvailableSlot
|
|
270
265
|
);
|
|
271
266
|
this.statePruneFromSlot = stateCutoffSlot;
|
|
272
|
-
this.chain.earliestAvailableSlot = Math.max(this.chain.earliestAvailableSlot, blockCutoffSlot);
|
|
273
267
|
timer?.({source: ArchiveStoreTask.PruneHistory});
|
|
274
268
|
}
|
|
275
269
|
|
|
@@ -12,7 +12,8 @@ export async function pruneHistory(
|
|
|
12
12
|
metrics: Metrics | null | undefined,
|
|
13
13
|
finalizedEpoch: Epoch,
|
|
14
14
|
currentEpoch: Epoch,
|
|
15
|
-
statePruneFromSlot: Slot
|
|
15
|
+
statePruneFromSlot: Slot,
|
|
16
|
+
beforePrune: (blockCutoffSlot: Slot) => Promise<void>
|
|
16
17
|
): Promise<{blockCutoffSlot: Slot; stateCutoffSlot: Slot}> {
|
|
17
18
|
const blockCutoffEpoch = Math.min(
|
|
18
19
|
// set by config, with underflow protection
|
|
@@ -51,6 +52,8 @@ export async function pruneHistory(
|
|
|
51
52
|
stateSlots: prettyPrintIndices(states),
|
|
52
53
|
});
|
|
53
54
|
|
|
55
|
+
await beforePrune(blockCutoffSlot);
|
|
56
|
+
|
|
54
57
|
const step1 = metrics?.pruneHistory.pruneKeys.startTimer();
|
|
55
58
|
await Promise.all([
|
|
56
59
|
// ->
|
package/src/chain/chain.ts
CHANGED
|
@@ -244,6 +244,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
244
244
|
protected readonly blockProcessor: BlockProcessor;
|
|
245
245
|
protected readonly payloadEnvelopeProcessor: PayloadEnvelopeProcessor;
|
|
246
246
|
protected readonly db: IBeaconDb;
|
|
247
|
+
private genesisStateRoot?: RootHex | null;
|
|
247
248
|
// this is only available if nHistoricalStates is enabled
|
|
248
249
|
private readonly cpStateDatastore?: CPStateDatastore;
|
|
249
250
|
private abortController = new AbortController();
|
|
@@ -278,6 +279,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
278
279
|
validatorMonitor,
|
|
279
280
|
anchorState,
|
|
280
281
|
isAnchorStateFinalized,
|
|
282
|
+
earliestAvailableSlot,
|
|
281
283
|
executionEngine,
|
|
282
284
|
executionBuilder,
|
|
283
285
|
builderApiClientOpts,
|
|
@@ -297,6 +299,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
297
299
|
validatorMonitor: ValidatorMonitor | null;
|
|
298
300
|
anchorState: IBeaconStateView;
|
|
299
301
|
isAnchorStateFinalized: boolean;
|
|
302
|
+
earliestAvailableSlot: Slot;
|
|
300
303
|
executionEngine: IExecutionEngine;
|
|
301
304
|
executionBuilder?: IExecutionBuilder;
|
|
302
305
|
builderApiClientOpts?: BuilderApiClientOpts;
|
|
@@ -362,7 +365,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
362
365
|
logger,
|
|
363
366
|
});
|
|
364
367
|
|
|
365
|
-
this._earliestAvailableSlot =
|
|
368
|
+
this._earliestAvailableSlot = earliestAvailableSlot;
|
|
366
369
|
|
|
367
370
|
this.shufflingCache = new ShufflingCache(metrics, logger, this.opts, [
|
|
368
371
|
{
|
|
@@ -723,6 +726,17 @@ export class BeaconChain implements IBeaconChain {
|
|
|
723
726
|
}
|
|
724
727
|
}
|
|
725
728
|
|
|
729
|
+
if (this.genesisStateRoot === undefined) {
|
|
730
|
+
const genesisBlock = await this.db.blockArchive.get(GENESIS_SLOT);
|
|
731
|
+
this.genesisStateRoot = genesisBlock ? toRootHex(genesisBlock.message.stateRoot) : null;
|
|
732
|
+
}
|
|
733
|
+
if (this.genesisStateRoot === stateRoot) {
|
|
734
|
+
const state = await this.db.stateArchive.getBinary(GENESIS_SLOT);
|
|
735
|
+
if (state) {
|
|
736
|
+
return {state, executionOptimistic: false, finalized: finalizedCheckpoint.epoch !== GENESIS_EPOCH};
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
726
740
|
if (opts?.allowRegen) {
|
|
727
741
|
const state = await this.regen.getState(stateRoot, RegenCaller.restApi);
|
|
728
742
|
const block = this.forkChoice.getBlockDefaultStatus(
|
|
@@ -1524,6 +1538,22 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1524
1538
|
}
|
|
1525
1539
|
}
|
|
1526
1540
|
|
|
1541
|
+
persistExecutionPayloadEnvelope(envelope: gloas.ExecutionPayloadEnvelope): void {
|
|
1542
|
+
const blockRoot = toRootHex(envelope.beaconBlockRoot);
|
|
1543
|
+
void this.persistSszObject(
|
|
1544
|
+
"ExecutionPayloadEnvelope",
|
|
1545
|
+
ssz.gloas.ExecutionPayloadEnvelope.serialize(envelope),
|
|
1546
|
+
blockRoot,
|
|
1547
|
+
"produced_execution_payload_envelope"
|
|
1548
|
+
).catch((e) => {
|
|
1549
|
+
this.logger.error(
|
|
1550
|
+
"Error persisting produced execution payload envelope",
|
|
1551
|
+
{slot: envelope.payload.slotNumber, blockRoot},
|
|
1552
|
+
e as Error
|
|
1553
|
+
);
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1527
1557
|
/**
|
|
1528
1558
|
* Invalid state root error is critical and it causes the node to stale most of the time so we want to always
|
|
1529
1559
|
* persist preState, postState and block for further investigation.
|
|
@@ -1719,7 +1749,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1719
1749
|
// remove date suffixes in file name, and check duplicate to avoid redundant persistence
|
|
1720
1750
|
await writeIfNotExist(filepath, bytes);
|
|
1721
1751
|
|
|
1722
|
-
this.logger.debug("Persisted
|
|
1752
|
+
this.logger.debug("Persisted SSZ object", {id: logStr, filepath});
|
|
1723
1753
|
}
|
|
1724
1754
|
|
|
1725
1755
|
private onScrapeMetrics(metrics: Metrics): void {
|
package/src/chain/interface.ts
CHANGED
|
@@ -290,6 +290,7 @@ export interface IBeaconChain {
|
|
|
290
290
|
updateBeaconProposerData(epoch: Epoch, proposers: ProposerPreparationData[]): Promise<void>;
|
|
291
291
|
|
|
292
292
|
persistBlock(data: BeaconBlock | BlindedBeaconBlock, suffix?: string): void;
|
|
293
|
+
persistExecutionPayloadEnvelope(envelope: gloas.ExecutionPayloadEnvelope): void;
|
|
293
294
|
persistInvalidStateRoot(
|
|
294
295
|
preState: IBeaconStateView,
|
|
295
296
|
postState: IBeaconStateView,
|
|
@@ -6,8 +6,8 @@ import {toRootHex} from "@lodestar/utils";
|
|
|
6
6
|
*
|
|
7
7
|
* The primary consumer is `validateExecutionPayloadBid`, which looks up the matching
|
|
8
8
|
* preferences via `get(bid.slot, dependent_root)` to enforce the IGNORE-existence and
|
|
9
|
-
* REJECT-equality rules from the gloas spec. The beacon API
|
|
10
|
-
*
|
|
9
|
+
* REJECT-equality rules from the gloas spec. The beacon API `getProposerPreferences`
|
|
10
|
+
* endpoint reads from the same pool via `getAll`.
|
|
11
11
|
*
|
|
12
12
|
* `validator_index` is intentionally not part of the key: gossip validation enforces
|
|
13
13
|
* `proposers[proposalSlot % SLOTS_PER_EPOCH] === validatorIndex` against the shuffling
|
|
@@ -37,15 +37,19 @@ export class ProposerPreferencesPool {
|
|
|
37
37
|
byRoot.set(rootHex, signed);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/** API read-out: flatten across branches, optionally filtered by slot. */
|
|
41
|
-
getAll(slot?: Slot): gloas.SignedProposerPreferences[] {
|
|
42
|
-
if (slot !== undefined) {
|
|
43
|
-
const byRoot = this.bySlot.get(slot);
|
|
44
|
-
return byRoot ? Array.from(byRoot.values()) : [];
|
|
45
|
-
}
|
|
40
|
+
/** API read-out: flatten across branches, optionally filtered by slot and dependent root. */
|
|
41
|
+
getAll(slot?: Slot, dependentRootHex?: RootHex): gloas.SignedProposerPreferences[] {
|
|
46
42
|
const out: gloas.SignedProposerPreferences[] = [];
|
|
47
|
-
for (const byRoot of this.bySlot
|
|
48
|
-
|
|
43
|
+
for (const [s, byRoot] of this.bySlot) {
|
|
44
|
+
if (slot !== undefined && s !== slot) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
for (const [root, signed] of byRoot) {
|
|
48
|
+
if (dependentRootHex !== undefined && root !== dependentRootHex) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
out.push(signed);
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
return out;
|
|
51
55
|
}
|
package/src/chain/options.ts
CHANGED
|
@@ -27,8 +27,8 @@ export type IChainOptions = BlockProcessOpts &
|
|
|
27
27
|
blsVerifyAllMainThread?: boolean;
|
|
28
28
|
blsVerifyAllMultiThread?: boolean;
|
|
29
29
|
blacklistedBlocks?: string[];
|
|
30
|
-
// TODO GLOAS: add similar option for execution payload envelopes?
|
|
31
30
|
persistProducedBlocks?: boolean;
|
|
31
|
+
persistProducedPayloadEnvelopes?: boolean;
|
|
32
32
|
/** Archive finalized Gloas payload envelopes as headers (bodies reconstructed from the EL on read) */
|
|
33
33
|
dedupePayloads?: boolean;
|
|
34
34
|
persistInvalidSszObjects?: boolean;
|
package/src/db/beacon.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {FlatFileStore} from "./flatFileStore/flatFileStore.js";
|
|
|
8
8
|
import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
|
|
9
9
|
import {IBeaconDb} from "./interface.js";
|
|
10
10
|
import {CheckpointStateRepository} from "./repositories/checkpointState.js";
|
|
11
|
+
import {EarliestAvailableSlotRepository} from "./repositories/earliestAvailableSlot.js";
|
|
11
12
|
import {
|
|
12
13
|
AttesterSlashingRepository,
|
|
13
14
|
BLSToExecutionChangeRepository,
|
|
@@ -48,6 +49,7 @@ export class BeaconDb implements IBeaconDb {
|
|
|
48
49
|
|
|
49
50
|
stateArchive: StateArchiveRepository;
|
|
50
51
|
checkpointState: CheckpointStateRepository;
|
|
52
|
+
earliestAvailableSlot: EarliestAvailableSlotRepository;
|
|
51
53
|
|
|
52
54
|
voluntaryExit: VoluntaryExitRepository;
|
|
53
55
|
proposerSlashing: ProposerSlashingRepository;
|
|
@@ -83,6 +85,7 @@ export class BeaconDb implements IBeaconDb {
|
|
|
83
85
|
|
|
84
86
|
this.stateArchive = new StateArchiveRepository(config, db);
|
|
85
87
|
this.checkpointState = new CheckpointStateRepository(config, db);
|
|
88
|
+
this.earliestAvailableSlot = new EarliestAvailableSlotRepository(db);
|
|
86
89
|
this.voluntaryExit = new VoluntaryExitRepository(config, db);
|
|
87
90
|
this.blsToExecutionChange = new BLSToExecutionChangeRepository(config, db);
|
|
88
91
|
this.proposerSlashing = new ProposerSlashingRepository(config, db);
|
package/src/db/buckets.ts
CHANGED
|
@@ -72,6 +72,7 @@ export enum Bucket {
|
|
|
72
72
|
|
|
73
73
|
gloas_executionPayloadEnvelope = 59, // GLOAS BeaconBlockRoot -> SignedExecutionPayloadEnvelope
|
|
74
74
|
gloas_executionPayloadEnvelopeArchive = 60, // GLOAS Slot -> SignedExecutionPayloadEnvelope, or 0x00 + SignedExecutionPayloadHeaderEnvelope
|
|
75
|
+
earliestAvailableSlot = 61, // singleton -> Slot
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
export function getBucketNameByValue<T extends Bucket>(enumValue: T): keyof typeof Bucket {
|
package/src/db/interface.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {Slot} from "@lodestar/types";
|
|
|
3
3
|
import type {IDataColumnStore} from "./dataColumnStore.js";
|
|
4
4
|
import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
|
|
5
5
|
import {CheckpointStateRepository} from "./repositories/checkpointState.js";
|
|
6
|
+
import {EarliestAvailableSlotRepository} from "./repositories/earliestAvailableSlot.js";
|
|
6
7
|
import {
|
|
7
8
|
AttesterSlashingRepository,
|
|
8
9
|
BLSToExecutionChangeRepository,
|
|
@@ -46,6 +47,7 @@ export interface IBeaconDb {
|
|
|
46
47
|
stateArchive: StateArchiveRepository;
|
|
47
48
|
// checkpoint states
|
|
48
49
|
checkpointState: CheckpointStateRepository;
|
|
50
|
+
earliestAvailableSlot: EarliestAvailableSlotRepository;
|
|
49
51
|
|
|
50
52
|
// op pool
|
|
51
53
|
voluntaryExit: VoluntaryExitRepository;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {Db, encodeKey} from "@lodestar/db";
|
|
2
|
+
import {Slot, ssz} from "@lodestar/types";
|
|
3
|
+
import {Bucket, getBucketNameByValue} from "../buckets.js";
|
|
4
|
+
|
|
5
|
+
export class EarliestAvailableSlotRepository {
|
|
6
|
+
private readonly key = encodeKey(Bucket.earliestAvailableSlot, 0);
|
|
7
|
+
private readonly opts = {bucketId: getBucketNameByValue(Bucket.earliestAvailableSlot)};
|
|
8
|
+
|
|
9
|
+
constructor(private readonly db: Db) {}
|
|
10
|
+
|
|
11
|
+
async get(): Promise<Slot | null> {
|
|
12
|
+
const bytes = await this.db.get(this.key, this.opts);
|
|
13
|
+
return bytes === null ? null : ssz.Slot.deserialize(bytes);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async set(slot: Slot): Promise<void> {
|
|
17
|
+
await this.db.put(this.key, ssz.Slot.serialize(slot), this.opts);
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/node/nodejs.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {BeaconConfig} from "@lodestar/config";
|
|
|
14
14
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
15
15
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
16
16
|
import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
|
|
17
|
+
import {Slot} from "@lodestar/types";
|
|
17
18
|
import {sleep, toRootHex} from "@lodestar/utils";
|
|
18
19
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
19
20
|
import {BeaconRestApiServer, getApi} from "../api/index.js";
|
|
@@ -61,6 +62,7 @@ export type BeaconNodeInitModules = {
|
|
|
61
62
|
peerStoreDir?: string;
|
|
62
63
|
anchorState: IBeaconStateView;
|
|
63
64
|
isAnchorStateFinalized: boolean;
|
|
65
|
+
earliestAvailableSlot: Slot;
|
|
64
66
|
metricsRegistries?: Registry[];
|
|
65
67
|
};
|
|
66
68
|
|
|
@@ -159,6 +161,7 @@ export class BeaconNode {
|
|
|
159
161
|
peerStoreDir,
|
|
160
162
|
anchorState,
|
|
161
163
|
isAnchorStateFinalized,
|
|
164
|
+
earliestAvailableSlot,
|
|
162
165
|
metricsRegistries = [],
|
|
163
166
|
}: BeaconNodeInitModules): Promise<T> {
|
|
164
167
|
if (hasher.name !== "hashtree") {
|
|
@@ -266,6 +269,7 @@ export class BeaconNode {
|
|
|
266
269
|
validatorMonitor,
|
|
267
270
|
anchorState,
|
|
268
271
|
isAnchorStateFinalized,
|
|
272
|
+
earliestAvailableSlot,
|
|
269
273
|
executionEngine: initializeExecutionEngine(executionEngineOpts, {
|
|
270
274
|
metrics,
|
|
271
275
|
signal,
|
package/src/sync/range/chain.ts
CHANGED
|
@@ -225,6 +225,9 @@ export class SyncChain {
|
|
|
225
225
|
Math.floor((localFinalizedEpoch - this.lastEpochWithProcessBlocks) / EPOCHS_PER_BATCH) * EPOCHS_PER_BATCH;
|
|
226
226
|
this.advanceChain(lastEpochWithProcessBlocksAligned);
|
|
227
227
|
|
|
228
|
+
// stopSyncing() cleared the retry timer, but peers can still be in rate limit backoff
|
|
229
|
+
this.scheduleRateLimitBackoffRetry();
|
|
230
|
+
|
|
228
231
|
// Potentially download new batches and process pending
|
|
229
232
|
this.triggerBatchDownloader();
|
|
230
233
|
this.triggerBatchProcessor();
|