@lodestar/beacon-node 1.48.0-dev.9b2790203a → 1.48.0-dev.9e31f255e3
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/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +4 -4
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -0
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -0
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -0
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +4 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +31 -34
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +3 -2
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +9 -16
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +4 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +60 -35
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +19 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -2
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts +15 -2
- package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +10 -11
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -14
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +57 -72
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +35 -54
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +38 -13
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +10 -29
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/db/beacon.d.ts +14 -5
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +17 -3
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/dataColumnStore.d.ts +39 -0
- package/lib/db/dataColumnStore.d.ts.map +1 -0
- package/lib/db/dataColumnStore.js +81 -0
- package/lib/db/dataColumnStore.js.map +1 -0
- package/lib/db/flatFileStore/atomicWrite.d.ts +8 -0
- package/lib/db/flatFileStore/atomicWrite.d.ts.map +1 -0
- package/lib/db/flatFileStore/atomicWrite.js +61 -0
- package/lib/db/flatFileStore/atomicWrite.js.map +1 -0
- package/lib/db/flatFileStore/dcolFormat.d.ts +90 -0
- package/lib/db/flatFileStore/dcolFormat.d.ts.map +1 -0
- package/lib/db/flatFileStore/dcolFormat.js +286 -0
- package/lib/db/flatFileStore/dcolFormat.js.map +1 -0
- package/lib/db/flatFileStore/errors.d.ts +82 -0
- package/lib/db/flatFileStore/errors.d.ts.map +1 -0
- package/lib/db/flatFileStore/errors.js +32 -0
- package/lib/db/flatFileStore/errors.js.map +1 -0
- package/lib/db/flatFileStore/flatFileStore.d.ts +41 -0
- package/lib/db/flatFileStore/flatFileStore.d.ts.map +1 -0
- package/lib/db/flatFileStore/flatFileStore.js +251 -0
- package/lib/db/flatFileStore/flatFileStore.js.map +1 -0
- package/lib/db/flatFileStore/index.d.ts +4 -0
- package/lib/db/flatFileStore/index.d.ts.map +1 -0
- package/lib/db/flatFileStore/index.js +2 -0
- package/lib/db/flatFileStore/index.js.map +1 -0
- package/lib/db/flatFileStore/interface.d.ts +25 -0
- package/lib/db/flatFileStore/interface.d.ts.map +1 -0
- package/lib/db/flatFileStore/interface.js +2 -0
- package/lib/db/flatFileStore/interface.js.map +1 -0
- package/lib/db/flatFileStore/metrics.d.ts +24 -0
- package/lib/db/flatFileStore/metrics.d.ts.map +1 -0
- package/lib/db/flatFileStore/metrics.js +25 -0
- package/lib/db/flatFileStore/metrics.js.map +1 -0
- package/lib/db/flatFileStore/path.d.ts +9 -0
- package/lib/db/flatFileStore/path.d.ts.map +1 -0
- package/lib/db/flatFileStore/path.js +21 -0
- package/lib/db/flatFileStore/path.js.map +1 -0
- package/lib/db/flatFileStore/slotIndex.d.ts +14 -0
- package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -0
- package/lib/db/flatFileStore/slotIndex.js +46 -0
- package/lib/db/flatFileStore/slotIndex.js.map +1 -0
- package/lib/db/flatFileStore/snappy.d.ts +3 -0
- package/lib/db/flatFileStore/snappy.d.ts.map +1 -0
- package/lib/db/flatFileStore/snappy.js +12 -0
- package/lib/db/flatFileStore/snappy.js.map +1 -0
- package/lib/db/index.d.ts +1 -0
- package/lib/db/index.d.ts.map +1 -1
- package/lib/db/interface.d.ts +8 -1
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.d.ts +8 -1
- package/lib/db/repositories/blockArchive.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.js +65 -28
- package/lib/db/repositories/blockArchive.js.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +1 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.js +3 -7
- package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +18 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +55 -1
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/monitoring/clientStats.js +1 -1
- package/lib/monitoring/clientStats.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +0 -7
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -1
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +4 -20
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -7
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +49 -3
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +232 -98
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +112 -91
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +2 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +6 -22
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +2 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -9
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +4 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/util/sszBytes.d.ts +6 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +18 -19
- package/src/api/impl/beacon/blocks/index.ts +8 -4
- package/src/chain/archiveStore/archiveStore.ts +6 -1
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -0
- package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -0
- package/src/chain/archiveStore/historicalState/worker.ts +4 -1
- package/src/chain/archiveStore/utils/archiveBlocks.ts +34 -39
- package/src/chain/blocks/index.ts +13 -2
- package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
- package/src/chain/chain.ts +75 -34
- package/src/chain/errors/blockError.ts +22 -10
- package/src/chain/errors/dataColumnSidecarError.ts +24 -8
- package/src/chain/errors/executionPayloadEnvelope.ts +14 -11
- package/src/chain/interface.ts +2 -2
- package/src/chain/prepareNextSlot.ts +21 -17
- package/src/chain/produceBlock/produceBlockBody.ts +62 -79
- package/src/chain/validation/block.ts +35 -70
- package/src/chain/validation/dataColumnSidecar.ts +47 -14
- package/src/chain/validation/executionPayloadEnvelope.ts +10 -37
- package/src/db/beacon.ts +33 -6
- package/src/db/dataColumnStore.ts +113 -0
- package/src/db/flatFileStore/atomicWrite.ts +65 -0
- package/src/db/flatFileStore/dcolFormat.ts +389 -0
- package/src/db/flatFileStore/errors.ts +53 -0
- package/src/db/flatFileStore/flatFileStore.ts +310 -0
- package/src/db/flatFileStore/index.ts +3 -0
- package/src/db/flatFileStore/interface.ts +21 -0
- package/src/db/flatFileStore/metrics.ts +48 -0
- package/src/db/flatFileStore/path.ts +32 -0
- package/src/db/flatFileStore/slotIndex.ts +56 -0
- package/src/db/flatFileStore/snappy.ts +14 -0
- package/src/db/index.ts +1 -0
- package/src/db/interface.ts +9 -1
- package/src/db/repositories/blockArchive.ts +89 -30
- package/src/db/repositories/blockArchiveIndex.ts +4 -9
- package/src/metrics/metrics/lodestar.ts +57 -1
- package/src/monitoring/clientStats.ts +1 -1
- package/src/network/gossip/encoding.ts +0 -7
- package/src/network/gossip/topic.ts +3 -25
- package/src/network/processor/gossipHandlers.ts +11 -11
- package/src/network/processor/index.ts +263 -148
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +139 -105
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +2 -0
- package/src/network/reqresp/protocols.ts +7 -24
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +15 -7
- package/src/node/nodejs.ts +6 -1
- package/src/util/sszBytes.ts +1 -1
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
2
|
-
import {
|
|
3
|
-
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
4
|
-
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
|
|
5
|
-
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
6
|
-
MAX_WITHDRAWALS_PER_PAYLOAD,
|
|
7
|
-
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
|
|
8
|
-
} from "@lodestar/params";
|
|
9
2
|
import {
|
|
10
3
|
computeStartSlotAtEpoch,
|
|
11
4
|
getExecutionPayloadEnvelopeSignatureSet,
|
|
@@ -89,6 +82,8 @@ async function validateExecutionPayloadEnvelope(
|
|
|
89
82
|
if (block.slot !== payload.slotNumber) {
|
|
90
83
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
91
84
|
code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
|
|
85
|
+
slot: payload.slotNumber,
|
|
86
|
+
root: blockRootHex,
|
|
92
87
|
envelopeSlot: payload.slotNumber,
|
|
93
88
|
blockSlot: block.slot,
|
|
94
89
|
});
|
|
@@ -98,6 +93,8 @@ async function validateExecutionPayloadEnvelope(
|
|
|
98
93
|
if (envelope.builderIndex !== payloadInput.getBuilderIndex()) {
|
|
99
94
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
100
95
|
code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
|
|
96
|
+
slot: payload.slotNumber,
|
|
97
|
+
root: blockRootHex,
|
|
101
98
|
envelopeBuilderIndex: envelope.builderIndex,
|
|
102
99
|
bidBuilderIndex: payloadInput.getBuilderIndex(),
|
|
103
100
|
});
|
|
@@ -107,6 +104,8 @@ async function validateExecutionPayloadEnvelope(
|
|
|
107
104
|
if (toRootHex(payload.blockHash) !== payloadInput.getBlockHashHex()) {
|
|
108
105
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
109
106
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
|
|
107
|
+
slot: payload.slotNumber,
|
|
108
|
+
root: blockRootHex,
|
|
110
109
|
envelopeBlockHash: toRootHex(payload.blockHash),
|
|
111
110
|
bidBlockHash: payloadInput.getBlockHashHex(),
|
|
112
111
|
});
|
|
@@ -117,41 +116,13 @@ async function validateExecutionPayloadEnvelope(
|
|
|
117
116
|
if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
|
|
118
117
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
119
118
|
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
|
|
119
|
+
slot: payload.slotNumber,
|
|
120
|
+
root: blockRootHex,
|
|
120
121
|
envelopeRequestsRoot: toRootHex(requestsRoot),
|
|
121
122
|
bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
|
|
122
123
|
});
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
// [REJECT] The counts of `execution_requests` are within their respective limits.
|
|
126
|
-
// New in Gloas:EIP7688 — progressive lists are unbounded at the type level, so bounds
|
|
127
|
-
// are enforced here in gossip validation.
|
|
128
|
-
const {executionRequests} = envelope;
|
|
129
|
-
const requestCountLimits: [string, number, number][] = [
|
|
130
|
-
["withdrawals", executionRequests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
|
|
131
|
-
["consolidations", executionRequests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
|
|
132
|
-
["builderDeposits", executionRequests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD],
|
|
133
|
-
["builderExits", executionRequests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
|
|
134
|
-
];
|
|
135
|
-
for (const [name, count, limit] of requestCountLimits) {
|
|
136
|
-
if (count > limit) {
|
|
137
|
-
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
138
|
-
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED,
|
|
139
|
-
name,
|
|
140
|
-
count,
|
|
141
|
-
limit,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// [REJECT] The number of withdrawals is within the limit.
|
|
147
|
-
if (payload.withdrawals.length > MAX_WITHDRAWALS_PER_PAYLOAD) {
|
|
148
|
-
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
149
|
-
code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED,
|
|
150
|
-
count: payload.withdrawals.length,
|
|
151
|
-
limit: MAX_WITHDRAWALS_PER_PAYLOAD,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
126
|
// Get the block state to verify the builder's signature.
|
|
156
127
|
const blockState = await chain.regen
|
|
157
128
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
@@ -178,6 +149,8 @@ async function validateExecutionPayloadEnvelope(
|
|
|
178
149
|
if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
|
|
179
150
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
180
151
|
code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
|
|
152
|
+
slot: payload.slotNumber,
|
|
153
|
+
root: blockRootHex,
|
|
181
154
|
});
|
|
182
155
|
}
|
|
183
156
|
}
|
package/src/db/beacon.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {Db, LevelDbControllerMetrics, encodeKey} from "@lodestar/db";
|
|
3
|
+
import {Slot} from "@lodestar/types";
|
|
4
|
+
import {Logger} from "@lodestar/utils";
|
|
3
5
|
import {Bucket} from "./buckets.js";
|
|
6
|
+
import {type IDataColumnStore, LegacyDataColumnStore} from "./dataColumnStore.js";
|
|
7
|
+
import {FlatFileStore} from "./flatFileStore/flatFileStore.js";
|
|
8
|
+
import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
|
|
4
9
|
import {IBeaconDb} from "./interface.js";
|
|
5
10
|
import {CheckpointStateRepository} from "./repositories/checkpointState.js";
|
|
6
11
|
import {
|
|
@@ -24,9 +29,10 @@ import {
|
|
|
24
29
|
VoluntaryExitRepository,
|
|
25
30
|
} from "./repositories/index.js";
|
|
26
31
|
|
|
27
|
-
export type
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
export type BeaconDbOpts = {
|
|
33
|
+
dataColumnDir: string;
|
|
34
|
+
logger: Logger;
|
|
35
|
+
metrics?: FlatFileStoreMetrics | null;
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
export class BeaconDb implements IBeaconDb {
|
|
@@ -57,9 +63,14 @@ export class BeaconDb implements IBeaconDb {
|
|
|
57
63
|
|
|
58
64
|
backfilledRanges: BackfilledRanges;
|
|
59
65
|
|
|
66
|
+
readonly dataColumns: IDataColumnStore;
|
|
67
|
+
lastLegacyArchiveSlot: Slot | null = null;
|
|
68
|
+
private readonly flatFileStore: FlatFileStore;
|
|
69
|
+
|
|
60
70
|
constructor(
|
|
61
71
|
config: ChainForkConfig,
|
|
62
|
-
protected readonly db: Db
|
|
72
|
+
protected readonly db: Db,
|
|
73
|
+
opts: BeaconDbOpts
|
|
63
74
|
) {
|
|
64
75
|
// Warning: If code is ever run in the constructor, must change this stub to not extend 'packages/beacon-node/test/utils/stub/beaconDb.ts' -
|
|
65
76
|
this.block = new BlockRepository(config, db);
|
|
@@ -87,14 +98,30 @@ export class BeaconDb implements IBeaconDb {
|
|
|
87
98
|
this.syncCommitteeWitness = new SyncCommitteeWitnessRepository(config, db);
|
|
88
99
|
|
|
89
100
|
this.backfilledRanges = new BackfilledRanges(config, db);
|
|
101
|
+
|
|
102
|
+
this.flatFileStore = new FlatFileStore(opts.dataColumnDir, config, opts.logger, opts.metrics);
|
|
103
|
+
this.dataColumns = new LegacyDataColumnStore(
|
|
104
|
+
this.flatFileStore,
|
|
105
|
+
this.dataColumnSidecar,
|
|
106
|
+
this.dataColumnSidecarArchive,
|
|
107
|
+
this.blockArchive
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async init(): Promise<void> {
|
|
112
|
+
await this.flatFileStore.init();
|
|
113
|
+
const [lastLegacyKey] = await this.dataColumnSidecarArchive.keys({reverse: true, limit: 1});
|
|
114
|
+
this.lastLegacyArchiveSlot = lastLegacyKey?.prefix ?? null;
|
|
90
115
|
}
|
|
91
116
|
|
|
92
|
-
close(): Promise<void> {
|
|
117
|
+
async close(): Promise<void> {
|
|
118
|
+
await this.flatFileStore.close();
|
|
93
119
|
return this.db.close();
|
|
94
120
|
}
|
|
95
121
|
|
|
96
|
-
setMetrics(metrics: LevelDbControllerMetrics): void {
|
|
122
|
+
setMetrics(metrics: LevelDbControllerMetrics, flatFileStoreMetrics: FlatFileStoreMetrics | null = null): void {
|
|
97
123
|
this.db.setMetrics(metrics);
|
|
124
|
+
this.flatFileStore.setMetrics(flatFileStoreMetrics);
|
|
98
125
|
}
|
|
99
126
|
|
|
100
127
|
async pruneHotDb(): Promise<void> {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {ColumnIndex, DataColumnSidecar, RootHex, Slot} from "@lodestar/types";
|
|
2
|
+
import {fromHex} from "@lodestar/utils";
|
|
3
|
+
import type {IFlatFileStore} from "./flatFileStore/interface.js";
|
|
4
|
+
import type {
|
|
5
|
+
BlockArchiveRepository,
|
|
6
|
+
DataColumnSidecarArchiveRepository,
|
|
7
|
+
DataColumnSidecarRepository,
|
|
8
|
+
} from "./repositories/index.js";
|
|
9
|
+
|
|
10
|
+
export type DataColumnKey = {slot: Slot; blockRoot: RootHex};
|
|
11
|
+
export type IndexedDataColumnBytes = {index: ColumnIndex; data: Uint8Array};
|
|
12
|
+
|
|
13
|
+
type LegacyHotDataColumnStore = Pick<DataColumnSidecarRepository, "values" | "getManyBinary" | "deleteMany">;
|
|
14
|
+
type LegacyArchiveDataColumnStore = Pick<
|
|
15
|
+
DataColumnSidecarArchiveRepository,
|
|
16
|
+
"values" | "getManyBinary" | "keys" | "deleteMany"
|
|
17
|
+
>;
|
|
18
|
+
type BlockRootIndex = Pick<BlockArchiveRepository, "getSlotByRoot">;
|
|
19
|
+
|
|
20
|
+
export interface IDataColumnStore {
|
|
21
|
+
getAll(key: DataColumnKey): Promise<DataColumnSidecar[]>;
|
|
22
|
+
getManyBinary(key: DataColumnKey, indices: ColumnIndex[]): Promise<(Uint8Array | undefined)[]>;
|
|
23
|
+
putManyBinary(key: DataColumnKey, columns: IndexedDataColumnBytes[]): Promise<void>;
|
|
24
|
+
deleteMany(keys: DataColumnKey[]): Promise<void>;
|
|
25
|
+
pruneBefore(slot: Slot): Promise<Slot[]>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Bridges pre-flat-file LevelDB columns during the supported in-place upgrade window.
|
|
30
|
+
* Remove the legacy repositories after upgrades from the last pre-flat-file release are no longer supported.
|
|
31
|
+
*/
|
|
32
|
+
export class LegacyDataColumnStore implements IDataColumnStore {
|
|
33
|
+
constructor(
|
|
34
|
+
private readonly flatFiles: IFlatFileStore,
|
|
35
|
+
private readonly legacyHot: LegacyHotDataColumnStore,
|
|
36
|
+
private readonly legacyArchive: LegacyArchiveDataColumnStore,
|
|
37
|
+
private readonly blockArchive: BlockRootIndex
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
async getAll({slot, blockRoot}: DataColumnKey): Promise<DataColumnSidecar[]> {
|
|
41
|
+
const sidecars = await this.flatFiles.getDataColumns(slot, blockRoot);
|
|
42
|
+
if (sidecars.length > 0) return sidecars;
|
|
43
|
+
|
|
44
|
+
const sidecarsByIndex = new Map<ColumnIndex, DataColumnSidecar>();
|
|
45
|
+
const root = fromHex(blockRoot);
|
|
46
|
+
for (const sidecar of await this.legacyHot.values(root)) {
|
|
47
|
+
if (!sidecarsByIndex.has(sidecar.index)) sidecarsByIndex.set(sidecar.index, sidecar);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if ((await this.blockArchive.getSlotByRoot(root)) === slot) {
|
|
51
|
+
for (const sidecar of await this.legacyArchive.values(slot)) {
|
|
52
|
+
if (!sidecarsByIndex.has(sidecar.index)) sidecarsByIndex.set(sidecar.index, sidecar);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return [...sidecarsByIndex.values()].sort((a, b) => a.index - b.index);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getManyBinary({slot, blockRoot}: DataColumnKey, indices: ColumnIndex[]): Promise<(Uint8Array | undefined)[]> {
|
|
60
|
+
const flatFileSidecars = await this.flatFiles.getDataColumnsBinary(slot, blockRoot, indices);
|
|
61
|
+
if (flatFileSidecars !== null) return flatFileSidecars;
|
|
62
|
+
if (indices.length === 0) return [];
|
|
63
|
+
|
|
64
|
+
const root = fromHex(blockRoot);
|
|
65
|
+
const result = await this.legacyHot.getManyBinary(root, indices);
|
|
66
|
+
|
|
67
|
+
const missingPositions = getMissingPositions(result);
|
|
68
|
+
if (missingPositions.length === 0 || (await this.blockArchive.getSlotByRoot(root)) !== slot) return result;
|
|
69
|
+
|
|
70
|
+
const archivedSidecars = await this.legacyArchive.getManyBinary(
|
|
71
|
+
slot,
|
|
72
|
+
missingPositions.map((position) => indices[position])
|
|
73
|
+
);
|
|
74
|
+
fillMissing(result, missingPositions, archivedSidecars);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async putManyBinary({slot, blockRoot}: DataColumnKey, columns: IndexedDataColumnBytes[]): Promise<void> {
|
|
79
|
+
await this.flatFiles.putDataColumnsBinary(slot, blockRoot, columns);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async deleteMany(keys: DataColumnKey[]): Promise<void> {
|
|
83
|
+
await this.flatFiles.deleteMany(keys);
|
|
84
|
+
await this.legacyHot.deleteMany(keys.map(({blockRoot}) => fromHex(blockRoot)));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async pruneBefore(slot: Slot): Promise<Slot[]> {
|
|
88
|
+
const flatFileSlots = await this.flatFiles.pruneBefore(slot);
|
|
89
|
+
const prefixedKeys = await this.legacyArchive.keys({lt: {prefix: slot, id: 0}});
|
|
90
|
+
const slots = [...new Set(prefixedKeys.map(({prefix}) => prefix))];
|
|
91
|
+
if (slots.length > 0) await this.legacyArchive.deleteMany(slots);
|
|
92
|
+
return [...new Set([...flatFileSlots, ...slots])].sort((a, b) => a - b);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getMissingPositions(sidecars: (Uint8Array | undefined)[]): number[] {
|
|
97
|
+
const positions: number[] = [];
|
|
98
|
+
for (let i = 0; i < sidecars.length; i++) {
|
|
99
|
+
if (sidecars[i] === undefined) positions.push(i);
|
|
100
|
+
}
|
|
101
|
+
return positions;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function fillMissing(
|
|
105
|
+
result: (Uint8Array | undefined)[],
|
|
106
|
+
missingPositions: number[],
|
|
107
|
+
sidecars: (Uint8Array | null | undefined)[]
|
|
108
|
+
): void {
|
|
109
|
+
for (let i = 0; i < missingPositions.length; i++) {
|
|
110
|
+
const sidecar = sidecars[i];
|
|
111
|
+
if (sidecar != null) result[missingPositions[i]] = sidecar;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {mkdir, open, rename, rm} from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const pendingDirectorySyncs = new Map<string, string[]>();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Atomic write: write to `.part` temp file -> datasync -> rename to target.
|
|
8
|
+
* This guarantees crash-safe writes — if the process crashes mid-write,
|
|
9
|
+
* only the `.part` file is corrupted, never the target.
|
|
10
|
+
*/
|
|
11
|
+
export async function atomicWrite(targetPath: string, data: Uint8Array): Promise<void> {
|
|
12
|
+
const dir = path.dirname(targetPath);
|
|
13
|
+
const partPath = `${targetPath}.part-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
14
|
+
await mkdirDurable(dir);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const fd = await open(partPath, "wx");
|
|
18
|
+
try {
|
|
19
|
+
await fd.writeFile(data);
|
|
20
|
+
await fd.datasync();
|
|
21
|
+
} finally {
|
|
22
|
+
await fd.close();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await rename(partPath, targetPath);
|
|
26
|
+
|
|
27
|
+
// Ensure rename metadata is durable on crash.
|
|
28
|
+
await syncDirectory(dir);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
await rm(partPath, {force: true}).catch(() => {});
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function mkdirDurable(dir: string): Promise<void> {
|
|
36
|
+
const targetDir = path.resolve(dir);
|
|
37
|
+
let directoriesToSync = pendingDirectorySyncs.get(targetDir);
|
|
38
|
+
if (directoriesToSync === undefined) {
|
|
39
|
+
const firstCreatedDir = await mkdir(targetDir, {recursive: true});
|
|
40
|
+
if (firstCreatedDir === undefined) return;
|
|
41
|
+
|
|
42
|
+
let createdDir = path.resolve(firstCreatedDir);
|
|
43
|
+
directoriesToSync = [path.dirname(createdDir)];
|
|
44
|
+
while (createdDir !== targetDir) {
|
|
45
|
+
directoriesToSync.push(createdDir);
|
|
46
|
+
const [nextSegment] = path.relative(createdDir, targetDir).split(path.sep);
|
|
47
|
+
createdDir = path.join(createdDir, nextSegment);
|
|
48
|
+
}
|
|
49
|
+
pendingDirectorySyncs.set(targetDir, directoriesToSync);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const directory of directoriesToSync) {
|
|
53
|
+
await syncDirectory(directory);
|
|
54
|
+
}
|
|
55
|
+
pendingDirectorySyncs.delete(targetDir);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function syncDirectory(dir: string): Promise<void> {
|
|
59
|
+
const dirFd = await open(dir, "r");
|
|
60
|
+
try {
|
|
61
|
+
await dirFd.sync();
|
|
62
|
+
} finally {
|
|
63
|
+
await dirFd.close();
|
|
64
|
+
}
|
|
65
|
+
}
|