@lodestar/beacon-node 1.43.0-dev.e341cdc614 → 1.43.0-dev.f758dd5f38
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 +1 -4
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -2
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +0 -4
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +4 -4
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +14 -13
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +59 -83
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +0 -1
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +14 -20
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +24 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +76 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +5 -4
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +9 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +3 -3
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
- 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 +11 -19
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -3
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +46 -16
- 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 +48 -23
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +11 -4
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -18
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +13 -1
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +19 -9
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +4 -3
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +21 -14
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -0
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +6 -0
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +20 -0
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +18 -0
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +3 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/network.js +1 -1
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.js +3 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -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/node/notifier.js +7 -1
- package/lib/node/notifier.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +16 -3
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +17 -16
- package/src/api/impl/beacon/blocks/index.ts +1 -4
- package/src/api/impl/beacon/state/utils.ts +2 -2
- package/src/api/impl/validator/index.ts +0 -4
- package/src/chain/archiveStore/archiveStore.ts +5 -5
- package/src/chain/archiveStore/interface.ts +4 -4
- package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +4 -4
- package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
- package/src/chain/blocks/importExecutionPayload.ts +73 -103
- package/src/chain/blocks/index.ts +0 -1
- package/src/chain/blocks/types.ts +14 -25
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +129 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
- package/src/chain/chain.ts +22 -16
- package/src/chain/emitter.ts +3 -3
- package/src/chain/errors/executionPayloadBid.ts +6 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
- package/src/chain/forkChoice/index.ts +8 -24
- package/src/chain/interface.ts +4 -2
- package/src/chain/prepareNextSlot.ts +60 -17
- package/src/chain/produceBlock/produceBlockBody.ts +66 -20
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
- package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
- package/src/chain/validation/executionPayloadBid.ts +14 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +20 -10
- package/src/chain/validation/payloadAttestationMessage.ts +5 -3
- package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
- package/src/execution/engine/http.ts +21 -14
- package/src/execution/engine/interface.ts +1 -0
- package/src/execution/engine/mock.ts +8 -1
- package/src/execution/engine/types.ts +41 -0
- package/src/network/network.ts +1 -1
- package/src/network/processor/gossipHandlers.ts +3 -3
- package/src/node/nodejs.ts +4 -2
- package/src/node/notifier.ts +8 -1
- package/src/util/sszBytes.ts +21 -3
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {byteArrayEquals, fromHex, toRootHex} from "@lodestar/utils";
|
|
3
|
+
import {isStatePostGloas} from "@lodestar/state-transition";
|
|
4
|
+
import {fromHex} from "@lodestar/utils";
|
|
6
5
|
import {ExecutionPayloadStatus} from "../../execution/index.js";
|
|
7
6
|
import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
8
7
|
import {BeaconChain} from "../chain.js";
|
|
9
8
|
import {RegenCaller} from "../regen/interface.js";
|
|
10
9
|
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
11
10
|
import {ImportPayloadOpts} from "./types.js";
|
|
11
|
+
import {
|
|
12
|
+
verifyExecutionPayloadEnvelope,
|
|
13
|
+
verifyExecutionPayloadEnvelopeSignature,
|
|
14
|
+
} from "./verifyExecutionPayloadEnvelope.js";
|
|
12
15
|
import {verifyPayloadsDataAvailability} from "./verifyPayloadsDataAvailability.js";
|
|
13
16
|
|
|
14
17
|
const EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS = 64;
|
|
@@ -17,7 +20,7 @@ export enum PayloadErrorCode {
|
|
|
17
20
|
EXECUTION_ENGINE_INVALID = "PAYLOAD_ERROR_EXECUTION_ENGINE_INVALID",
|
|
18
21
|
EXECUTION_ENGINE_ERROR = "PAYLOAD_ERROR_EXECUTION_ENGINE_ERROR",
|
|
19
22
|
BLOCK_NOT_IN_FORK_CHOICE = "PAYLOAD_ERROR_BLOCK_NOT_IN_FORK_CHOICE",
|
|
20
|
-
|
|
23
|
+
ENVELOPE_VERIFICATION_ERROR = "PAYLOAD_ERROR_ENVELOPE_VERIFICATION_ERROR",
|
|
21
24
|
INVALID_SIGNATURE = "PAYLOAD_ERROR_INVALID_SIGNATURE",
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -37,7 +40,7 @@ export type PayloadErrorType =
|
|
|
37
40
|
blockRootHex: string;
|
|
38
41
|
}
|
|
39
42
|
| {
|
|
40
|
-
code: PayloadErrorCode.
|
|
43
|
+
code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR;
|
|
41
44
|
message: string;
|
|
42
45
|
}
|
|
43
46
|
| {
|
|
@@ -69,18 +72,19 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
69
72
|
/**
|
|
70
73
|
* Import an execution payload envelope after all data is available.
|
|
71
74
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* 2. Gets the ProtoBlock from fork choice
|
|
75
|
-
* 3. Applies write-queue backpressure (waitForSpace) early, before verification
|
|
76
|
-
* 4. Regenerates the block state
|
|
77
|
-
* 5. Runs EL verification (notifyNewPayload) in parallel with signature verification and processExecutionPayloadEnvelope
|
|
78
|
-
* 6. Persists verified payload envelope to hot DB
|
|
79
|
-
* 7. Updates fork choice
|
|
80
|
-
* 8. Caches the post-execution payload state
|
|
81
|
-
* 9. Records metrics for column sources
|
|
82
|
-
* 10. Emits `execution_payload` for recent enough payloads after successful import
|
|
75
|
+
* The envelope is only verified here, no state mutation. State effects from the payload
|
|
76
|
+
* are applied on the next block via processParentExecutionPayload.
|
|
83
77
|
*
|
|
78
|
+
* Steps:
|
|
79
|
+
* 1. Emit `execution_payload_available` event for payload attestation
|
|
80
|
+
* 2. Get the ProtoBlock from fork choice
|
|
81
|
+
* 3. Wait for data columns to be available
|
|
82
|
+
* 4. Regenerate state for envelope verification
|
|
83
|
+
* 5. Verify envelope (fields against state, signature, and EL in parallel where possible)
|
|
84
|
+
* 6. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
|
|
85
|
+
* 7. Update fork choice (transitions the block's PENDING variant to FULL)
|
|
86
|
+
* 8. Record metrics for payload envelope and column sources
|
|
87
|
+
* 9. Emit `execution_payload` event
|
|
84
88
|
*/
|
|
85
89
|
export async function importExecutionPayload(
|
|
86
90
|
this: BeaconChain,
|
|
@@ -90,17 +94,18 @@ export async function importExecutionPayload(
|
|
|
90
94
|
): Promise<void> {
|
|
91
95
|
const signedEnvelope = payloadInput.getPayloadEnvelope();
|
|
92
96
|
const envelope = signedEnvelope.message;
|
|
97
|
+
const slot = envelope.payload.slotNumber;
|
|
93
98
|
const blockRootHex = payloadInput.blockRootHex;
|
|
94
99
|
const blockHashHex = payloadInput.getBlockHashHex();
|
|
95
|
-
const fork = this.config.getForkName(
|
|
100
|
+
const fork = this.config.getForkName(slot);
|
|
96
101
|
|
|
97
|
-
// 1. Emit `execution_payload_available` event at the start of import. At this point the
|
|
98
|
-
// is already complete, so the payload and required data are available for
|
|
99
|
-
// This event
|
|
100
|
-
// it before getting a response from the
|
|
101
|
-
if (this.clock.currentSlot -
|
|
102
|
+
// 1. Emit `execution_payload_available` event at the start of import. At this point the
|
|
103
|
+
// payload input is already complete, so the payload and required data are available for
|
|
104
|
+
// payload attestation. This event only signals availability (not validity), so we can emit
|
|
105
|
+
// it before getting a response from the EL on whether the payload is valid or not.
|
|
106
|
+
if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
102
107
|
this.emitter.emit(routes.events.EventType.executionPayloadAvailable, {
|
|
103
|
-
slot
|
|
108
|
+
slot,
|
|
104
109
|
blockRoot: blockRootHex,
|
|
105
110
|
});
|
|
106
111
|
}
|
|
@@ -114,16 +119,11 @@ export async function importExecutionPayload(
|
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
121
|
|
|
117
|
-
// 3. Wait for data columns to be available
|
|
122
|
+
// 3. Wait for data columns to be available.
|
|
118
123
|
// The helper is shared with future gloas sync services; take the single-item batch form here.
|
|
119
124
|
await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
120
125
|
|
|
121
|
-
// 4.
|
|
122
|
-
// The actual DB write is deferred until after verification succeeds.
|
|
123
|
-
await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
|
|
124
|
-
|
|
125
|
-
// 5. Get pre-state for processExecutionPayloadEnvelope
|
|
126
|
-
// We need the block state (post-block, pre-payload) to process the envelope
|
|
126
|
+
// 4. Regenerate state for envelope verification
|
|
127
127
|
const blockState = await this.regen.getBlockSlotState(
|
|
128
128
|
protoBlock,
|
|
129
129
|
protoBlock.slot,
|
|
@@ -132,13 +132,30 @@ export async function importExecutionPayload(
|
|
|
132
132
|
);
|
|
133
133
|
if (!isStatePostGloas(blockState)) {
|
|
134
134
|
throw new PayloadError({
|
|
135
|
-
code: PayloadErrorCode.
|
|
136
|
-
message: `Expected gloas+
|
|
135
|
+
code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
|
|
136
|
+
message: `Expected gloas+ state for payload import, got fork=${blockState.forkName}`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 5. Verify envelope fields against state first to fail fast before the EL + BLS work.
|
|
141
|
+
// When validSignature is true, gossip/API has already verified both the signature and the
|
|
142
|
+
// executionRequestsRoot, so we skip those checks here.
|
|
143
|
+
try {
|
|
144
|
+
verifyExecutionPayloadEnvelope(this.config, blockState, envelope, {
|
|
145
|
+
verifyExecutionRequestsRoot: !opts.validSignature,
|
|
137
146
|
});
|
|
147
|
+
} catch (e) {
|
|
148
|
+
throw new PayloadError(
|
|
149
|
+
{
|
|
150
|
+
code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
|
|
151
|
+
message: (e as Error).message,
|
|
152
|
+
},
|
|
153
|
+
`Envelope verification error: ${(e as Error).message}`
|
|
154
|
+
);
|
|
138
155
|
}
|
|
139
156
|
|
|
140
|
-
//
|
|
141
|
-
const [execResult, signatureValid
|
|
157
|
+
// 5a. Run EL and signature verification in parallel
|
|
158
|
+
const [execResult, signatureValid] = await Promise.all([
|
|
142
159
|
this.executionEngine.notifyNewPayload(
|
|
143
160
|
fork,
|
|
144
161
|
envelope.payload,
|
|
@@ -149,45 +166,22 @@ export async function importExecutionPayload(
|
|
|
149
166
|
|
|
150
167
|
opts.validSignature === true
|
|
151
168
|
? Promise.resolve(true)
|
|
152
|
-
: (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return this.bls.verifySignatureSets([signatureSet]);
|
|
161
|
-
})(),
|
|
162
|
-
|
|
163
|
-
// Signature verified separately above.
|
|
164
|
-
// State root check is done separately below with better error typing (matching block pipeline pattern).
|
|
165
|
-
(async () => {
|
|
166
|
-
try {
|
|
167
|
-
return {
|
|
168
|
-
postPayloadState: blockState.processExecutionPayloadEnvelope(signedEnvelope, {
|
|
169
|
-
verifySignature: false,
|
|
170
|
-
verifyStateRoot: false,
|
|
171
|
-
}),
|
|
172
|
-
};
|
|
173
|
-
} catch (e) {
|
|
174
|
-
throw new PayloadError(
|
|
175
|
-
{
|
|
176
|
-
code: PayloadErrorCode.STATE_TRANSITION_ERROR,
|
|
177
|
-
message: (e as Error).message,
|
|
178
|
-
},
|
|
179
|
-
`State transition error: ${(e as Error).message}`
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
})(),
|
|
169
|
+
: verifyExecutionPayloadEnvelopeSignature(
|
|
170
|
+
this.config,
|
|
171
|
+
blockState,
|
|
172
|
+
this.pubkeyCache,
|
|
173
|
+
signedEnvelope,
|
|
174
|
+
payloadInput.proposerIndex,
|
|
175
|
+
this.bls
|
|
176
|
+
),
|
|
183
177
|
]);
|
|
184
178
|
|
|
185
|
-
//
|
|
179
|
+
// 5b. Check signature verification result
|
|
186
180
|
if (!signatureValid) {
|
|
187
181
|
throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
|
|
188
182
|
}
|
|
189
183
|
|
|
190
|
-
//
|
|
184
|
+
// 5c. Handle EL response
|
|
191
185
|
switch (execResult.status) {
|
|
192
186
|
case ExecutionPayloadStatus.VALID:
|
|
193
187
|
break;
|
|
@@ -213,69 +207,45 @@ export async function importExecutionPayload(
|
|
|
213
207
|
});
|
|
214
208
|
}
|
|
215
209
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!byteArrayEquals(envelope.stateRoot, postPayloadStateRoot)) {
|
|
220
|
-
throw new PayloadError({
|
|
221
|
-
code: PayloadErrorCode.STATE_TRANSITION_ERROR,
|
|
222
|
-
message: `Envelope state root mismatch expected=${toRootHex(envelope.stateRoot)} actual=${toRootHex(postPayloadStateRoot)}`,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// 6. Persist payload envelope to hot DB (performed asynchronously to avoid blocking)
|
|
210
|
+
// 6. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
|
|
211
|
+
// on the import pipeline during sync, then perform the write asynchronously to avoid blocking.
|
|
212
|
+
await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
|
|
227
213
|
this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
|
|
228
214
|
if (!isQueueErrorAborted(e)) {
|
|
229
215
|
this.logger.error(
|
|
230
216
|
"Error pushing payload envelope to unfinalized write queue",
|
|
231
|
-
{slot
|
|
217
|
+
{slot, blockRoot: blockRootHex},
|
|
232
218
|
e as Error
|
|
233
219
|
);
|
|
234
220
|
}
|
|
235
221
|
});
|
|
236
222
|
|
|
237
|
-
// 7. Update fork choice
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
blockHashHex,
|
|
241
|
-
envelope.payload.blockNumber,
|
|
242
|
-
toRootHex(postPayloadStateRoot),
|
|
243
|
-
toForkChoiceExecutionStatus(execResult.status)
|
|
244
|
-
);
|
|
223
|
+
// 7. Update fork choice, transitions the block's PENDING variant to FULL
|
|
224
|
+
const execStatus = toForkChoiceExecutionStatus(execResult.status);
|
|
225
|
+
this.forkChoice.onExecutionPayload(blockRootHex, blockHashHex, envelope.payload.blockNumber, execStatus);
|
|
245
226
|
|
|
246
|
-
// 8.
|
|
247
|
-
this.regen.processState(blockRootHex, postPayloadState);
|
|
248
|
-
if (postPayloadState.slot % SLOTS_PER_EPOCH === 0) {
|
|
249
|
-
const {checkpoint} = postPayloadState.computeAnchorCheckpoint();
|
|
250
|
-
this.regen.addCheckpointState(checkpoint, postPayloadState);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 9. Record metrics for payload envelope and column sources
|
|
227
|
+
// 8. Record metrics for payload envelope and column sources
|
|
254
228
|
this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
|
|
255
229
|
for (const {source} of payloadInput.getSampledColumnsWithSource()) {
|
|
256
230
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
257
231
|
}
|
|
258
232
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// 10. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
|
|
262
|
-
if (this.clock.currentSlot - envelope.slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
233
|
+
// 9. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
|
|
234
|
+
if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
263
235
|
this.emitter.emit(routes.events.EventType.executionPayload, {
|
|
264
|
-
slot
|
|
236
|
+
slot,
|
|
265
237
|
builderIndex: envelope.builderIndex,
|
|
266
238
|
blockHash: blockHashHex,
|
|
267
239
|
blockRoot: blockRootHex,
|
|
268
|
-
stateRoot: stateRootHex,
|
|
269
240
|
// TODO GLOAS: revisit once we support optimistic import
|
|
270
241
|
executionOptimistic: false,
|
|
271
242
|
});
|
|
272
243
|
}
|
|
273
244
|
|
|
274
245
|
this.logger.verbose("Execution payload imported", {
|
|
275
|
-
slot
|
|
246
|
+
slot,
|
|
276
247
|
builderIndex: envelope.builderIndex,
|
|
277
248
|
blockRoot: blockRootHex,
|
|
278
249
|
blockHash: blockHashHex,
|
|
279
|
-
stateRoot: stateRootHex,
|
|
280
250
|
});
|
|
281
251
|
}
|
|
@@ -89,7 +89,6 @@ export async function processBlocks(
|
|
|
89
89
|
(block, i): FullyVerifiedBlock => ({
|
|
90
90
|
blockInput: block,
|
|
91
91
|
postState: postStates[i],
|
|
92
|
-
postPayloadState: null,
|
|
93
92
|
parentBlockSlot: parentSlots[i],
|
|
94
93
|
executionStatus: executionStatuses[i],
|
|
95
94
|
// start supporting optimistic syncing/processing
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {BlockExecutionStatus
|
|
2
|
+
import {BlockExecutionStatus} from "@lodestar/fork-choice";
|
|
3
3
|
import {ForkSeq} from "@lodestar/params";
|
|
4
4
|
import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
5
5
|
import type {IndexedAttestation, Slot, fulu} from "@lodestar/types";
|
|
@@ -43,8 +43,9 @@ export enum BlobSidecarValidation {
|
|
|
43
43
|
|
|
44
44
|
export type ImportPayloadOpts = {
|
|
45
45
|
/**
|
|
46
|
-
* Set to true
|
|
47
|
-
*
|
|
46
|
+
* Set to true when the envelope was already validated upstream (e.g., gossip/API validation):
|
|
47
|
+
* signature is trusted and execution_requests_root was already verified against the bid.
|
|
48
|
+
* When false/undefined, both are verified during import.
|
|
48
49
|
*/
|
|
49
50
|
validSignature?: boolean;
|
|
50
51
|
};
|
|
@@ -88,7 +89,14 @@ export type ImportBlockOpts = {
|
|
|
88
89
|
seenTimestampSec?: number;
|
|
89
90
|
};
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
/**
|
|
93
|
+
* A wrapper around a `SignedBeaconBlock` that indicates that this block is fully verified and ready to import.
|
|
94
|
+
*
|
|
95
|
+
* `executionStatus` reflects the outcome of execution payload verification at block-import time:
|
|
96
|
+
* - pre-gloas: Valid | Syncing | PreMerge (from EL notifyNewPayload against the in-block payload)
|
|
97
|
+
* - post-gloas: PayloadSeparated (payload arrives separately as an envelope and is imported later)
|
|
98
|
+
*/
|
|
99
|
+
export type FullyVerifiedBlock = {
|
|
92
100
|
blockInput: IBlockInput;
|
|
93
101
|
postState: IBeaconStateView;
|
|
94
102
|
parentBlockSlot: Slot;
|
|
@@ -98,25 +106,6 @@ type FullyVerifiedBlockBase = {
|
|
|
98
106
|
indexedAttestations: IndexedAttestation[];
|
|
99
107
|
/** Seen timestamp seconds */
|
|
100
108
|
seenTimestampSec: number;
|
|
109
|
+
/** If the execution payload couldn't be verified because of EL syncing status, used in optimistic sync */
|
|
110
|
+
executionStatus: BlockExecutionStatus;
|
|
101
111
|
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* A wrapper around a `SignedBeaconBlock` that indicates that this block is fully verified and ready to import.
|
|
105
|
-
*
|
|
106
|
-
* Discriminated union on `postPayloadState`:
|
|
107
|
-
* - `null` → block has no pre-verified envelope; `executionStatus` is any `BlockExecutionStatus`
|
|
108
|
-
* - non-null → envelope was pre-verified during state transition; `executionStatus` is narrowed to
|
|
109
|
-
* `Valid | Syncing` (matching what `forkChoice.onExecutionPayload` expects)
|
|
110
|
-
*/
|
|
111
|
-
export type FullyVerifiedBlock = FullyVerifiedBlockBase &
|
|
112
|
-
(
|
|
113
|
-
| {
|
|
114
|
-
postPayloadState: null;
|
|
115
|
-
/** If the execution payload couldn't be verified because of EL syncing status, used in optimistic sync or for merge block */
|
|
116
|
-
executionStatus: BlockExecutionStatus;
|
|
117
|
-
}
|
|
118
|
-
| {
|
|
119
|
-
postPayloadState: IBeaconStateView;
|
|
120
|
-
executionStatus: PayloadExecutionStatus;
|
|
121
|
-
}
|
|
122
|
-
);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
+
import {
|
|
3
|
+
type IBeaconStateViewGloas,
|
|
4
|
+
type PubkeyCache,
|
|
5
|
+
computeTimeAtSlot,
|
|
6
|
+
getExecutionPayloadEnvelopeSignatureSet,
|
|
7
|
+
} from "@lodestar/state-transition";
|
|
8
|
+
import {gloas, ssz} from "@lodestar/types";
|
|
9
|
+
import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
|
|
10
|
+
import {IBlsVerifier} from "../bls/index.js";
|
|
11
|
+
|
|
12
|
+
export type VerifyExecutionPayloadEnvelopeOpts = {
|
|
13
|
+
verifyExecutionRequestsRoot?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Verify execution payload envelope fields against the post-block state.
|
|
18
|
+
*
|
|
19
|
+
* Signature verification and the execution engine call (`verify_and_notify_new_payload`) are
|
|
20
|
+
* performed outside this function, see `verifyExecutionPayloadEnvelopeSignature` and
|
|
21
|
+
* `importExecutionPayload` which run both in parallel with this check.
|
|
22
|
+
*
|
|
23
|
+
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope
|
|
24
|
+
*/
|
|
25
|
+
export function verifyExecutionPayloadEnvelope(
|
|
26
|
+
config: BeaconConfig,
|
|
27
|
+
state: IBeaconStateViewGloas,
|
|
28
|
+
envelope: gloas.ExecutionPayloadEnvelope,
|
|
29
|
+
opts?: VerifyExecutionPayloadEnvelopeOpts
|
|
30
|
+
): void {
|
|
31
|
+
const {verifyExecutionRequestsRoot = true} = opts ?? {};
|
|
32
|
+
const payload = envelope.payload;
|
|
33
|
+
|
|
34
|
+
// Verify consistency with the beacon block.
|
|
35
|
+
// Compute header root on a copy of latestBlockHeader to avoid mutating state.
|
|
36
|
+
const headerValue = {...state.latestBlockHeader};
|
|
37
|
+
if (byteArrayEquals(headerValue.stateRoot, ssz.Root.defaultValue())) {
|
|
38
|
+
headerValue.stateRoot = state.hashTreeRoot();
|
|
39
|
+
}
|
|
40
|
+
const headerRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(headerValue);
|
|
41
|
+
if (!byteArrayEquals(envelope.beaconBlockRoot, headerRoot)) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Envelope's block is not the latest block header envelope=${toRootHex(envelope.beaconBlockRoot)} latestBlockHeader=${toRootHex(headerRoot)}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Verify consistency with the committed bid
|
|
48
|
+
const bid = state.latestExecutionPayloadBid;
|
|
49
|
+
if (envelope.builderIndex !== bid.builderIndex) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Builder index mismatch between envelope and committed bid envelope=${envelope.builderIndex} bid=${bid.builderIndex}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if (!byteArrayEquals(bid.prevRandao, payload.prevRandao)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Prev randao mismatch between bid and payload bid=${toHex(bid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (Number(bid.gasLimit) !== payload.gasLimit) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${Number(bid.gasLimit)}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (!byteArrayEquals(bid.blockHash, payload.blockHash)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Block hash mismatch between payload and bid payload=${toRootHex(payload.blockHash)} bid=${toRootHex(bid.blockHash)}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
// Verify execution_requests_root matches bid commitment.
|
|
70
|
+
// Can be skipped if already verified during gossip validation.
|
|
71
|
+
if (verifyExecutionRequestsRoot) {
|
|
72
|
+
const requestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
73
|
+
if (!byteArrayEquals(requestsRoot, bid.executionRequestsRoot)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Execution requests root mismatch envelope=${toRootHex(requestsRoot)} bid=${toRootHex(bid.executionRequestsRoot)}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Verify the execution payload is valid
|
|
81
|
+
if (payload.slotNumber !== state.slot) {
|
|
82
|
+
throw new Error(`Slot mismatch between payload and state payload=${payload.slotNumber} state=${state.slot}`);
|
|
83
|
+
}
|
|
84
|
+
if (!byteArrayEquals(payload.parentHash, state.latestBlockHash)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Parent hash mismatch between payload and state payload=${toRootHex(payload.parentHash)} state=${toRootHex(state.latestBlockHash)}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const expectedTimestamp = computeTimeAtSlot(config, state.slot, state.genesisTime);
|
|
90
|
+
if (payload.timestamp !== expectedTimestamp) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Timestamp mismatch between payload and state payload=${payload.timestamp} state=${expectedTimestamp}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Verify consistency with expected withdrawals
|
|
97
|
+
const payloadWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(payload.withdrawals);
|
|
98
|
+
const expectedWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(state.payloadExpectedWithdrawals);
|
|
99
|
+
if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Withdrawals mismatch between payload and expected payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Execution engine verification (verify_and_notify_new_payload) is done externally by the caller
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Verify the BLS signature of an execution payload envelope.
|
|
110
|
+
*
|
|
111
|
+
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope_signature
|
|
112
|
+
*/
|
|
113
|
+
export async function verifyExecutionPayloadEnvelopeSignature(
|
|
114
|
+
config: BeaconConfig,
|
|
115
|
+
state: IBeaconStateViewGloas,
|
|
116
|
+
pubkeyCache: PubkeyCache,
|
|
117
|
+
signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
|
|
118
|
+
proposerIndex: number,
|
|
119
|
+
bls: IBlsVerifier
|
|
120
|
+
): Promise<boolean> {
|
|
121
|
+
const signatureSet = getExecutionPayloadEnvelopeSignatureSet(
|
|
122
|
+
config,
|
|
123
|
+
pubkeyCache,
|
|
124
|
+
state,
|
|
125
|
+
signedEnvelope,
|
|
126
|
+
proposerIndex
|
|
127
|
+
);
|
|
128
|
+
return bls.verifySignatureSets([signatureSet]);
|
|
129
|
+
}
|
|
@@ -5,7 +5,7 @@ import {writeDataColumnsToDb} from "./writeBlockInputToDb.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Persists payload envelope data to DB. This operation must be eventually completed if a payload is imported.
|
|
7
7
|
*
|
|
8
|
-
* TODO GLOAS: Persist envelope metadata (
|
|
8
|
+
* TODO GLOAS: Persist envelope metadata (executionRequests, builderIndex, etc.) without the full
|
|
9
9
|
* execution payload body — only keep the blockHash reference. The EL already stores the payload.
|
|
10
10
|
* See https://github.com/ChainSafe/lodestar/issues/5671
|
|
11
11
|
*/
|
|
@@ -33,23 +33,14 @@ export async function persistPayloadEnvelopeInput(
|
|
|
33
33
|
this: BeaconChain,
|
|
34
34
|
payloadInput: PayloadEnvelopeInput
|
|
35
35
|
): Promise<void> {
|
|
36
|
-
await writePayloadEnvelopeInputToDb
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
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", {
|
|
36
|
+
await writePayloadEnvelopeInputToDb.call(this, payloadInput).catch((e) => {
|
|
37
|
+
this.logger.error(
|
|
38
|
+
"Error persisting payload envelope in hot db",
|
|
39
|
+
{
|
|
51
40
|
slot: payloadInput.slot,
|
|
52
41
|
root: payloadInput.blockRootHex,
|
|
53
|
-
}
|
|
54
|
-
|
|
42
|
+
},
|
|
43
|
+
e
|
|
44
|
+
);
|
|
45
|
+
});
|
|
55
46
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Type} from "@chainsafe/ssz";
|
|
4
4
|
import {BeaconConfig} from "@lodestar/config";
|
|
5
|
-
import {
|
|
5
|
+
import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
6
6
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
7
7
|
import {
|
|
8
8
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
ValidatorIndex,
|
|
40
40
|
Wei,
|
|
41
41
|
deneb,
|
|
42
|
+
electra,
|
|
42
43
|
gloas,
|
|
43
44
|
isBlindedBeaconBlock,
|
|
44
45
|
phase0,
|
|
@@ -680,7 +681,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
680
681
|
}
|
|
681
682
|
|
|
682
683
|
getStateByCheckpoint(
|
|
683
|
-
checkpoint:
|
|
684
|
+
checkpoint: CheckpointWithHex
|
|
684
685
|
): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null {
|
|
685
686
|
// finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
|
|
686
687
|
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
@@ -701,7 +702,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
701
702
|
}
|
|
702
703
|
|
|
703
704
|
async getStateOrBytesByCheckpoint(
|
|
704
|
-
checkpoint:
|
|
705
|
+
checkpoint: CheckpointWithHex
|
|
705
706
|
): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
706
707
|
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
707
708
|
const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex);
|
|
@@ -886,6 +887,17 @@ export class BeaconChain implements IBeaconChain {
|
|
|
886
887
|
);
|
|
887
888
|
}
|
|
888
889
|
|
|
890
|
+
async getParentExecutionRequests(
|
|
891
|
+
parentBlockSlot: Slot,
|
|
892
|
+
parentBlockRootHex: RootHex
|
|
893
|
+
): Promise<electra.ExecutionRequests> {
|
|
894
|
+
const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
|
|
895
|
+
if (envelope === null) {
|
|
896
|
+
throw Error(`Parent execution payload envelope not found slot=${parentBlockSlot}, root=${parentBlockRootHex}`);
|
|
897
|
+
}
|
|
898
|
+
return envelope.message.executionRequests;
|
|
899
|
+
}
|
|
900
|
+
|
|
889
901
|
async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
|
|
890
902
|
const fork = this.config.getForkName(blockSlot);
|
|
891
903
|
|
|
@@ -1277,7 +1289,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1277
1289
|
* @param blockState state that declares justified checkpoint `checkpoint`
|
|
1278
1290
|
*/
|
|
1279
1291
|
private justifiedBalancesGetter(
|
|
1280
|
-
checkpoint:
|
|
1292
|
+
checkpoint: CheckpointWithHex,
|
|
1281
1293
|
blockState: IBeaconStateView
|
|
1282
1294
|
): EffectiveBalanceIncrements {
|
|
1283
1295
|
this.metrics?.balancesCache.requests.inc();
|
|
@@ -1316,7 +1328,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1316
1328
|
* @param blockState state that declares justified checkpoint `checkpoint`
|
|
1317
1329
|
*/
|
|
1318
1330
|
private closestJustifiedBalancesStateToCheckpoint(
|
|
1319
|
-
checkpoint:
|
|
1331
|
+
checkpoint: CheckpointWithHex,
|
|
1320
1332
|
blockState: IBeaconStateView
|
|
1321
1333
|
): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} {
|
|
1322
1334
|
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
@@ -1331,10 +1343,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1331
1343
|
}
|
|
1332
1344
|
|
|
1333
1345
|
// Find a state in the same branch of checkpoint at same epoch. Balances should exactly the same
|
|
1334
|
-
for (const descendantBlock of this.forkChoice.
|
|
1335
|
-
checkpoint.rootHex,
|
|
1336
|
-
checkpoint.payloadStatus
|
|
1337
|
-
)) {
|
|
1346
|
+
for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
|
|
1338
1347
|
if (computeEpochAtSlot(descendantBlock.slot) === checkpoint.epoch) {
|
|
1339
1348
|
const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
|
|
1340
1349
|
if (descendantBlockState) {
|
|
@@ -1350,10 +1359,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1350
1359
|
|
|
1351
1360
|
// Find a state in the same branch of checkpoint at a latter epoch. Balances are not the same, but should be close
|
|
1352
1361
|
// Note: must call .forwardIterateDescendants() again since nodes are not sorted
|
|
1353
|
-
for (const descendantBlock of this.forkChoice.
|
|
1354
|
-
checkpoint.rootHex,
|
|
1355
|
-
checkpoint.payloadStatus
|
|
1356
|
-
)) {
|
|
1362
|
+
for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
|
|
1357
1363
|
if (computeEpochAtSlot(descendantBlock.slot) > checkpoint.epoch) {
|
|
1358
1364
|
const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
|
|
1359
1365
|
if (descendantBlockState) {
|
|
@@ -1457,7 +1463,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1457
1463
|
this.seenContributionAndProof.prune(head.slot);
|
|
1458
1464
|
}
|
|
1459
1465
|
|
|
1460
|
-
private onForkChoiceJustified(this: BeaconChain, cp:
|
|
1466
|
+
private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithHex): void {
|
|
1461
1467
|
this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
|
|
1462
1468
|
}
|
|
1463
1469
|
|
|
@@ -1468,7 +1474,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1468
1474
|
});
|
|
1469
1475
|
}
|
|
1470
1476
|
|
|
1471
|
-
private async onForkChoiceFinalized(this: BeaconChain, cp:
|
|
1477
|
+
private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
|
|
1472
1478
|
this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
|
|
1473
1479
|
const finalizedSlot = computeStartSlotAtEpoch(cp.epoch);
|
|
1474
1480
|
this.seenBlockProposers.prune(finalizedSlot);
|
|
@@ -1509,7 +1515,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1509
1515
|
}
|
|
1510
1516
|
}
|
|
1511
1517
|
|
|
1512
|
-
private async updateValidatorsCustodyRequirement(finalizedCheckpoint:
|
|
1518
|
+
private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithHex): Promise<void> {
|
|
1513
1519
|
if (this.custodyConfig.targetCustodyGroupCount === this.config.NUMBER_OF_CUSTODY_GROUPS) {
|
|
1514
1520
|
// Custody requirements can only be increased, we can disable dynamic custody updates
|
|
1515
1521
|
// if the node already maintains custody of all custody groups in case it is configured
|
package/src/chain/emitter.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {EventEmitter} from "node:events";
|
|
2
2
|
import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
3
3
|
import {routes} from "@lodestar/api";
|
|
4
|
-
import {
|
|
4
|
+
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
5
5
|
import {IBeaconStateView} from "@lodestar/state-transition";
|
|
6
6
|
import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
|
|
7
7
|
import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
|
|
@@ -110,8 +110,8 @@ export type ChainEventData = {
|
|
|
110
110
|
export type IChainEvents = ApiEvents & {
|
|
111
111
|
[ChainEvent.checkpoint]: (checkpoint: phase0.Checkpoint, state: IBeaconStateView) => void;
|
|
112
112
|
|
|
113
|
-
[ChainEvent.forkChoiceJustified]: (checkpoint:
|
|
114
|
-
[ChainEvent.forkChoiceFinalized]: (checkpoint:
|
|
113
|
+
[ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithHex) => void;
|
|
114
|
+
[ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithHex) => void;
|
|
115
115
|
|
|
116
116
|
[ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;
|
|
117
117
|
|