@lodestar/beacon-node 1.49.0 → 1.50.0-dev.55c1d0c0d1
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/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +28 -0
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +21 -2
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +5 -4
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/jsonRpcHttpClient.d.ts +2 -1
- package/lib/execution/engine/jsonRpcHttpClient.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcHttpClient.js +6 -1
- package/lib/execution/engine/jsonRpcHttpClient.js.map +1 -1
- package/lib/execution/engine/transportMetrics.d.ts +13 -0
- package/lib/execution/engine/transportMetrics.d.ts.map +1 -0
- package/lib/execution/engine/transportMetrics.js +2 -0
- package/lib/execution/engine/transportMetrics.js.map +1 -0
- package/lib/metrics/metrics/beacon.d.ts +1 -1
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +6 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +10 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/metadata.d.ts +1 -1
- package/lib/network/metadata.d.ts.map +1 -1
- package/lib/network/metadata.js +17 -17
- package/lib/network/metadata.js.map +1 -1
- package/lib/network/peers/datastore.d.ts +9 -9
- package/lib/network/peers/datastore.d.ts.map +1 -1
- package/lib/network/peers/datastore.js +36 -36
- package/lib/network/peers/datastore.js.map +1 -1
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +6 -6
- package/lib/util/execution.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/state/index.ts +36 -0
- package/src/chain/archiveStore/archiveStore.ts +21 -2
- package/src/execution/engine/http.ts +6 -4
- package/src/execution/engine/jsonRpcHttpClient.ts +9 -2
- package/src/execution/engine/transportMetrics.ts +9 -0
- package/src/metrics/metrics/beacon.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +10 -0
- package/src/network/metadata.ts +17 -17
- package/src/network/peers/datastore.ts +36 -36
- package/src/util/execution.ts +10 -6
package/src/network/metadata.ts
CHANGED
|
@@ -42,13 +42,13 @@ export class MetadataController {
|
|
|
42
42
|
private onSetValue: (key: string, value: Uint8Array) => void;
|
|
43
43
|
private networkConfig: NetworkConfig;
|
|
44
44
|
private logger: Logger;
|
|
45
|
-
private
|
|
45
|
+
private metadata: fulu.Metadata;
|
|
46
46
|
|
|
47
47
|
constructor(opts: MetadataOpts, modules: MetadataModules) {
|
|
48
48
|
this.networkConfig = modules.networkConfig;
|
|
49
49
|
this.logger = modules.logger;
|
|
50
50
|
this.onSetValue = modules.onSetValue;
|
|
51
|
-
this.
|
|
51
|
+
this.metadata = opts.metadata ?? {
|
|
52
52
|
...ssz.fulu.Metadata.defaultValue(),
|
|
53
53
|
custodyGroupCount: modules.networkConfig.custodyConfig.targetCustodyGroupCount,
|
|
54
54
|
};
|
|
@@ -58,61 +58,61 @@ export class MetadataController {
|
|
|
58
58
|
// updateEth2Field() MUST be called with clock epoch
|
|
59
59
|
this.updateEth2Field(currentEpoch);
|
|
60
60
|
|
|
61
|
-
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.
|
|
61
|
+
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.metadata.attnets));
|
|
62
62
|
|
|
63
63
|
const config = this.networkConfig.config;
|
|
64
64
|
|
|
65
65
|
if (config.getForkSeq(computeStartSlotAtEpoch(currentEpoch)) >= ForkSeq.altair) {
|
|
66
66
|
// Only persist syncnets if altair fork is already activated. If currentFork is altair but head is phase0
|
|
67
67
|
// adding syncnets to the ENR is not a problem, we will just have a useless field for a few hours.
|
|
68
|
-
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.
|
|
68
|
+
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.metadata.syncnets));
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Set CGC regardless of fork. It may be useful to clients before Fulu, and will be ignored otherwise.
|
|
72
|
-
this.onSetValue(ENRKey.cgc, serializeCgc(this.
|
|
72
|
+
this.onSetValue(ENRKey.cgc, serializeCgc(this.metadata.custodyGroupCount));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
get seqNumber(): bigint {
|
|
76
|
-
return this.
|
|
76
|
+
return this.metadata.seqNumber;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
get syncnets(): BitArray {
|
|
80
|
-
return this.
|
|
80
|
+
return this.metadata.syncnets;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
set syncnets(syncnets: BitArray) {
|
|
84
84
|
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(syncnets));
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
85
|
+
this.metadata.seqNumber++;
|
|
86
|
+
this.metadata.syncnets = syncnets;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
get attnets(): BitArray {
|
|
90
|
-
return this.
|
|
90
|
+
return this.metadata.attnets;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
set attnets(attnets: BitArray) {
|
|
94
94
|
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(attnets));
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
95
|
+
this.metadata.seqNumber++;
|
|
96
|
+
this.metadata.attnets = attnets;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
get custodyGroupCount(): number {
|
|
100
|
-
return this.
|
|
100
|
+
return this.metadata.custodyGroupCount;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
set custodyGroupCount(custodyGroupCount: number) {
|
|
104
|
-
if (custodyGroupCount === this.
|
|
104
|
+
if (custodyGroupCount === this.metadata.custodyGroupCount) {
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
this.onSetValue(ENRKey.cgc, serializeCgc(custodyGroupCount));
|
|
108
108
|
this.logger.debug("Updated cgc field in ENR", {custodyGroupCount});
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
109
|
+
this.metadata.seqNumber++;
|
|
110
|
+
this.metadata.custodyGroupCount = custodyGroupCount;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** Consumers that need the phase0.Metadata type can just ignore the .syncnets property */
|
|
114
114
|
get json(): fulu.Metadata {
|
|
115
|
-
return this.
|
|
115
|
+
return this.metadata;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
@@ -16,23 +16,23 @@ type AwaitGenerator<T, TReturn = any, TNext = any> = Generator<T, TReturn, TNext
|
|
|
16
16
|
* This has a memory issue because all peer data stays in memory and loaded at startup time
|
|
17
17
|
* This is written for libp2p >=0.35, we maintain the same mechanism but with bounded data structure
|
|
18
18
|
* This datastore includes a memory datastore and fallback to db datastore
|
|
19
|
-
* Use an in-memory datastore with last accessed time and
|
|
19
|
+
* Use an in-memory datastore with last accessed time and maxMemoryItems, on start it's empty (lazy load)
|
|
20
20
|
* - get: Search in-memory datastore first, if not found search from db.
|
|
21
21
|
* - If found from db, add back to the in-memory datastore
|
|
22
22
|
* - Update lastAccessedMs
|
|
23
|
-
* - put: move oldest items from memory to db if there are more than
|
|
24
|
-
* - update memory datastore, only update db datastore if there are at least
|
|
23
|
+
* - put: move oldest items from memory to db if there are more than maxMemoryItems items in memory
|
|
24
|
+
* - update memory datastore, only update db datastore if there are at least threshold dirty items
|
|
25
25
|
* - Update lastAccessedMs
|
|
26
26
|
*/
|
|
27
27
|
export class Eth2PeerDataStore extends BaseDatastore {
|
|
28
|
-
private
|
|
29
|
-
private
|
|
28
|
+
private dbDatastore: LevelDatastore;
|
|
29
|
+
private memoryDatastore: Map<string, MemoryItem>;
|
|
30
30
|
/** Same to PersistentPeerStore of the old libp2p implementation */
|
|
31
|
-
private
|
|
31
|
+
private dirtyItems = new Set<string>();
|
|
32
32
|
/** If there are more dirty items than threshold, commit data to db */
|
|
33
|
-
private
|
|
33
|
+
private threshold: number;
|
|
34
34
|
/** If there are more memory items than this, prune oldest ones from memory and move to db */
|
|
35
|
-
private
|
|
35
|
+
private maxMemoryItems: number;
|
|
36
36
|
|
|
37
37
|
constructor(
|
|
38
38
|
dbDatastore: LevelDatastore | string,
|
|
@@ -47,18 +47,18 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
47
47
|
throw Error(`Threshold ${threshold} should be at most maxMemoryItems ${maxMemoryItems}`);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
50
|
+
this.dbDatastore = typeof dbDatastore === "string" ? new LevelDatastore(dbDatastore) : dbDatastore;
|
|
51
|
+
this.memoryDatastore = new Map();
|
|
52
|
+
this.threshold = threshold;
|
|
53
|
+
this.maxMemoryItems = maxMemoryItems;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async open(): Promise<void> {
|
|
57
|
-
return this.
|
|
57
|
+
return this.dbDatastore.open();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
async close(): Promise<void> {
|
|
61
|
-
return this.
|
|
61
|
+
return this.dbDatastore.close();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
async put(key: Key, val: Uint8Array, _options?: AbortOptions): Promise<Key> {
|
|
@@ -67,23 +67,23 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Same interface to put with "fromDb" option, if this item is updated back from db
|
|
70
|
-
* Move oldest items from memory data store to db if it's over this.
|
|
70
|
+
* Move oldest items from memory data store to db if it's over this.maxMemoryItems
|
|
71
71
|
*/
|
|
72
72
|
async _put(key: Key, val: Uint8Array, fromDb = false): Promise<Key> {
|
|
73
|
-
while (this.
|
|
73
|
+
while (this.memoryDatastore.size >= this.maxMemoryItems) {
|
|
74
74
|
// it's likely this is called only 1 time
|
|
75
75
|
await this.pruneMemoryDatastore();
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const keyStr = key.toString();
|
|
79
|
-
const memoryItem = this.
|
|
79
|
+
const memoryItem = this.memoryDatastore.get(keyStr);
|
|
80
80
|
if (memoryItem) {
|
|
81
81
|
// update existing
|
|
82
82
|
memoryItem.lastAccessedMs = Date.now();
|
|
83
83
|
memoryItem.data = val;
|
|
84
84
|
} else {
|
|
85
85
|
// new
|
|
86
|
-
this.
|
|
86
|
+
this.memoryDatastore.set(keyStr, {data: val, lastAccessedMs: Date.now()});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if (!fromDb) await this._addDirtyItem(keyStr);
|
|
@@ -98,15 +98,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
98
98
|
*/
|
|
99
99
|
async get(key: Key, options?: AbortOptions): Promise<Uint8Array> {
|
|
100
100
|
const keyStr = key.toString();
|
|
101
|
-
const memoryItem = this.
|
|
101
|
+
const memoryItem = this.memoryDatastore.get(keyStr);
|
|
102
102
|
if (memoryItem) {
|
|
103
103
|
memoryItem.lastAccessedMs = Date.now();
|
|
104
104
|
return memoryItem.data;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// this throws error if not found
|
|
108
|
-
const dbValue = await this.
|
|
109
|
-
// don't call this.
|
|
108
|
+
const dbValue = await this.dbDatastore.get(key, options);
|
|
109
|
+
// don't call this.memoryDatastore.set directly
|
|
110
110
|
// we want to get through prune() logic with fromDb as true
|
|
111
111
|
await this._put(key, dbValue, true);
|
|
112
112
|
return dbValue;
|
|
@@ -125,30 +125,30 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
async delete(key: Key, options?: AbortOptions): Promise<void> {
|
|
128
|
-
this.
|
|
129
|
-
await this.
|
|
128
|
+
this.memoryDatastore.delete(key.toString());
|
|
129
|
+
await this.dbDatastore.delete(key, options);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
async *_all(q: Query, options?: AbortOptions): AwaitGenerator<Pair> {
|
|
133
|
-
for (const [key, value] of this.
|
|
133
|
+
for (const [key, value] of this.memoryDatastore.entries()) {
|
|
134
134
|
yield {
|
|
135
135
|
key: new Key(key),
|
|
136
136
|
value: value.data,
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
yield* this.
|
|
139
|
+
yield* this.dbDatastore.query(q, options);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
async *_allKeys(q: KeyQuery, options?: AbortOptions): AwaitGenerator<Key> {
|
|
143
|
-
for (const key of this.
|
|
143
|
+
for (const key of this.memoryDatastore.keys()) {
|
|
144
144
|
yield new Key(key);
|
|
145
145
|
}
|
|
146
|
-
yield* this.
|
|
146
|
+
yield* this.dbDatastore.queryKeys(q, options);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
private async _addDirtyItem(keyStr: string): Promise<void> {
|
|
150
|
-
this.
|
|
151
|
-
if (this.
|
|
150
|
+
this.dirtyItems.add(keyStr);
|
|
151
|
+
if (this.dirtyItems.size >= this.threshold) {
|
|
152
152
|
try {
|
|
153
153
|
await this._commitData();
|
|
154
154
|
} catch (_e) {}
|
|
@@ -156,15 +156,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
private async _commitData(): Promise<void> {
|
|
159
|
-
const batch = this.
|
|
160
|
-
for (const keyStr of this.
|
|
161
|
-
const memoryItem = this.
|
|
159
|
+
const batch = this.dbDatastore.batch();
|
|
160
|
+
for (const keyStr of this.dirtyItems) {
|
|
161
|
+
const memoryItem = this.memoryDatastore.get(keyStr);
|
|
162
162
|
if (memoryItem) {
|
|
163
163
|
batch.put(new Key(keyStr), memoryItem.data);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
await batch.commit();
|
|
167
|
-
this.
|
|
167
|
+
this.dirtyItems.clear();
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
@@ -175,7 +175,7 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
175
175
|
let oldestKey: string | undefined = undefined;
|
|
176
176
|
let oldestValue: Uint8Array | undefined = undefined;
|
|
177
177
|
|
|
178
|
-
for (const [key, value] of this.
|
|
178
|
+
for (const [key, value] of this.memoryDatastore) {
|
|
179
179
|
if (value.lastAccessedMs < oldestAccessedMs) {
|
|
180
180
|
oldestAccessedMs = value.lastAccessedMs;
|
|
181
181
|
oldestKey = key;
|
|
@@ -184,8 +184,8 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
if (oldestKey && oldestValue) {
|
|
187
|
-
await this.
|
|
188
|
-
this.
|
|
187
|
+
await this.dbDatastore.put(new Key(oldestKey), oldestValue);
|
|
188
|
+
this.memoryDatastore.delete(oldestKey);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
}
|
package/src/util/execution.ts
CHANGED
|
@@ -259,8 +259,8 @@ export async function getDataColumnSidecarsFromExecution(
|
|
|
259
259
|
return DataColumnEngineResult.SuccessResolved;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
/** engine_getPayloadBodiesByHashV2:
|
|
263
|
-
const
|
|
262
|
+
/** engine_getPayloadBodiesByHashV2: every EL must accept requests of up to 32 hashes, larger ones may fail with -38004 */
|
|
263
|
+
const MAX_BODIES_PER_REQUEST = 32;
|
|
264
264
|
|
|
265
265
|
type SlotEnvelopeBytes = {slot: Slot; envelopeBytes: Uint8Array};
|
|
266
266
|
|
|
@@ -299,7 +299,7 @@ export async function* reconstructExecutionPayloadEnvelopesByRange(
|
|
|
299
299
|
|
|
300
300
|
for await (const {key, value: bytes} of archive.binaryEntriesStream({gte: startSlot, lt: endSlot})) {
|
|
301
301
|
batch.push({slot: archive.decodeKey(key), ...decodeArchivedEnvelope(bytes)});
|
|
302
|
-
if (batch.length ===
|
|
302
|
+
if (batch.length === MAX_BODIES_PER_REQUEST) {
|
|
303
303
|
yield* reconstructBatch(executionEngine, logger, metrics, batch);
|
|
304
304
|
batch = [];
|
|
305
305
|
}
|
|
@@ -359,9 +359,13 @@ export async function reconstructExecutionPayloadEnvelopes(
|
|
|
359
359
|
headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[]
|
|
360
360
|
): Promise<(gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[]> {
|
|
361
361
|
const out: (gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[] = [];
|
|
362
|
-
for (let i = 0; i < headerEnvelopes.length; i +=
|
|
362
|
+
for (let i = 0; i < headerEnvelopes.length; i += MAX_BODIES_PER_REQUEST) {
|
|
363
363
|
out.push(
|
|
364
|
-
...(await reconstructEnvelopesBatch(
|
|
364
|
+
...(await reconstructEnvelopesBatch(
|
|
365
|
+
executionEngine,
|
|
366
|
+
metrics,
|
|
367
|
+
headerEnvelopes.slice(i, i + MAX_BODIES_PER_REQUEST)
|
|
368
|
+
))
|
|
365
369
|
);
|
|
366
370
|
}
|
|
367
371
|
return out;
|
|
@@ -409,7 +413,7 @@ async function reconstructEnvelopesBatch(
|
|
|
409
413
|
withdrawals: body.withdrawals,
|
|
410
414
|
blockAccessList: body.blockAccessList,
|
|
411
415
|
});
|
|
412
|
-
metrics?.payloadEnvelopeReconstruction.envelopes.inc({result: "
|
|
416
|
+
metrics?.payloadEnvelopeReconstruction.envelopes.inc({result: "success"});
|
|
413
417
|
return envelope;
|
|
414
418
|
} catch (e) {
|
|
415
419
|
if (
|