@lodestar/beacon-node 1.41.0-dev.192806a314 → 1.41.0-dev.20f622cc52
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 +3 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/validator/index.js +2 -2
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/utils/updateBackfillRange.js +1 -1
- package/lib/chain/archiveStore/utils/updateBackfillRange.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +26 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +4 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/chain.js +6 -6
- 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/executionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -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 +30 -24
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +4 -1
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +5 -1
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +1 -1
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.js +2 -2
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/blobSidecar.js +2 -2
- package/lib/chain/validation/blobSidecar.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +5 -2
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +1 -1
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +1 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +4 -4
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +1 -2
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +1 -0
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/package.json +26 -26
- package/src/api/impl/beacon/blocks/index.ts +3 -2
- package/src/api/impl/validator/index.ts +2 -2
- package/src/chain/archiveStore/utils/updateBackfillRange.ts +1 -1
- package/src/chain/blocks/importBlock.ts +34 -3
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +7 -2
- package/src/chain/chain.ts +6 -6
- package/src/chain/emitter.ts +3 -3
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -2
- package/src/chain/forkChoice/index.ts +39 -21
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +1 -2
- package/src/chain/regen/queued.ts +7 -2
- package/src/chain/regen/regen.ts +8 -2
- package/src/chain/validation/aggregateAndProof.ts +1 -1
- package/src/chain/validation/attestation.ts +2 -2
- package/src/chain/validation/blobSidecar.ts +2 -2
- package/src/chain/validation/block.ts +8 -3
- package/src/chain/validation/dataColumnSidecar.ts +1 -1
- package/src/chain/validation/executionPayloadBid.ts +1 -2
- package/src/chain/validation/executionPayloadEnvelope.ts +4 -4
- package/src/chain/validation/payloadAttestationMessage.ts +1 -2
- package/src/network/processor/gossipHandlers.ts +5 -0
- package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ApiError, ApplicationMethods} from "@lodestar/api/server";
|
|
3
|
+
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
3
4
|
import {
|
|
4
5
|
BUILDER_INDEX_SELF_BUILD,
|
|
5
6
|
ForkPostBellatrix,
|
|
@@ -205,7 +206,7 @@ export function getBeaconBlockApi({
|
|
|
205
206
|
case routes.beacon.BroadcastValidation.consensus: {
|
|
206
207
|
// check if this beacon node produced the block else run validations
|
|
207
208
|
if (!blockLocallyProduced) {
|
|
208
|
-
const parentBlock = chain.forkChoice.
|
|
209
|
+
const parentBlock = chain.forkChoice.getBlockDefaultStatus(signedBlock.message.parentRoot);
|
|
209
210
|
if (parentBlock === null) {
|
|
210
211
|
chain.emitter.emit(ChainEvent.unknownParent, {
|
|
211
212
|
blockInput: blockForImport,
|
|
@@ -650,7 +651,7 @@ export function getBeaconBlockApi({
|
|
|
650
651
|
}
|
|
651
652
|
|
|
652
653
|
// TODO GLOAS: review checks, do we want to implement `broadcast_validation`?
|
|
653
|
-
const block = chain.forkChoice.getBlockHex(blockRootHex);
|
|
654
|
+
const block = chain.forkChoice.getBlockHex(blockRootHex, PayloadStatus.EMPTY);
|
|
654
655
|
if (block === null) {
|
|
655
656
|
throw new ApiError(404, `Block not found for beacon block root ${blockRootHex}`);
|
|
656
657
|
}
|
|
@@ -389,7 +389,7 @@ export function getValidatorApi(
|
|
|
389
389
|
*/
|
|
390
390
|
|
|
391
391
|
function notOnOptimisticBlockRoot(beaconBlockRoot: Root): void {
|
|
392
|
-
const protoBeaconBlock = chain.forkChoice.
|
|
392
|
+
const protoBeaconBlock = chain.forkChoice.getBlockDefaultStatus(beaconBlockRoot);
|
|
393
393
|
if (!protoBeaconBlock) {
|
|
394
394
|
throw new ApiError(404, `Block not in forkChoice, beaconBlockRoot=${toRootHex(beaconBlockRoot)}`);
|
|
395
395
|
}
|
|
@@ -401,7 +401,7 @@ export function getValidatorApi(
|
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
function notOnOutOfRangeData(beaconBlockRoot: Root): void {
|
|
404
|
-
const protoBeaconBlock = chain.forkChoice.
|
|
404
|
+
const protoBeaconBlock = chain.forkChoice.getBlockDefaultStatus(beaconBlockRoot);
|
|
405
405
|
if (!protoBeaconBlock) {
|
|
406
406
|
throw new ApiError(404, `Block not in forkChoice, beaconBlockRoot=${toRootHex(beaconBlockRoot)}`);
|
|
407
407
|
}
|
|
@@ -20,7 +20,7 @@ export async function updateBackfillRange(
|
|
|
20
20
|
try {
|
|
21
21
|
// Mark the sequence in backfill db from finalized block's slot till anchor slot as
|
|
22
22
|
// filled.
|
|
23
|
-
const finalizedBlockFC = chain.forkChoice.
|
|
23
|
+
const finalizedBlockFC = chain.forkChoice.getBlockHexDefaultStatus(finalized.rootHex);
|
|
24
24
|
if (finalizedBlockFC && finalizedBlockFC.slot > chain.anchorStateLatestBlockSlot) {
|
|
25
25
|
await db.backfilledRanges.put(finalizedBlockFC.slot, chain.anchorStateLatestBlockSlot);
|
|
26
26
|
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
isStartSlotOfEpoch,
|
|
21
21
|
isStateValidatorsNodesPopulated,
|
|
22
22
|
} from "@lodestar/state-transition";
|
|
23
|
-
import {Attestation, BeaconBlock, altair, capella, electra, phase0, ssz} from "@lodestar/types";
|
|
23
|
+
import {Attestation, BeaconBlock, altair, capella, electra, isGloasBeaconBlock, phase0, ssz} from "@lodestar/types";
|
|
24
24
|
import {isErrorAborted, toRootHex} from "@lodestar/utils";
|
|
25
25
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
26
26
|
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
@@ -230,6 +230,32 @@ export async function importBlock(
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// 4.5. Import payload attestations to fork choice (Gloas)
|
|
234
|
+
//
|
|
235
|
+
if (isGloasBeaconBlock(block.message)) {
|
|
236
|
+
for (const payloadAttestation of block.message.body.payloadAttestations) {
|
|
237
|
+
try {
|
|
238
|
+
// Extract PTC indices from aggregation bits
|
|
239
|
+
const ptcIndices: number[] = [];
|
|
240
|
+
for (let i = 0; i < payloadAttestation.aggregationBits.bitLen; i++) {
|
|
241
|
+
if (payloadAttestation.aggregationBits.get(i)) {
|
|
242
|
+
ptcIndices.push(i);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (ptcIndices.length > 0) {
|
|
247
|
+
this.forkChoice.notifyPtcMessages(
|
|
248
|
+
toRootHex(payloadAttestation.data.beaconBlockRoot),
|
|
249
|
+
ptcIndices,
|
|
250
|
+
payloadAttestation.data.payloadPresent
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
} catch (e) {
|
|
254
|
+
this.logger.warn("Error processing PayloadAttestation from block", {slot: blockSlot}, e as Error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
233
259
|
// 5. Compute head. If new head, immediately stateCache.setHeadState()
|
|
234
260
|
|
|
235
261
|
const oldHead = this.forkChoice.getHead();
|
|
@@ -345,7 +371,7 @@ export async function importBlock(
|
|
|
345
371
|
// 3) Proposer boost reorg related flag is turned on (this is checked inside the function)
|
|
346
372
|
// 4) Block meets the criteria of being re-orged out (this is also checked inside the function)
|
|
347
373
|
const result = this.forkChoice.shouldOverrideForkChoiceUpdate(
|
|
348
|
-
blockSummary
|
|
374
|
+
blockSummary,
|
|
349
375
|
this.clock.secFromSlot(currentSlot),
|
|
350
376
|
currentSlot
|
|
351
377
|
);
|
|
@@ -441,7 +467,12 @@ export async function importBlock(
|
|
|
441
467
|
this.metrics?.currentActiveValidators.set(activeValidatorsCount);
|
|
442
468
|
this.metrics?.currentValidators.set({status: "active"}, activeValidatorsCount);
|
|
443
469
|
|
|
444
|
-
const parentBlockSummary =
|
|
470
|
+
const parentBlockSummary = isGloasBeaconBlock(block.message)
|
|
471
|
+
? this.forkChoice.getBlockHexAndBlockHash(
|
|
472
|
+
toRootHex(checkpointState.latestBlockHeader.parentRoot),
|
|
473
|
+
toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
474
|
+
)
|
|
475
|
+
: this.forkChoice.getBlockDefaultStatus(checkpointState.latestBlockHeader.parentRoot);
|
|
445
476
|
|
|
446
477
|
if (parentBlockSummary) {
|
|
447
478
|
const justifiedCheckpoint = checkpointState.currentJustifiedCheckpoint;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
3
3
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
|
-
import {RootHex, Slot} from "@lodestar/types";
|
|
4
|
+
import {RootHex, Slot, isGloasBeaconBlock} from "@lodestar/types";
|
|
5
5
|
import {toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {IClock} from "../../util/clock.js";
|
|
7
7
|
import {BlockError, BlockErrorCode} from "../errors/index.js";
|
|
@@ -90,7 +90,12 @@ export function verifyBlocksSanityChecks(
|
|
|
90
90
|
} else {
|
|
91
91
|
// When importing a block segment, only the first NON-IGNORED block must be known to the fork-choice.
|
|
92
92
|
const parentRoot = toRootHex(block.message.parentRoot);
|
|
93
|
-
parentBlock =
|
|
93
|
+
parentBlock = isGloasBeaconBlock(block.message)
|
|
94
|
+
? chain.forkChoice.getBlockHexAndBlockHash(
|
|
95
|
+
parentRoot,
|
|
96
|
+
toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
97
|
+
)
|
|
98
|
+
: chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
94
99
|
if (!parentBlock) {
|
|
95
100
|
throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
|
|
96
101
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -600,7 +600,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
600
600
|
): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
601
601
|
if (opts?.allowRegen) {
|
|
602
602
|
const state = await this.regen.getState(stateRoot, RegenCaller.restApi);
|
|
603
|
-
const block = this.forkChoice.
|
|
603
|
+
const block = this.forkChoice.getBlockDefaultStatus(state.latestBlockHeader.hashTreeRoot());
|
|
604
604
|
const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
605
605
|
return {
|
|
606
606
|
state,
|
|
@@ -616,7 +616,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
616
616
|
// TODO: This is very inneficient for debug requests of serialized content, since it deserializes to serialize again
|
|
617
617
|
const cachedStateCtx = this.regen.getStateSync(stateRoot);
|
|
618
618
|
if (cachedStateCtx) {
|
|
619
|
-
const block = this.forkChoice.
|
|
619
|
+
const block = this.forkChoice.getBlockDefaultStatus(cachedStateCtx.latestBlockHeader.hashTreeRoot());
|
|
620
620
|
const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
621
621
|
return {
|
|
622
622
|
state: cachedStateCtx,
|
|
@@ -650,7 +650,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
650
650
|
// finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
|
|
651
651
|
const cachedStateCtx = this.regen.getCheckpointStateSync(checkpoint);
|
|
652
652
|
if (cachedStateCtx) {
|
|
653
|
-
const block = this.forkChoice.
|
|
653
|
+
const block = this.forkChoice.getBlockDefaultStatus(cachedStateCtx.latestBlockHeader.hashTreeRoot());
|
|
654
654
|
const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
655
655
|
return {
|
|
656
656
|
state: cachedStateCtx,
|
|
@@ -667,7 +667,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
667
667
|
): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
668
668
|
const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpoint);
|
|
669
669
|
if (cachedStateCtx) {
|
|
670
|
-
const block = this.forkChoice.
|
|
670
|
+
const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root);
|
|
671
671
|
const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
672
672
|
return {
|
|
673
673
|
state: cachedStateCtx,
|
|
@@ -711,7 +711,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
711
711
|
async getBlockByRoot(
|
|
712
712
|
root: string
|
|
713
713
|
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
714
|
-
const block = this.forkChoice.
|
|
714
|
+
const block = this.forkChoice.getBlockHexDefaultStatus(root);
|
|
715
715
|
if (block) {
|
|
716
716
|
// Block found in fork-choice.
|
|
717
717
|
// It may be in the block input cache, awaiting full DA reconstruction, check there first
|
|
@@ -735,7 +735,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
735
735
|
async getSerializedBlockByRoot(
|
|
736
736
|
root: string
|
|
737
737
|
): Promise<{block: Uint8Array; executionOptimistic: boolean; finalized: boolean; slot: Slot} | null> {
|
|
738
|
-
const block = this.forkChoice.
|
|
738
|
+
const block = this.forkChoice.getBlockHexDefaultStatus(root);
|
|
739
739
|
if (block) {
|
|
740
740
|
// Block found in fork-choice.
|
|
741
741
|
// It may be in the block input cache, awaiting full DA reconstruction, check there first
|
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 {CheckpointWithPayload} from "@lodestar/fork-choice";
|
|
5
5
|
import {CachedBeaconStateAllForks} from "@lodestar/state-transition";
|
|
6
6
|
import {DataColumnSidecars, RootHex, deneb, phase0} from "@lodestar/types";
|
|
7
7
|
import {PeerIdStr} from "../util/peerId.js";
|
|
@@ -83,8 +83,8 @@ export type ChainEventData = {
|
|
|
83
83
|
export type IChainEvents = ApiEvents & {
|
|
84
84
|
[ChainEvent.checkpoint]: (checkpoint: phase0.Checkpoint, state: CachedBeaconStateAllForks) => void;
|
|
85
85
|
|
|
86
|
-
[ChainEvent.forkChoiceJustified]: (checkpoint:
|
|
87
|
-
[ChainEvent.forkChoiceFinalized]: (checkpoint:
|
|
86
|
+
[ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithPayload) => void;
|
|
87
|
+
[ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithPayload) => void;
|
|
88
88
|
|
|
89
89
|
[ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;
|
|
90
90
|
|
|
@@ -25,9 +25,13 @@ export type ExecutionPayloadEnvelopeErrorType =
|
|
|
25
25
|
| {
|
|
26
26
|
code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH;
|
|
27
27
|
envelopeBuilderIndex: BuilderIndex;
|
|
28
|
-
bidBuilderIndex: BuilderIndex;
|
|
28
|
+
bidBuilderIndex: BuilderIndex | null;
|
|
29
|
+
}
|
|
30
|
+
| {
|
|
31
|
+
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH;
|
|
32
|
+
envelopeBlockHash: RootHex;
|
|
33
|
+
bidBlockHash: RootHex | null;
|
|
29
34
|
}
|
|
30
|
-
| {code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH; envelopeBlockHash: RootHex; bidBlockHash: RootHex}
|
|
31
35
|
| {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE}
|
|
32
36
|
| {code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL; blockRoot: RootHex};
|
|
33
37
|
|
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
ForkChoice,
|
|
5
5
|
ForkChoiceStore,
|
|
6
6
|
JustifiedBalancesGetter,
|
|
7
|
+
PayloadStatus,
|
|
7
8
|
ProtoArray,
|
|
8
9
|
ProtoBlock,
|
|
9
10
|
ForkChoiceOpts as RawForkChoiceOpts,
|
|
11
|
+
getCheckpointPayloadStatus,
|
|
10
12
|
} from "@lodestar/fork-choice";
|
|
11
13
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
12
14
|
import {
|
|
@@ -104,6 +106,14 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
104
106
|
// production code use ForkChoice constructor directly
|
|
105
107
|
const forkchoiceConstructor = opts.forkchoiceConstructor ?? ForkChoice;
|
|
106
108
|
|
|
109
|
+
const isForkPostGloas = (state as CachedBeaconStateGloas).latestBlockHash !== undefined;
|
|
110
|
+
|
|
111
|
+
// Determine justified checkpoint payload status
|
|
112
|
+
const justifiedPayloadStatus = getCheckpointPayloadStatus(state, justifiedCheckpoint.epoch);
|
|
113
|
+
|
|
114
|
+
// Determine finalized checkpoint payload status
|
|
115
|
+
const finalizedPayloadStatus = getCheckpointPayloadStatus(state, finalizedCheckpoint.epoch);
|
|
116
|
+
|
|
107
117
|
return new forkchoiceConstructor(
|
|
108
118
|
config,
|
|
109
119
|
|
|
@@ -113,6 +123,8 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
113
123
|
finalizedCheckpoint,
|
|
114
124
|
justifiedBalances,
|
|
115
125
|
justifiedBalancesGetter,
|
|
126
|
+
justifiedPayloadStatus,
|
|
127
|
+
finalizedPayloadStatus,
|
|
116
128
|
{
|
|
117
129
|
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
|
|
118
130
|
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
|
|
@@ -145,15 +157,12 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
145
157
|
: {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
|
|
146
158
|
|
|
147
159
|
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
builderIndex: (state as CachedBeaconStateGloas).latestExecutionPayloadBid.builderIndex,
|
|
155
|
-
blockHashHex: toRootHex((state as CachedBeaconStateGloas).latestExecutionPayloadBid.blockHash),
|
|
156
|
-
}),
|
|
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
|
+
parentBlockHash: isForkPostGloas ? toRootHex((state as CachedBeaconStateGloas).latestBlockHash) : null,
|
|
157
166
|
},
|
|
158
167
|
currentSlot
|
|
159
168
|
),
|
|
@@ -196,12 +205,22 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
196
205
|
|
|
197
206
|
// this is not the justified state, but there is no other ways to get justified balances
|
|
198
207
|
const justifiedBalances = getEffectiveBalanceIncrementsZeroInactive(unfinalizedState);
|
|
208
|
+
|
|
209
|
+
const isForkPostGloas = (unfinalizedState as CachedBeaconStateGloas).latestBlockHash !== undefined;
|
|
210
|
+
|
|
211
|
+
// For unfinalized state, use getCheckpointPayloadStatus to determine the correct status.
|
|
212
|
+
// It checks state.execution_payload_availability to determine EMPTY vs FULL.
|
|
213
|
+
const justifiedPayloadStatus = getCheckpointPayloadStatus(unfinalizedState, justifiedCheckpoint.epoch);
|
|
214
|
+
const finalizedPayloadStatus = getCheckpointPayloadStatus(unfinalizedState, finalizedCheckpoint.epoch);
|
|
215
|
+
|
|
199
216
|
const store = new ForkChoiceStore(
|
|
200
217
|
currentSlot,
|
|
201
218
|
justifiedCheckpoint,
|
|
202
219
|
finalizedCheckpoint,
|
|
203
220
|
justifiedBalances,
|
|
204
221
|
justifiedBalancesGetter,
|
|
222
|
+
justifiedPayloadStatus,
|
|
223
|
+
finalizedPayloadStatus,
|
|
205
224
|
{
|
|
206
225
|
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
|
|
207
226
|
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
|
|
@@ -235,15 +254,14 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
235
254
|
: {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
|
|
236
255
|
|
|
237
256
|
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}),
|
|
257
|
+
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
|
+
parentBlockHash: isForkPostGloas ? toRootHex((unfinalizedState as CachedBeaconStateGloas).latestBlockHash) : null,
|
|
247
265
|
};
|
|
248
266
|
|
|
249
267
|
const parentSlot = blockHeader.slot - 1;
|
|
@@ -285,9 +303,9 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
285
303
|
};
|
|
286
304
|
|
|
287
305
|
const protoArray = ProtoArray.initialize(finalizedBlock, currentSlot);
|
|
288
|
-
protoArray.onBlock(justifiedBlock, currentSlot);
|
|
289
|
-
protoArray.onBlock(parentBlock, currentSlot);
|
|
290
|
-
protoArray.onBlock(headBlock, currentSlot);
|
|
306
|
+
protoArray.onBlock(justifiedBlock, currentSlot, null);
|
|
307
|
+
protoArray.onBlock(parentBlock, currentSlot, null);
|
|
308
|
+
protoArray.onBlock(headBlock, currentSlot, null);
|
|
291
309
|
|
|
292
310
|
logger?.verbose("Initialized protoArray successfully", {...logCtx, length: protoArray.length()});
|
|
293
311
|
|
|
@@ -864,7 +864,7 @@ function isValidShuffling(
|
|
|
864
864
|
// attestation's shuffling is the same as the current state's.
|
|
865
865
|
// To account for skipped slots, find the first block at *or before* the pivot slot.
|
|
866
866
|
const beaconBlockRootHex = blockRootHex;
|
|
867
|
-
const beaconBlock = forkChoice.
|
|
867
|
+
const beaconBlock = forkChoice.getBlockHexDefaultStatus(beaconBlockRootHex);
|
|
868
868
|
if (!beaconBlock) {
|
|
869
869
|
return InvalidAttestationData.BlockNotInForkChoice;
|
|
870
870
|
}
|
|
@@ -723,8 +723,7 @@ export function getPayloadAttributesForSSE(
|
|
|
723
723
|
|
|
724
724
|
let parentBlockNumber: number;
|
|
725
725
|
if (isForkPostGloas(fork)) {
|
|
726
|
-
|
|
727
|
-
const parentBlock = chain.forkChoice.getBlock(parentBlockRoot);
|
|
726
|
+
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(toRootHex(parentBlockRoot), toRootHex(parentHash));
|
|
728
727
|
if (parentBlock?.executionPayloadBlockHash == null) {
|
|
729
728
|
throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
|
|
730
729
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
3
3
|
import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
4
|
-
import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
|
|
4
|
+
import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lodestar/types";
|
|
5
5
|
import {Logger, toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {Metrics} from "../../metrics/index.js";
|
|
7
7
|
import {JobItemQueue} from "../../util/queue/index.js";
|
|
@@ -88,7 +88,12 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
88
88
|
*/
|
|
89
89
|
getPreStateSync(block: BeaconBlock): CachedBeaconStateAllForks | null {
|
|
90
90
|
const parentRoot = toRootHex(block.parentRoot);
|
|
91
|
-
const parentBlock =
|
|
91
|
+
const parentBlock = isGloasBeaconBlock(block)
|
|
92
|
+
? this.forkChoice.getBlockHexAndBlockHash(
|
|
93
|
+
parentRoot,
|
|
94
|
+
toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
95
|
+
)
|
|
96
|
+
: this.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
92
97
|
if (!parentBlock) {
|
|
93
98
|
throw new RegenError({
|
|
94
99
|
code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
|
package/src/chain/regen/regen.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
processSlots,
|
|
12
12
|
stateTransition,
|
|
13
13
|
} from "@lodestar/state-transition";
|
|
14
|
-
import {BeaconBlock, RootHex, SignedBeaconBlock, Slot} from "@lodestar/types";
|
|
14
|
+
import {BeaconBlock, RootHex, SignedBeaconBlock, Slot, isGloasBeaconBlock} from "@lodestar/types";
|
|
15
15
|
import {Logger, fromHex, toRootHex} from "@lodestar/utils";
|
|
16
16
|
import {IBeaconDb} from "../../db/index.js";
|
|
17
17
|
import {Metrics} from "../../metrics/index.js";
|
|
@@ -58,7 +58,13 @@ export class StateRegenerator implements IStateRegeneratorInternal {
|
|
|
58
58
|
opts: StateRegenerationOpts,
|
|
59
59
|
regenCaller: RegenCaller
|
|
60
60
|
): Promise<CachedBeaconStateAllForks> {
|
|
61
|
-
const
|
|
61
|
+
const parentRoot = toRootHex(block.parentRoot);
|
|
62
|
+
const parentBlock = isGloasBeaconBlock(block)
|
|
63
|
+
? this.modules.forkChoice.getBlockHexAndBlockHash(
|
|
64
|
+
parentRoot,
|
|
65
|
+
toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
66
|
+
)
|
|
67
|
+
: this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
62
68
|
if (!parentBlock) {
|
|
63
69
|
throw new RegenError({
|
|
64
70
|
code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
|
|
@@ -81,7 +81,7 @@ async function validateAggregateAndProof(
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
// [REJECT] `aggregate.data.index == 0` if `block.slot == aggregate.data.slot`.
|
|
84
|
-
const block = chain.forkChoice.
|
|
84
|
+
const block = chain.forkChoice.getBlockDefaultStatus(attData.beaconBlockRoot);
|
|
85
85
|
|
|
86
86
|
// If block is unknown, we don't handle it here. It will throw error later on at `verifyHeadBlockAndTargetRoot()`
|
|
87
87
|
if (block !== null && block.slot === attData.slot && attData.index !== 0) {
|
|
@@ -307,7 +307,7 @@ async function validateAttestationNoSignatureCheck(
|
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
// [REJECT] `attestation.data.index == 0` if `block.slot == attestation.data.slot`.
|
|
310
|
-
const block = chain.forkChoice.
|
|
310
|
+
const block = chain.forkChoice.getBlockDefaultStatus(attData.beaconBlockRoot);
|
|
311
311
|
|
|
312
312
|
// block being null will be handled by `verifyHeadBlockAndTargetRoot`
|
|
313
313
|
if (block !== null && block.slot === attSlot && attData.index !== 0) {
|
|
@@ -756,7 +756,7 @@ export function getAttestationDataSigningRoot(config: BeaconConfig, data: phase0
|
|
|
756
756
|
function verifyHeadBlockIsKnown(chain: IBeaconChain, beaconBlockRoot: Root): ProtoBlock {
|
|
757
757
|
// TODO (LH): Enforce a maximum skip distance for unaggregated attestations.
|
|
758
758
|
|
|
759
|
-
const headBlock = chain.forkChoice.
|
|
759
|
+
const headBlock = chain.forkChoice.getBlockDefaultStatus(beaconBlockRoot);
|
|
760
760
|
if (headBlock === null) {
|
|
761
761
|
throw new AttestationError(GossipAction.IGNORE, {
|
|
762
762
|
code: AttestationErrorCode.UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT,
|
|
@@ -78,7 +78,7 @@ export async function validateGossipBlobSidecar(
|
|
|
78
78
|
// already know this block.
|
|
79
79
|
const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobSidecar.signedBlockHeader.message);
|
|
80
80
|
const blockHex = toRootHex(blockRoot);
|
|
81
|
-
if (chain.forkChoice.
|
|
81
|
+
if (chain.forkChoice.getBlockHexDefaultStatus(blockHex) !== null) {
|
|
82
82
|
throw new BlobSidecarGossipError(GossipAction.IGNORE, {code: BlobSidecarErrorCode.ALREADY_KNOWN, root: blockHex});
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -89,7 +89,7 @@ export async function validateGossipBlobSidecar(
|
|
|
89
89
|
// gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is
|
|
90
90
|
// retrieved).
|
|
91
91
|
const parentRoot = toRootHex(blobSidecar.signedBlockHeader.message.parentRoot);
|
|
92
|
-
const parentBlock = chain.forkChoice.
|
|
92
|
+
const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
93
93
|
if (parentBlock === null) {
|
|
94
94
|
// If fork choice does *not* consider the parent to be a descendant of the finalized block,
|
|
95
95
|
// then there are two more cases:
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isExecutionEnabled,
|
|
10
10
|
isExecutionStateType,
|
|
11
11
|
} from "@lodestar/state-transition";
|
|
12
|
-
import {SignedBeaconBlock, deneb, gloas} from "@lodestar/types";
|
|
12
|
+
import {SignedBeaconBlock, deneb, gloas, isGloasBeaconBlock} from "@lodestar/types";
|
|
13
13
|
import {byteArrayEquals, sleep, toRootHex} from "@lodestar/utils";
|
|
14
14
|
import {BlockErrorCode, BlockGossipError, GossipAction} from "../errors/index.js";
|
|
15
15
|
import {IBeaconChain} from "../interface.js";
|
|
@@ -56,7 +56,7 @@ export async function validateGossipBlock(
|
|
|
56
56
|
// check, we will load the parent and state from disk only to find out later that we
|
|
57
57
|
// already know this block.
|
|
58
58
|
const blockRoot = toRootHex(config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block));
|
|
59
|
-
if (chain.forkChoice.
|
|
59
|
+
if (chain.forkChoice.getBlockHexDefaultStatus(blockRoot) !== null) {
|
|
60
60
|
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -72,7 +72,12 @@ export async function validateGossipBlock(
|
|
|
72
72
|
// [REJECT] The current finalized_checkpoint is an ancestor of block -- i.e.
|
|
73
73
|
// get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == store.finalized_checkpoint.root
|
|
74
74
|
const parentRoot = toRootHex(block.parentRoot);
|
|
75
|
-
const parentBlock =
|
|
75
|
+
const parentBlock = isGloasBeaconBlock(block)
|
|
76
|
+
? chain.forkChoice.getBlockHexAndBlockHash(
|
|
77
|
+
parentRoot,
|
|
78
|
+
toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
79
|
+
)
|
|
80
|
+
: chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
76
81
|
if (parentBlock === null) {
|
|
77
82
|
// If fork choice does *not* consider the parent to be a descendant of the finalized block,
|
|
78
83
|
// then there are two more cases:
|
|
@@ -73,7 +73,7 @@ export async function validateGossipDataColumnSidecar(
|
|
|
73
73
|
// 6) [IGNORE] The sidecar's block's parent (defined by block_header.parent_root) has been seen (via gossip
|
|
74
74
|
// or non-gossip sources)
|
|
75
75
|
const parentRoot = toRootHex(blockHeader.parentRoot);
|
|
76
|
-
const parentBlock = chain.forkChoice.
|
|
76
|
+
const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
77
77
|
if (parentBlock === null) {
|
|
78
78
|
// If fork choice does *not* consider the parent to be a descendant of the finalized block,
|
|
79
79
|
// then there are two more cases:
|
|
@@ -113,8 +113,7 @@ async function validateExecutionPayloadBid(
|
|
|
113
113
|
|
|
114
114
|
// [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon
|
|
115
115
|
// block in fork choice.
|
|
116
|
-
|
|
117
|
-
if (block === null) {
|
|
116
|
+
if (!chain.forkChoice.hasBlock(bid.parentBlockRoot)) {
|
|
118
117
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
119
118
|
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
120
119
|
parentBlockRoot: parentBlockRootHex,
|
|
@@ -37,7 +37,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
37
37
|
// the block is retrieved).
|
|
38
38
|
// TODO GLOAS: Need to review this, we should queue the envelope for later
|
|
39
39
|
// processing if the block is not yet known, otherwise we would ignore it here
|
|
40
|
-
const block = chain.forkChoice.
|
|
40
|
+
const block = chain.forkChoice.getBlockDefaultStatus(envelope.beaconBlockRoot);
|
|
41
41
|
if (block === null) {
|
|
42
42
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
43
43
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN,
|
|
@@ -79,7 +79,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if (block.builderIndex
|
|
82
|
+
if (block.builderIndex == null || block.blockHashFromBid == null) {
|
|
83
83
|
// This indicates this block is a pre-gloas block which is wrong
|
|
84
84
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
85
85
|
code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
|
|
@@ -97,11 +97,11 @@ async function validateExecutionPayloadEnvelope(
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
// [REJECT] `payload.block_hash == bid.block_hash`
|
|
100
|
-
if (toRootHex(payload.blockHash) !== block.
|
|
100
|
+
if (toRootHex(payload.blockHash) !== block.blockHashFromBid) {
|
|
101
101
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
102
102
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
|
|
103
103
|
envelopeBlockHash: toRootHex(payload.blockHash),
|
|
104
|
-
bidBlockHash: block.
|
|
104
|
+
bidBlockHash: block.blockHashFromBid,
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -59,8 +59,7 @@ async function validatePayloadAttestationMessage(
|
|
|
59
59
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
60
60
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
61
61
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
62
|
-
|
|
63
|
-
if (block === null) {
|
|
62
|
+
if (!chain.forkChoice.hasBlock(data.beaconBlockRoot)) {
|
|
64
63
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
65
64
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
66
65
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
@@ -856,6 +856,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
856
856
|
} catch (e) {
|
|
857
857
|
logger.error("Error adding to payloadAttestation pool", {}, e as Error);
|
|
858
858
|
}
|
|
859
|
+
chain.forkChoice.notifyPtcMessages(
|
|
860
|
+
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
861
|
+
[validationResult.validatorCommitteeIndex],
|
|
862
|
+
payloadAttestationMessage.data.payloadPresent
|
|
863
|
+
);
|
|
859
864
|
},
|
|
860
865
|
[GossipType.execution_payload_bid]: async ({
|
|
861
866
|
gossipData,
|
|
@@ -20,7 +20,7 @@ export async function* onBlobSidecarsByRoot(
|
|
|
20
20
|
for (const blobIdentifier of requestBody) {
|
|
21
21
|
const {blockRoot, index} = blobIdentifier;
|
|
22
22
|
const blockRootHex = toRootHex(blockRoot);
|
|
23
|
-
const block = chain.forkChoice.
|
|
23
|
+
const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex);
|
|
24
24
|
|
|
25
25
|
// NOTE: Only support non-finalized blocks.
|
|
26
26
|
// SPEC: Clients MUST support requesting blocks and sidecars since the latest finalized epoch.
|
|
@@ -34,7 +34,7 @@ export async function* onDataColumnSidecarsByRoot(
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const blockRootHex = toRootHex(blockRoot);
|
|
37
|
-
const block = chain.forkChoice.
|
|
37
|
+
const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex);
|
|
38
38
|
// If the block is not in fork choice, it may be finalized. Attempt to find its slot in block archive
|
|
39
39
|
const slot = block ? block.slot : await db.blockArchive.getSlotByRoot(blockRoot);
|
|
40
40
|
|