@lodestar/beacon-node 1.42.0-dev.5f2fffc2ce → 1.42.0-dev.7552832620
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 +35 -16
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/chain/blocks/blockInput/types.d.ts +3 -3
- package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +18 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +48 -0
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -0
- package/lib/chain/blocks/importExecutionPayload.js +159 -0
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.d.ts +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.js +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +80 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +248 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +29 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +11 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +15 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +46 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -0
- package/lib/chain/blocks/types.d.ts +7 -0
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +12 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +40 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -0
- package/lib/chain/chain.d.ts +7 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +28 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +12 -2
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +3 -1
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +0 -10
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +6 -3
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +6 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +1 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +1 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +2 -2
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +38 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +76 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -0
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +30 -19
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/lightClientFinalityUpdate.js +1 -1
- package/lib/chain/validation/lightClientFinalityUpdate.js.map +1 -1
- package/lib/chain/validation/lightClientOptimisticUpdate.js +1 -1
- package/lib/chain/validation/lightClientOptimisticUpdate.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +2 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +4 -1
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -2
- package/lib/metrics/metrics/lodestar.d.ts +28 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +74 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/network.js +2 -2
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/extractSlotRootFns.d.ts.map +1 -1
- package/lib/network/processor/extractSlotRootFns.js +14 -4
- package/lib/network/processor/extractSlotRootFns.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +31 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
- package/lib/sync/backfill/backfill.d.ts +1 -1
- package/lib/sync/backfill/backfill.js +1 -1
- package/lib/sync/constants.d.ts +1 -1
- package/lib/sync/constants.js +1 -1
- package/lib/util/sszBytes.d.ts +4 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +69 -12
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +36 -17
- package/src/chain/blocks/blockInput/types.ts +3 -3
- package/src/chain/blocks/importBlock.ts +36 -2
- package/src/chain/blocks/importExecutionPayload.ts +241 -0
- package/src/chain/blocks/payloadEnvelopeInput/index.ts +2 -0
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +336 -0
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +33 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +61 -0
- package/src/chain/blocks/types.ts +8 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +55 -0
- package/src/chain/chain.ts +37 -3
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -2
- package/src/chain/forkChoice/index.ts +0 -10
- package/src/chain/interface.ts +6 -3
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/produceBlock/computeNewStateRoot.ts +6 -1
- package/src/chain/produceBlock/produceBlockBody.ts +1 -1
- package/src/chain/regen/interface.ts +2 -0
- package/src/chain/seenCache/index.ts +1 -1
- package/src/chain/seenCache/seenGossipBlockInput.ts +2 -2
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +106 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +38 -25
- package/src/chain/validation/lightClientFinalityUpdate.ts +1 -1
- package/src/chain/validation/lightClientOptimisticUpdate.ts +1 -1
- package/src/chain/validatorMonitor.ts +11 -1
- package/src/execution/engine/interface.ts +2 -2
- package/src/metrics/metrics/lodestar.ts +77 -0
- package/src/network/network.ts +2 -2
- package/src/network/processor/extractSlotRootFns.ts +18 -5
- package/src/network/processor/gossipHandlers.ts +37 -1
- package/src/network/reqresp/ReqRespBeaconNode.ts +1 -1
- package/src/sync/backfill/backfill.ts +1 -1
- package/src/sync/constants.ts +1 -1
- package/src/util/sszBytes.ts +90 -10
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts +0 -15
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts.map +0 -1
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js +0 -28
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js.map +0 -1
- package/src/chain/seenCache/seenExecutionPayloadEnvelope.ts +0 -34
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {BeaconChain} from "../chain.js";
|
|
2
|
+
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
3
|
+
import {writeDataColumnsToDb} from "./writeBlockInputToDb.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Persists payload envelope data to DB. This operation must be eventually completed if a payload is imported.
|
|
7
|
+
*
|
|
8
|
+
* TODO GLOAS: Persist envelope metadata (stateRoot, executionRequests, builderIndex, etc.) without the full
|
|
9
|
+
* execution payload body — only keep the blockHash reference. The EL already stores the payload.
|
|
10
|
+
* See https://github.com/ChainSafe/lodestar/issues/5671
|
|
11
|
+
*/
|
|
12
|
+
export async function writePayloadEnvelopeInputToDb(
|
|
13
|
+
this: BeaconChain,
|
|
14
|
+
payloadInput: PayloadEnvelopeInput
|
|
15
|
+
): Promise<void> {
|
|
16
|
+
const envelope = payloadInput.getPayloadEnvelope();
|
|
17
|
+
const blockRootHex = payloadInput.blockRootHex;
|
|
18
|
+
|
|
19
|
+
const envelopeBytes = this.serializedCache.get(envelope);
|
|
20
|
+
const envelopePromise = envelopeBytes
|
|
21
|
+
? this.db.executionPayloadEnvelope.putBinary(this.db.executionPayloadEnvelope.getId(envelope), envelopeBytes)
|
|
22
|
+
: this.db.executionPayloadEnvelope.add(envelope);
|
|
23
|
+
|
|
24
|
+
// Write envelope and data columns in parallel (reuses shared column writing logic)
|
|
25
|
+
await Promise.all([envelopePromise, writeDataColumnsToDb.call(this, payloadInput)]);
|
|
26
|
+
this.logger.debug("Persisted payload envelope to db", {
|
|
27
|
+
slot: payloadInput.slot,
|
|
28
|
+
root: blockRootHex,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function persistPayloadEnvelopeInput(
|
|
33
|
+
this: BeaconChain,
|
|
34
|
+
payloadInput: PayloadEnvelopeInput
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
await writePayloadEnvelopeInputToDb
|
|
37
|
+
.call(this, payloadInput)
|
|
38
|
+
.catch((e) => {
|
|
39
|
+
this.logger.error(
|
|
40
|
+
"Error persisting payload envelope in hot db",
|
|
41
|
+
{
|
|
42
|
+
slot: payloadInput.slot,
|
|
43
|
+
root: payloadInput.blockRootHex,
|
|
44
|
+
},
|
|
45
|
+
e
|
|
46
|
+
);
|
|
47
|
+
})
|
|
48
|
+
.finally(() => {
|
|
49
|
+
this.seenPayloadEnvelopeInputCache.prune(payloadInput.blockRootHex);
|
|
50
|
+
this.logger.debug("Pruned payload envelope input", {
|
|
51
|
+
slot: payloadInput.slot,
|
|
52
|
+
root: payloadInput.blockRootHex,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -84,7 +84,10 @@ import {CheckpointBalancesCache} from "./balancesCache.js";
|
|
|
84
84
|
import {BeaconProposerCache} from "./beaconProposerCache.js";
|
|
85
85
|
import {IBlockInput, isBlockInputBlobs, isBlockInputColumns} from "./blocks/blockInput/index.js";
|
|
86
86
|
import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js";
|
|
87
|
+
import {PayloadEnvelopeProcessor} from "./blocks/payloadEnvelopeProcessor.js";
|
|
88
|
+
import {ImportPayloadOpts} from "./blocks/types.js";
|
|
87
89
|
import {persistBlockInput} from "./blocks/writeBlockInputToDb.js";
|
|
90
|
+
import {persistPayloadEnvelopeInput} from "./blocks/writePayloadEnvelopeInputToDb.js";
|
|
88
91
|
import {BlsMultiThreadWorkerPool, BlsSingleThreadVerifier, IBlsVerifier} from "./bls/index.js";
|
|
89
92
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
90
93
|
import {ChainEvent, ChainEventEmitter} from "./emitter.js";
|
|
@@ -109,13 +112,14 @@ import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produ
|
|
|
109
112
|
import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
|
|
110
113
|
import {ReprocessController} from "./reprocess.js";
|
|
111
114
|
import {
|
|
115
|
+
PayloadEnvelopeInput,
|
|
112
116
|
SeenAggregators,
|
|
113
117
|
SeenAttesters,
|
|
114
118
|
SeenBlockProposers,
|
|
115
119
|
SeenContributionAndProof,
|
|
116
120
|
SeenExecutionPayloadBids,
|
|
117
|
-
SeenExecutionPayloadEnvelopes,
|
|
118
121
|
SeenPayloadAttesters,
|
|
122
|
+
SeenPayloadEnvelopeInput,
|
|
119
123
|
SeenSyncCommitteeMessages,
|
|
120
124
|
} from "./seenCache/index.js";
|
|
121
125
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -148,6 +152,13 @@ const DEFAULT_MAX_CACHED_PRODUCED_RESULTS = 4;
|
|
|
148
152
|
*/
|
|
149
153
|
const DEFAULT_MAX_PENDING_UNFINALIZED_BLOCK_WRITES = 16;
|
|
150
154
|
|
|
155
|
+
/**
|
|
156
|
+
* The maximum number of pending unfinalized payload envelope writes to the database before backpressure is applied.
|
|
157
|
+
* Payload envelope write queue entries hold references to payload inputs (including columns),
|
|
158
|
+
* keeping them in memory. Keep moderate to avoid OOM during sync.
|
|
159
|
+
*/
|
|
160
|
+
const DEFAULT_MAX_PENDING_UNFINALIZED_PAYLOAD_ENVELOPE_WRITES = 16;
|
|
161
|
+
|
|
151
162
|
export class BeaconChain implements IBeaconChain {
|
|
152
163
|
readonly genesisTime: UintNum64;
|
|
153
164
|
readonly genesisValidatorsRoot: Root;
|
|
@@ -172,6 +183,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
172
183
|
readonly reprocessController: ReprocessController;
|
|
173
184
|
readonly archiveStore: ArchiveStore;
|
|
174
185
|
readonly unfinalizedBlockWrites: JobItemQueue<[IBlockInput], void>;
|
|
186
|
+
readonly unfinalizedPayloadEnvelopeWrites: JobItemQueue<[PayloadEnvelopeInput], void>;
|
|
175
187
|
|
|
176
188
|
// Ops pool
|
|
177
189
|
readonly attestationPool: AttestationPool;
|
|
@@ -187,13 +199,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
187
199
|
readonly seenAggregators = new SeenAggregators();
|
|
188
200
|
readonly seenPayloadAttesters = new SeenPayloadAttesters();
|
|
189
201
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
190
|
-
readonly seenExecutionPayloadEnvelopes = new SeenExecutionPayloadEnvelopes();
|
|
191
202
|
readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
|
|
192
203
|
readonly seenBlockProposers = new SeenBlockProposers();
|
|
193
204
|
readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
|
|
194
205
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
195
206
|
readonly seenAttestationDatas: SeenAttestationDatas;
|
|
196
207
|
readonly seenBlockInputCache: SeenBlockInput;
|
|
208
|
+
readonly seenPayloadEnvelopeInputCache: SeenPayloadEnvelopeInput;
|
|
197
209
|
// Seen cache for liveness checks
|
|
198
210
|
readonly seenBlockAttesters = new SeenBlockAttesters();
|
|
199
211
|
|
|
@@ -221,6 +233,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
221
233
|
readonly opts: IChainOptions;
|
|
222
234
|
|
|
223
235
|
protected readonly blockProcessor: BlockProcessor;
|
|
236
|
+
protected readonly payloadEnvelopeProcessor: PayloadEnvelopeProcessor;
|
|
224
237
|
protected readonly db: IBeaconDb;
|
|
225
238
|
// this is only available if nHistoricalStates is enabled
|
|
226
239
|
private readonly cpStateDatastore?: CPStateDatastore;
|
|
@@ -334,6 +347,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
334
347
|
metrics,
|
|
335
348
|
logger,
|
|
336
349
|
});
|
|
350
|
+
this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
|
|
351
|
+
chainEvents: emitter,
|
|
352
|
+
signal,
|
|
353
|
+
serializedCache: this.serializedCache,
|
|
354
|
+
metrics,
|
|
355
|
+
logger,
|
|
356
|
+
});
|
|
337
357
|
|
|
338
358
|
this._earliestAvailableSlot = anchorState.slot;
|
|
339
359
|
|
|
@@ -411,6 +431,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
411
431
|
this.reprocessController = new ReprocessController(this.metrics);
|
|
412
432
|
|
|
413
433
|
this.blockProcessor = new BlockProcessor(this, metrics, opts, signal);
|
|
434
|
+
this.payloadEnvelopeProcessor = new PayloadEnvelopeProcessor(this, metrics, signal);
|
|
414
435
|
|
|
415
436
|
this.forkChoice = forkChoice;
|
|
416
437
|
this.clock = clock;
|
|
@@ -447,6 +468,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
447
468
|
metrics?.unfinalizedBlockWritesQueue
|
|
448
469
|
);
|
|
449
470
|
|
|
471
|
+
this.unfinalizedPayloadEnvelopeWrites = new JobItemQueue(
|
|
472
|
+
persistPayloadEnvelopeInput.bind(this),
|
|
473
|
+
{
|
|
474
|
+
maxLength: DEFAULT_MAX_PENDING_UNFINALIZED_PAYLOAD_ENVELOPE_WRITES,
|
|
475
|
+
signal,
|
|
476
|
+
},
|
|
477
|
+
metrics?.unfinalizedPayloadEnvelopeWritesQueue
|
|
478
|
+
);
|
|
479
|
+
|
|
450
480
|
// always run PrepareNextSlotScheduler except for fork_choice spec tests
|
|
451
481
|
if (!opts?.disablePrepareNextSlot) {
|
|
452
482
|
new PrepareNextSlotScheduler(this, this.config, metrics, this.logger, signal);
|
|
@@ -477,6 +507,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
477
507
|
// we can abort any ongoing unfinalized block writes.
|
|
478
508
|
// TODO: persist fork choice to disk and allow unfinalized block writes to complete.
|
|
479
509
|
this.unfinalizedBlockWrites.dropAllJobs();
|
|
510
|
+
this.unfinalizedPayloadEnvelopeWrites.dropAllJobs();
|
|
480
511
|
|
|
481
512
|
this.abortController.abort();
|
|
482
513
|
}
|
|
@@ -1010,6 +1041,10 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1010
1041
|
return this.blockProcessor.processBlocksJob(blocks, opts);
|
|
1011
1042
|
}
|
|
1012
1043
|
|
|
1044
|
+
async processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void> {
|
|
1045
|
+
return this.payloadEnvelopeProcessor.processPayloadEnvelopeJob(payloadInput, opts);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1013
1048
|
getStatus(): Status {
|
|
1014
1049
|
const head = this.forkChoice.getHead();
|
|
1015
1050
|
const finalizedCheckpoint = this.forkChoice.getFinalizedCheckpoint();
|
|
@@ -1402,7 +1437,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1402
1437
|
this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
|
|
1403
1438
|
const finalizedSlot = computeStartSlotAtEpoch(cp.epoch);
|
|
1404
1439
|
this.seenBlockProposers.prune(finalizedSlot);
|
|
1405
|
-
this.seenExecutionPayloadEnvelopes.prune(finalizedSlot);
|
|
1406
1440
|
|
|
1407
1441
|
// Update validator custody to account for effective balance changes
|
|
1408
1442
|
await this.updateValidatorsCustodyRequirement(cp);
|
|
@@ -4,17 +4,21 @@ import {GossipActionError} from "./gossipValidation.js";
|
|
|
4
4
|
export enum ExecutionPayloadEnvelopeErrorCode {
|
|
5
5
|
BELONG_TO_FINALIZED_BLOCK = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BELONG_TO_FINALIZED_BLOCK",
|
|
6
6
|
BLOCK_ROOT_UNKNOWN = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BLOCK_ROOT_UNKNOWN",
|
|
7
|
+
PARENT_UNKNOWN = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_PARENT_UNKNOWN",
|
|
8
|
+
UNKNOWN_BLOCK_STATE = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_UNKNOWN_BLOCK_STATE",
|
|
7
9
|
ENVELOPE_ALREADY_KNOWN = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_ALREADY_KNOWN",
|
|
8
10
|
INVALID_BLOCK = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_INVALID_BLOCK",
|
|
9
11
|
SLOT_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_SLOT_MISMATCH",
|
|
10
12
|
BUILDER_INDEX_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BUILDER_INDEX_MISMATCH",
|
|
11
13
|
BLOCK_HASH_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BLOCK_HASH_MISMATCH",
|
|
12
14
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_INVALID_SIGNATURE",
|
|
13
|
-
|
|
15
|
+
PAYLOAD_ENVELOPE_INPUT_MISSING = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_PAYLOAD_ENVELOPE_INPUT_MISSING",
|
|
14
16
|
}
|
|
15
17
|
export type ExecutionPayloadEnvelopeErrorType =
|
|
16
18
|
| {code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK; envelopeSlot: Slot; finalizedSlot: Slot}
|
|
17
19
|
| {code: ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN; blockRoot: RootHex}
|
|
20
|
+
| {code: ExecutionPayloadEnvelopeErrorCode.PARENT_UNKNOWN; parentRoot: RootHex; slot: Slot}
|
|
21
|
+
| {code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE; blockRoot: RootHex; slot: Slot}
|
|
18
22
|
| {
|
|
19
23
|
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN;
|
|
20
24
|
blockRoot: RootHex;
|
|
@@ -33,6 +37,6 @@ export type ExecutionPayloadEnvelopeErrorType =
|
|
|
33
37
|
bidBlockHash: RootHex | null;
|
|
34
38
|
}
|
|
35
39
|
| {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE}
|
|
36
|
-
| {code: ExecutionPayloadEnvelopeErrorCode.
|
|
40
|
+
| {code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING; blockRoot: RootHex};
|
|
37
41
|
|
|
38
42
|
export class ExecutionPayloadEnvelopeError extends GossipActionError<ExecutionPayloadEnvelopeErrorType> {}
|
|
@@ -158,10 +158,6 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
158
158
|
|
|
159
159
|
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
|
|
160
160
|
payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL, // TODO GLOAS: Post-gloas how do we know if the checkpoint payload is FULL or EMPTY?
|
|
161
|
-
builderIndex: isForkPostGloas ? (state as CachedBeaconStateGloas).latestExecutionPayloadBid.builderIndex : null,
|
|
162
|
-
blockHashFromBid: isForkPostGloas
|
|
163
|
-
? toRootHex((state as CachedBeaconStateGloas).latestExecutionPayloadBid.blockHash)
|
|
164
|
-
: null,
|
|
165
161
|
parentBlockHash: isForkPostGloas ? toRootHex((state as CachedBeaconStateGloas).latestBlockHash) : null,
|
|
166
162
|
},
|
|
167
163
|
currentSlot
|
|
@@ -255,12 +251,6 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
255
251
|
|
|
256
252
|
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
|
|
257
253
|
payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL, // TODO GLOAS: Post-gloas how do we know if the checkpoint payload is FULL or EMPTY?
|
|
258
|
-
builderIndex: isForkPostGloas
|
|
259
|
-
? (unfinalizedState as CachedBeaconStateGloas).latestExecutionPayloadBid.builderIndex
|
|
260
|
-
: null,
|
|
261
|
-
blockHashFromBid: isForkPostGloas
|
|
262
|
-
? toRootHex((unfinalizedState as CachedBeaconStateGloas).latestExecutionPayloadBid.blockHash)
|
|
263
|
-
: null,
|
|
264
254
|
parentBlockHash: isForkPostGloas ? toRootHex((unfinalizedState as CachedBeaconStateGloas).latestBlockHash) : null,
|
|
265
255
|
};
|
|
266
256
|
|
package/src/chain/interface.ts
CHANGED
|
@@ -32,7 +32,7 @@ import {IArchiveStore} from "./archiveStore/interface.js";
|
|
|
32
32
|
import {CheckpointBalancesCache} from "./balancesCache.js";
|
|
33
33
|
import {BeaconProposerCache, ProposerPreparationData} from "./beaconProposerCache.js";
|
|
34
34
|
import {IBlockInput} from "./blocks/blockInput/index.js";
|
|
35
|
-
import {ImportBlockOpts} from "./blocks/types.js";
|
|
35
|
+
import {ImportBlockOpts, ImportPayloadOpts} from "./blocks/types.js";
|
|
36
36
|
import {IBlsVerifier} from "./bls/index.js";
|
|
37
37
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
38
38
|
import {ChainEventEmitter} from "./emitter.js";
|
|
@@ -58,7 +58,6 @@ import {
|
|
|
58
58
|
SeenBlockProposers,
|
|
59
59
|
SeenContributionAndProof,
|
|
60
60
|
SeenExecutionPayloadBids,
|
|
61
|
-
SeenExecutionPayloadEnvelopes,
|
|
62
61
|
SeenPayloadAttesters,
|
|
63
62
|
SeenSyncCommitteeMessages,
|
|
64
63
|
} from "./seenCache/index.js";
|
|
@@ -66,6 +65,7 @@ import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
|
66
65
|
import {SeenAttestationDatas} from "./seenCache/seenAttestationData.js";
|
|
67
66
|
import {SeenBlockAttesters} from "./seenCache/seenBlockAttesters.js";
|
|
68
67
|
import {SeenBlockInput} from "./seenCache/seenGossipBlockInput.js";
|
|
68
|
+
import {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenCache/seenPayloadEnvelopeInput.js";
|
|
69
69
|
import {ShufflingCache} from "./shufflingCache.js";
|
|
70
70
|
import {ValidatorMonitor} from "./validatorMonitor.js";
|
|
71
71
|
|
|
@@ -128,13 +128,13 @@ export interface IBeaconChain {
|
|
|
128
128
|
readonly seenAggregators: SeenAggregators;
|
|
129
129
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
130
130
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
131
|
-
readonly seenExecutionPayloadEnvelopes: SeenExecutionPayloadEnvelopes;
|
|
132
131
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
133
132
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
134
133
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
135
134
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
136
135
|
readonly seenAttestationDatas: SeenAttestationDatas;
|
|
137
136
|
readonly seenBlockInputCache: SeenBlockInput;
|
|
137
|
+
readonly seenPayloadEnvelopeInputCache: SeenPayloadEnvelopeInput;
|
|
138
138
|
// Seen cache for liveness checks
|
|
139
139
|
readonly seenBlockAttesters: SeenBlockAttesters;
|
|
140
140
|
|
|
@@ -242,6 +242,9 @@ export interface IBeaconChain {
|
|
|
242
242
|
/** Process a chain of blocks until complete */
|
|
243
243
|
processChainSegment(blocks: IBlockInput[], opts?: ImportBlockOpts): Promise<void>;
|
|
244
244
|
|
|
245
|
+
/** Process execution payload envelope: verify, import to fork choice, and persist to DB */
|
|
246
|
+
processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void>;
|
|
247
|
+
|
|
245
248
|
getStatus(): Status;
|
|
246
249
|
|
|
247
250
|
recomputeForkChoiceHead(caller: ForkchoiceCaller): ProtoBlock;
|
|
@@ -41,7 +41,7 @@ export function isValidBlsToExecutionChangeForBlockInclusion(
|
|
|
41
41
|
state: CachedBeaconStateAllForks,
|
|
42
42
|
signedBLSToExecutionChange: capella.SignedBLSToExecutionChange
|
|
43
43
|
): boolean {
|
|
44
|
-
// For each condition from https://github.com/ethereum/consensus-specs/blob/
|
|
44
|
+
// For each condition from https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/capella/beacon-chain.md#new-process_bls_to_execution_change
|
|
45
45
|
//
|
|
46
46
|
// 1. assert address_change.validator_index < len(state.validators):
|
|
47
47
|
// If valid before will always be valid in the future, no need to check
|
|
@@ -73,7 +73,12 @@ export function computeEnvelopeStateRoot(
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
|
|
76
|
-
const postEnvelopeState = processExecutionPayloadEnvelope(postBlockState, signedEnvelope,
|
|
76
|
+
const postEnvelopeState = processExecutionPayloadEnvelope(postBlockState, signedEnvelope, {
|
|
77
|
+
// Signature is zero-ed (G2_POINT_AT_INFINITY), skip verification
|
|
78
|
+
verifySignature: false,
|
|
79
|
+
// State root is being computed here, the envelope doesn't have it yet
|
|
80
|
+
verifyStateRoot: false,
|
|
81
|
+
// Preserve cache in source state, since the resulting state is not added to the state cache
|
|
77
82
|
dontTransferCache: true,
|
|
78
83
|
});
|
|
79
84
|
processEnvelopeTimer?.();
|
|
@@ -441,7 +441,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
441
441
|
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
442
442
|
feeRecipient,
|
|
443
443
|
});
|
|
444
|
-
// https://github.com/ethereum/consensus-specs/blob/
|
|
444
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/deneb/validator.md#constructing-the-beaconblockbody
|
|
445
445
|
const prepareRes = await prepareExecutionPayload(
|
|
446
446
|
this,
|
|
447
447
|
this.logger,
|
|
@@ -9,8 +9,10 @@ export enum RegenCaller {
|
|
|
9
9
|
processBlock = "processBlock",
|
|
10
10
|
produceBlock = "produceBlock",
|
|
11
11
|
validateGossipBlock = "validateGossipBlock",
|
|
12
|
+
validateGossipPayloadEnvelope = "validateGossipPayloadEnvelope",
|
|
12
13
|
validateGossipBlob = "validateGossipBlob",
|
|
13
14
|
validateGossipDataColumn = "validateGossipDataColumn",
|
|
15
|
+
validateGossipExecutionPayloadEnvelope = "validateGossipExecutionPayloadEnvelope",
|
|
14
16
|
precomputeEpoch = "precomputeEpoch",
|
|
15
17
|
predictProposerHead = "predictProposerHead",
|
|
16
18
|
produceAttestationData = "produceAttestationData",
|
|
@@ -3,5 +3,5 @@ export {SeenBlockProposers} from "./seenBlockProposers.js";
|
|
|
3
3
|
export {SeenSyncCommitteeMessages} from "./seenCommittee.js";
|
|
4
4
|
export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
|
|
5
5
|
export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
|
|
6
|
-
export {SeenExecutionPayloadEnvelopes} from "./seenExecutionPayloadEnvelope.js";
|
|
7
6
|
export {SeenBlockInput} from "./seenGossipBlockInput.js";
|
|
7
|
+
export {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenPayloadEnvelopeInput.js";
|
|
@@ -180,7 +180,7 @@ export class SeenBlockInput {
|
|
|
180
180
|
blockInput = this.blockInputs.get(parentRootHex ?? "");
|
|
181
181
|
parentRootHex = blockInput?.parentRootHex;
|
|
182
182
|
}
|
|
183
|
-
this.logger?.debug(
|
|
183
|
+
this.logger?.debug("BlockInputCache.prune deleted cached BlockInputs", {deletedCount});
|
|
184
184
|
this.pruneToMaxSize();
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -193,7 +193,7 @@ export class SeenBlockInput {
|
|
|
193
193
|
this.evictBlockInput(blockInput);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
-
this.logger?.debug(
|
|
196
|
+
this.logger?.debug("BlockInputCache.onFinalized deleted cached BlockInputs", {deletedCount});
|
|
197
197
|
this.pruneToMaxSize();
|
|
198
198
|
};
|
|
199
199
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
2
|
+
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
|
+
import {RootHex} from "@lodestar/types";
|
|
4
|
+
import {Logger} from "@lodestar/utils";
|
|
5
|
+
import {Metrics} from "../../metrics/metrics.js";
|
|
6
|
+
import {SerializedCache} from "../../util/serializedCache.js";
|
|
7
|
+
import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
8
|
+
import {ChainEvent, ChainEventEmitter} from "../emitter.js";
|
|
9
|
+
|
|
10
|
+
export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/index.js";
|
|
11
|
+
export {PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
12
|
+
|
|
13
|
+
export type SeenPayloadEnvelopeInputModules = {
|
|
14
|
+
chainEvents: ChainEventEmitter;
|
|
15
|
+
signal: AbortSignal;
|
|
16
|
+
serializedCache: SerializedCache;
|
|
17
|
+
metrics: Metrics | null;
|
|
18
|
+
logger?: Logger;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
|
|
23
|
+
*
|
|
24
|
+
* Created during block import when a block is processed.
|
|
25
|
+
* Pruned on finalization and after payload is written to DB.
|
|
26
|
+
*/
|
|
27
|
+
export class SeenPayloadEnvelopeInput {
|
|
28
|
+
private readonly chainEvents: ChainEventEmitter;
|
|
29
|
+
private readonly signal: AbortSignal;
|
|
30
|
+
private readonly serializedCache: SerializedCache;
|
|
31
|
+
private readonly metrics: Metrics | null;
|
|
32
|
+
private readonly logger?: Logger;
|
|
33
|
+
private payloadInputs = new Map<RootHex, PayloadEnvelopeInput>();
|
|
34
|
+
|
|
35
|
+
constructor({chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
|
|
36
|
+
this.chainEvents = chainEvents;
|
|
37
|
+
this.signal = signal;
|
|
38
|
+
this.serializedCache = serializedCache;
|
|
39
|
+
this.metrics = metrics;
|
|
40
|
+
this.logger = logger;
|
|
41
|
+
|
|
42
|
+
if (metrics) {
|
|
43
|
+
metrics.seenCache.payloadEnvelopeInput.count.addCollect(() => {
|
|
44
|
+
metrics.seenCache.payloadEnvelopeInput.count.set(this.payloadInputs.size);
|
|
45
|
+
metrics.seenCache.payloadEnvelopeInput.serializedObjectRefs.set(
|
|
46
|
+
Array.from(this.payloadInputs.values()).reduce(
|
|
47
|
+
(count, payloadInput) => count + payloadInput.getSerializedCacheKeys().length,
|
|
48
|
+
0
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.chainEvents.on(ChainEvent.forkChoiceFinalized, this.onFinalized);
|
|
55
|
+
this.signal.addEventListener("abort", () => {
|
|
56
|
+
this.chainEvents.off(ChainEvent.forkChoiceFinalized, this.onFinalized);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private onFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
61
|
+
// Prune all entries with slot < finalized slot
|
|
62
|
+
const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
|
|
63
|
+
let deletedCount = 0;
|
|
64
|
+
for (const [, input] of this.payloadInputs) {
|
|
65
|
+
if (input.slot < finalizedSlot) {
|
|
66
|
+
this.evictPayloadInput(input);
|
|
67
|
+
deletedCount++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
add(props: CreateFromBlockProps): PayloadEnvelopeInput {
|
|
74
|
+
if (this.payloadInputs.has(props.blockRootHex)) {
|
|
75
|
+
throw new Error(`PayloadEnvelopeInput already exists for block ${props.blockRootHex}`);
|
|
76
|
+
}
|
|
77
|
+
const input = PayloadEnvelopeInput.createFromBlock(props);
|
|
78
|
+
this.payloadInputs.set(props.blockRootHex, input);
|
|
79
|
+
this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
|
|
80
|
+
return input;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get(blockRootHex: RootHex): PayloadEnvelopeInput | undefined {
|
|
84
|
+
return this.payloadInputs.get(blockRootHex);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
has(blockRootHex: RootHex): boolean {
|
|
88
|
+
return this.payloadInputs.has(blockRootHex);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
prune(blockRootHex: RootHex): void {
|
|
92
|
+
const payloadInput = this.payloadInputs.get(blockRootHex);
|
|
93
|
+
if (payloadInput) {
|
|
94
|
+
this.evictPayloadInput(payloadInput);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
size(): number {
|
|
99
|
+
return this.payloadInputs.size;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
|
|
103
|
+
this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
|
|
104
|
+
this.payloadInputs.delete(payloadInput.blockRootHex);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
2
2
|
import {
|
|
3
|
+
BeaconStateView,
|
|
3
4
|
CachedBeaconStateGloas,
|
|
4
5
|
computeStartSlotAtEpoch,
|
|
5
|
-
|
|
6
|
-
getExecutionPayloadEnvelopeSigningRoot,
|
|
6
|
+
getExecutionPayloadEnvelopeSignatureSet,
|
|
7
7
|
} from "@lodestar/state-transition";
|
|
8
8
|
import {gloas} from "@lodestar/types";
|
|
9
9
|
import {toRootHex} from "@lodestar/utils";
|
|
10
10
|
import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
|
|
11
11
|
import {IBeaconChain} from "../index.js";
|
|
12
|
+
import {RegenCaller} from "../regen/index.js";
|
|
12
13
|
|
|
13
14
|
export async function validateApiExecutionPayloadEnvelope(
|
|
14
15
|
chain: IBeaconChain,
|
|
@@ -47,7 +48,9 @@ async function validateExecutionPayloadEnvelope(
|
|
|
47
48
|
|
|
48
49
|
// [IGNORE] The node has not seen another valid
|
|
49
50
|
// `SignedExecutionPayloadEnvelope` for this block root from this builder.
|
|
50
|
-
|
|
51
|
+
const envelopeBlock = chain.forkChoice.getBlockHex(blockRootHex, PayloadStatus.FULL);
|
|
52
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
53
|
+
if (envelopeBlock || payloadInput?.hasPayloadEnvelope()) {
|
|
51
54
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
52
55
|
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
|
|
53
56
|
blockRoot: blockRootHex,
|
|
@@ -55,6 +58,14 @@ async function validateExecutionPayloadEnvelope(
|
|
|
55
58
|
});
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
if (!payloadInput) {
|
|
62
|
+
// PayloadEnvelopeInput should have been created during block import
|
|
63
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
64
|
+
code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING,
|
|
65
|
+
blockRoot: blockRootHex,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
|
|
59
70
|
const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
|
|
60
71
|
const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
|
|
@@ -79,45 +90,47 @@ async function validateExecutionPayloadEnvelope(
|
|
|
79
90
|
});
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
if (block.builderIndex == null || block.blockHashFromBid == null) {
|
|
83
|
-
// This indicates this block is a pre-gloas block which is wrong
|
|
84
|
-
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
85
|
-
code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
|
|
86
|
-
blockRoot: blockRootHex,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
93
|
// [REJECT] `envelope.builder_index == bid.builder_index`
|
|
91
|
-
if (envelope.builderIndex !==
|
|
94
|
+
if (envelope.builderIndex !== payloadInput.getBuilderIndex()) {
|
|
92
95
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
93
96
|
code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
|
|
94
97
|
envelopeBuilderIndex: envelope.builderIndex,
|
|
95
|
-
bidBuilderIndex:
|
|
98
|
+
bidBuilderIndex: payloadInput.getBuilderIndex(),
|
|
96
99
|
});
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
// [REJECT] `payload.block_hash == bid.block_hash`
|
|
100
|
-
if (toRootHex(payload.blockHash) !==
|
|
103
|
+
if (toRootHex(payload.blockHash) !== payloadInput.getBlockHashHex()) {
|
|
101
104
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
102
105
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
|
|
103
106
|
envelopeBlockHash: toRootHex(payload.blockHash),
|
|
104
|
-
bidBlockHash:
|
|
107
|
+
bidBlockHash: payloadInput.getBlockHashHex(),
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
// Get the post block state which is the pre-payload state to verify the builder's signature.
|
|
112
|
+
const blockState = await chain.regen
|
|
113
|
+
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
114
|
+
.catch(() => {
|
|
115
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
116
|
+
code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE,
|
|
117
|
+
blockRoot: blockRootHex,
|
|
118
|
+
slot: envelope.slot,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const state = blockState as CachedBeaconStateGloas;
|
|
123
|
+
const signatureSet = getExecutionPayloadEnvelopeSignatureSet(
|
|
124
|
+
chain.config,
|
|
125
|
+
chain.pubkeyCache,
|
|
126
|
+
new BeaconStateView(state),
|
|
127
|
+
executionPayloadEnvelope,
|
|
128
|
+
payloadInput.proposerIndex
|
|
114
129
|
);
|
|
115
130
|
|
|
116
|
-
if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
|
|
131
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
|
|
117
132
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
118
133
|
code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
|
|
119
134
|
});
|
|
120
135
|
}
|
|
121
|
-
|
|
122
|
-
chain.seenExecutionPayloadEnvelopes.add(blockRootHex, envelope.slot);
|
|
123
136
|
}
|
|
@@ -6,7 +6,7 @@ import {LightClientError, LightClientErrorCode} from "../errors/lightClientError
|
|
|
6
6
|
import {IBeaconChain} from "../interface.js";
|
|
7
7
|
import {updateReceivedTooEarly} from "./lightClientOptimisticUpdate.js";
|
|
8
8
|
|
|
9
|
-
// https://github.com/ethereum/consensus-specs/blob/
|
|
9
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#light_client_finality_update
|
|
10
10
|
export function validateLightClientFinalityUpdate(
|
|
11
11
|
config: ChainForkConfig,
|
|
12
12
|
chain: IBeaconChain,
|
|
@@ -6,7 +6,7 @@ import {GossipAction} from "../errors/index.js";
|
|
|
6
6
|
import {LightClientError, LightClientErrorCode} from "../errors/lightClientError.js";
|
|
7
7
|
import {IBeaconChain} from "../interface.js";
|
|
8
8
|
|
|
9
|
-
// https://github.com/ethereum/consensus-specs/blob/
|
|
9
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#light_client_optimistic_update
|
|
10
10
|
export function validateLightClientOptimisticUpdate(
|
|
11
11
|
config: ChainForkConfig,
|
|
12
12
|
chain: IBeaconChain,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ValidatorIndex,
|
|
24
24
|
altair,
|
|
25
25
|
deneb,
|
|
26
|
+
gloas,
|
|
26
27
|
} from "@lodestar/types";
|
|
27
28
|
import {LogData, LogHandler, LogLevel, Logger, MapDef, MapDefMax, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
28
29
|
import {GENESIS_SLOT} from "../constants/constants.js";
|
|
@@ -61,6 +62,11 @@ export type ValidatorMonitor = {
|
|
|
61
62
|
): void;
|
|
62
63
|
registerBeaconBlock(src: OpSource, delaySec: Seconds, block: BeaconBlock): void;
|
|
63
64
|
registerBlobSidecar(src: OpSource, seenTimestampSec: Seconds, blob: deneb.BlobSidecar): void;
|
|
65
|
+
registerExecutionPayloadEnvelope(
|
|
66
|
+
src: OpSource,
|
|
67
|
+
delaySec: Seconds,
|
|
68
|
+
envelope: gloas.SignedExecutionPayloadEnvelope
|
|
69
|
+
): void;
|
|
64
70
|
registerImportedBlock(block: BeaconBlock, data: {proposerBalanceDelta: number}): void;
|
|
65
71
|
onPoolSubmitUnaggregatedAttestation(
|
|
66
72
|
seenTimestampSec: number,
|
|
@@ -450,6 +456,10 @@ export function createValidatorMonitor(
|
|
|
450
456
|
//TODO: freetheblobs
|
|
451
457
|
},
|
|
452
458
|
|
|
459
|
+
registerExecutionPayloadEnvelope(_src, _delaySec, _envelope) {
|
|
460
|
+
// TODO GLOAS: implement execution payload envelope monitoring
|
|
461
|
+
},
|
|
462
|
+
|
|
453
463
|
registerImportedBlock(block, {proposerBalanceDelta}) {
|
|
454
464
|
const validator = validators.get(block.proposerIndex);
|
|
455
465
|
if (validator) {
|
|
@@ -889,7 +899,7 @@ function renderAttestationSummary(
|
|
|
889
899
|
summary: AttestationSummary | undefined,
|
|
890
900
|
flags: ParticipationFlags
|
|
891
901
|
): string {
|
|
892
|
-
// Reference https://github.com/ethereum/consensus-specs/blob/
|
|
902
|
+
// Reference https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/beacon-chain.md#get_attestation_participation_flag_indices
|
|
893
903
|
//
|
|
894
904
|
// is_matching_source = data.source == justified_checkpoint
|
|
895
905
|
// is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
|