@lodestar/beacon-node 1.43.0-dev.ade910fc78 → 1.43.0-dev.e341cdc614
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/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +4 -3
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts +1 -1
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js +1 -2
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +23 -31
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +10 -8
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.js +1 -1
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +1 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +14 -30
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +13 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/attestationError.d.ts +8 -1
- package/lib/chain/errors/attestationError.d.ts.map +1 -1
- package/lib/chain/errors/attestationError.js +4 -0
- package/lib/chain/errors/attestationError.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +6 -2
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -6
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +0 -6
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/errors.d.ts +1 -11
- package/lib/chain/regen/errors.d.ts.map +1 -1
- package/lib/chain/regen/errors.js +0 -2
- package/lib/chain/regen/errors.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +6 -11
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/queued.d.ts +6 -10
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +3 -10
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +0 -5
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +0 -8
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -7
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +0 -8
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +0 -6
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +12 -0
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +12 -0
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +2 -2
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +19 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/validator/index.ts +6 -5
- package/src/chain/GetBlobsTracker.ts +1 -2
- package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +2 -4
- package/src/chain/blocks/importBlock.ts +22 -35
- package/src/chain/blocks/importExecutionPayload.ts +11 -7
- package/src/chain/blocks/index.ts +1 -1
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
- package/src/chain/blocks/types.ts +1 -1
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
- package/src/chain/chain.ts +13 -32
- package/src/chain/emitter.ts +12 -0
- package/src/chain/errors/attestationError.ts +6 -1
- package/src/chain/forkChoice/index.ts +6 -2
- package/src/chain/prepareNextSlot.ts +2 -6
- package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
- package/src/chain/produceBlock/produceBlockBody.ts +0 -6
- package/src/chain/regen/errors.ts +1 -6
- package/src/chain/regen/interface.ts +6 -11
- package/src/chain/regen/queued.ts +6 -14
- package/src/chain/regen/regen.ts +0 -8
- package/src/chain/stateCache/persistentCheckpointsCache.ts +1 -14
- package/src/chain/stateCache/types.ts +0 -3
- package/src/chain/validation/aggregateAndProof.ts +13 -0
- package/src/chain/validation/attestation.ts +13 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +23 -7
|
@@ -90,7 +90,7 @@ export type ImportBlockOpts = {
|
|
|
90
90
|
|
|
91
91
|
type FullyVerifiedBlockBase = {
|
|
92
92
|
blockInput: IBlockInput;
|
|
93
|
-
|
|
93
|
+
postState: IBeaconStateView;
|
|
94
94
|
parentBlockSlot: Slot;
|
|
95
95
|
proposerBalanceDelta: number;
|
|
96
96
|
dataAvailabilityStatus: DataAvailabilityStatus;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {DataAvailabilityStatus} from "@lodestar/state-transition";
|
|
2
|
+
import {gloas} from "@lodestar/types";
|
|
3
|
+
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
4
|
+
|
|
5
|
+
// we can now wait for full 12 seconds because sync and reconstruction will try pulling
|
|
6
|
+
// the data columns from the network anyway while the envelope is being processed
|
|
7
|
+
export const PAYLOAD_DATA_AVAILABILITY_TIMEOUT = 12_000;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Verifies that all payload envelope inputs have their data columns available.
|
|
11
|
+
* - Waits a max of PAYLOAD_DATA_AVAILABILITY_TIMEOUT for all data to be available
|
|
12
|
+
* - Returns the time at which all data was available
|
|
13
|
+
* - Returns the data availability status for each payload input
|
|
14
|
+
*/
|
|
15
|
+
export async function verifyPayloadsDataAvailability(
|
|
16
|
+
payloadInputs: PayloadEnvelopeInput[],
|
|
17
|
+
signal: AbortSignal
|
|
18
|
+
): Promise<{
|
|
19
|
+
dataAvailabilityStatuses: DataAvailabilityStatus[];
|
|
20
|
+
availableTime: number;
|
|
21
|
+
}> {
|
|
22
|
+
const promises: Promise<gloas.DataColumnSidecar[]>[] = [];
|
|
23
|
+
for (const payloadInput of payloadInputs) {
|
|
24
|
+
if (!payloadInput.hasAllData()) {
|
|
25
|
+
promises.push(payloadInput.waitForAllData(PAYLOAD_DATA_AVAILABILITY_TIMEOUT, signal));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await Promise.all(promises);
|
|
29
|
+
|
|
30
|
+
const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
|
|
31
|
+
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
|
|
32
|
+
payloadInput.getBlobKzgCommitments().length === 0
|
|
33
|
+
? DataAvailabilityStatus.NotRequired
|
|
34
|
+
: DataAvailabilityStatus.Available
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return {dataAvailabilityStatuses, availableTime};
|
|
38
|
+
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -5,7 +5,6 @@ import {BeaconConfig} from "@lodestar/config";
|
|
|
5
5
|
import {CheckpointWithPayloadStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
6
6
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
7
7
|
import {
|
|
8
|
-
BUILDER_INDEX_SELF_BUILD,
|
|
9
8
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
10
9
|
type ForkPostFulu,
|
|
11
10
|
type ForkPostGloas,
|
|
@@ -24,7 +23,6 @@ import {
|
|
|
24
23
|
getEffectiveBalancesFromStateBytes,
|
|
25
24
|
isStatePostAltair,
|
|
26
25
|
isStatePostElectra,
|
|
27
|
-
isStatePostGloas,
|
|
28
26
|
} from "@lodestar/state-transition";
|
|
29
27
|
import {
|
|
30
28
|
BeaconBlock,
|
|
@@ -93,8 +91,8 @@ import {
|
|
|
93
91
|
} from "./opPools/index.js";
|
|
94
92
|
import {IChainOptions} from "./options.js";
|
|
95
93
|
import {PrepareNextSlotScheduler} from "./prepareNextSlot.js";
|
|
96
|
-
import {computeNewStateRoot
|
|
97
|
-
import {AssembledBlockType, BlockType,
|
|
94
|
+
import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
|
|
95
|
+
import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index.js";
|
|
98
96
|
import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
|
|
99
97
|
import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
|
|
100
98
|
import {ReprocessController} from "./reprocess.js";
|
|
@@ -118,7 +116,7 @@ import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datasto
|
|
|
118
116
|
import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
|
|
119
117
|
import {CPStateDatastore} from "./stateCache/datastore/types.js";
|
|
120
118
|
import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js";
|
|
121
|
-
import {PersistentCheckpointStateCache
|
|
119
|
+
import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js";
|
|
122
120
|
import {CheckpointStateCache} from "./stateCache/types.js";
|
|
123
121
|
import {ValidatorMonitor} from "./validatorMonitor.js";
|
|
124
122
|
|
|
@@ -685,8 +683,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
685
683
|
checkpoint: CheckpointWithPayloadStatus
|
|
686
684
|
): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null {
|
|
687
685
|
// finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
|
|
688
|
-
const
|
|
689
|
-
const cachedStateCtx = this.regen.getCheckpointStateSync(
|
|
686
|
+
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
687
|
+
const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHex);
|
|
690
688
|
if (cachedStateCtx) {
|
|
691
689
|
const block = this.forkChoice.getBlockDefaultStatus(
|
|
692
690
|
ssz.phase0.BeaconBlockHeader.hashTreeRoot(cachedStateCtx.latestBlockHeader)
|
|
@@ -705,8 +703,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
705
703
|
async getStateOrBytesByCheckpoint(
|
|
706
704
|
checkpoint: CheckpointWithPayloadStatus
|
|
707
705
|
): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
708
|
-
const
|
|
709
|
-
const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(
|
|
706
|
+
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
707
|
+
const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex);
|
|
710
708
|
if (cachedStateCtx) {
|
|
711
709
|
const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root);
|
|
712
710
|
const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
@@ -1062,7 +1060,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1062
1060
|
body,
|
|
1063
1061
|
} as AssembledBlockType<T>;
|
|
1064
1062
|
|
|
1065
|
-
const {newStateRoot, proposerReward
|
|
1063
|
+
const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block);
|
|
1066
1064
|
block.stateRoot = newStateRoot;
|
|
1067
1065
|
const blockRoot =
|
|
1068
1066
|
produceResult.type === BlockType.Full
|
|
@@ -1071,26 +1069,9 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1071
1069
|
const blockRootHex = toRootHex(blockRoot);
|
|
1072
1070
|
|
|
1073
1071
|
const fork = this.config.getForkName(slot);
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
const gloasResult = produceResult as ProduceFullGloas;
|
|
1081
|
-
const envelope: gloas.ExecutionPayloadEnvelope = {
|
|
1082
|
-
payload: gloasResult.executionPayload,
|
|
1083
|
-
executionRequests: gloasResult.executionRequests,
|
|
1084
|
-
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
1085
|
-
beaconBlockRoot: blockRoot,
|
|
1086
|
-
slot,
|
|
1087
|
-
stateRoot: ZERO_HASH,
|
|
1088
|
-
};
|
|
1089
|
-
if (!isStatePostGloas(postBlockState)) {
|
|
1090
|
-
throw Error(`Expected gloas+ post-state for execution payload envelope, got fork=${postBlockState.forkName}`);
|
|
1091
|
-
}
|
|
1092
|
-
const payloadEnvelopeStateRoot = computePayloadEnvelopeStateRoot(this.metrics, postBlockState, envelope);
|
|
1093
|
-
gloasResult.payloadEnvelopeStateRoot = payloadEnvelopeStateRoot;
|
|
1072
|
+
// TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
|
|
1073
|
+
if (isForkPostGloas(fork) && produceResult.type !== BlockType.Full) {
|
|
1074
|
+
throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
|
|
1094
1075
|
}
|
|
1095
1076
|
|
|
1096
1077
|
// Track the produced block for consensus broadcast validations, later validation, etc.
|
|
@@ -1338,8 +1319,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1338
1319
|
checkpoint: CheckpointWithPayloadStatus,
|
|
1339
1320
|
blockState: IBeaconStateView
|
|
1340
1321
|
): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} {
|
|
1341
|
-
const
|
|
1342
|
-
const state = this.regen.getCheckpointStateSync(
|
|
1322
|
+
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
1323
|
+
const state = this.regen.getCheckpointStateSync(checkpointHex);
|
|
1343
1324
|
if (state) {
|
|
1344
1325
|
return {state, stateId: "checkpoint_state", shouldWarn: false};
|
|
1345
1326
|
}
|
package/src/chain/emitter.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
|
|
|
7
7
|
import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
|
|
8
8
|
import {PeerIdStr} from "../util/peerId.js";
|
|
9
9
|
import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
|
|
10
|
+
import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Important chain events that occur during normal chain operation.
|
|
@@ -76,6 +77,11 @@ export enum ChainEvent {
|
|
|
76
77
|
* cut-off window passes for waiting on gossip
|
|
77
78
|
*/
|
|
78
79
|
incompleteBlockInput = "incompleteBlockInput",
|
|
80
|
+
/**
|
|
81
|
+
* Post-gloas: trigger BlockInputSync for payload envelopes whose envelope and/or sampled columns are partially
|
|
82
|
+
* received via gossip but are not complete by time the cut-off window passes for waiting on gossip
|
|
83
|
+
*/
|
|
84
|
+
incompletePayloadEnvelope = "incompletePayloadEnvelope",
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
export type HeadEventData = routes.events.EventData[routes.events.EventType.head];
|
|
@@ -93,6 +99,11 @@ export type ChainEventData = {
|
|
|
93
99
|
};
|
|
94
100
|
[ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
|
|
95
101
|
[ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
|
|
102
|
+
[ChainEvent.incompletePayloadEnvelope]: {
|
|
103
|
+
payloadInput: PayloadEnvelopeInput;
|
|
104
|
+
peer: PeerIdStr;
|
|
105
|
+
source: BlockInputSource;
|
|
106
|
+
};
|
|
96
107
|
[ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
|
|
97
108
|
};
|
|
98
109
|
|
|
@@ -116,6 +127,7 @@ export type IChainEvents = ApiEvents & {
|
|
|
116
127
|
[ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
|
|
117
128
|
[ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
|
|
118
129
|
[ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
|
|
130
|
+
[ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
|
|
119
131
|
[ChainEvent.unknownEnvelopeBlockRoot]: (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]) => void;
|
|
120
132
|
};
|
|
121
133
|
|
|
@@ -147,6 +147,10 @@ export enum AttestationErrorCode {
|
|
|
147
147
|
* Gloas: Current slot attestation is marking payload as present
|
|
148
148
|
*/
|
|
149
149
|
PREMATURELY_INDICATED_PAYLOAD_PRESENT = "ATTESTATION_ERROR_PREMATURELY_INDICATED_PAYLOAD_PRESENT",
|
|
150
|
+
/**
|
|
151
|
+
* Gloas: index-1 attestation but the execution payload has not been seen yet
|
|
152
|
+
*/
|
|
153
|
+
EXECUTION_PAYLOAD_NOT_SEEN = "ATTESTATION_ERROR_EXECUTION_PAYLOAD_NOT_SEEN",
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
export type AttestationErrorType =
|
|
@@ -185,7 +189,8 @@ export type AttestationErrorType =
|
|
|
185
189
|
| {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX}
|
|
186
190
|
| {code: AttestationErrorCode.ATTESTER_NOT_IN_COMMITTEE}
|
|
187
191
|
| {code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE; attDataIndex: number}
|
|
188
|
-
| {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT}
|
|
192
|
+
| {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT}
|
|
193
|
+
| {code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN; beaconBlockRoot: RootHex};
|
|
189
194
|
|
|
190
195
|
export class AttestationError extends GossipActionError<AttestationErrorType> {
|
|
191
196
|
getMetadata(): Record<string, string | number | null> {
|
|
@@ -105,10 +105,14 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
105
105
|
const isForkPostGloas = computeEpochAtSlot(state.slot) >= config.GLOAS_FORK_EPOCH;
|
|
106
106
|
|
|
107
107
|
// Determine justified checkpoint payload status
|
|
108
|
-
const justifiedPayloadStatus =
|
|
108
|
+
const justifiedPayloadStatus = isForkPostGloas
|
|
109
|
+
? PayloadStatus.PENDING
|
|
110
|
+
: getCheckpointPayloadStatus(config, state, justifiedCheckpoint.epoch);
|
|
109
111
|
|
|
110
112
|
// Determine finalized checkpoint payload status
|
|
111
|
-
const finalizedPayloadStatus =
|
|
113
|
+
const finalizedPayloadStatus = isForkPostGloas
|
|
114
|
+
? PayloadStatus.PENDING
|
|
115
|
+
: getCheckpointPayloadStatus(config, state, finalizedCheckpoint.epoch);
|
|
112
116
|
|
|
113
117
|
return new forkchoiceConstructor(
|
|
114
118
|
config,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
3
|
+
import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
4
4
|
import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
|
|
5
5
|
import {
|
|
6
6
|
IBeaconStateView,
|
|
@@ -217,11 +217,7 @@ export class PrepareNextSlotScheduler {
|
|
|
217
217
|
// + if next slot is a skipped slot, it'd help getting target checkpoint state faster to validate attestations
|
|
218
218
|
if (isEpochTransition) {
|
|
219
219
|
this.metrics?.precomputeNextEpochTransition.count.inc({result: "success"}, 1);
|
|
220
|
-
|
|
221
|
-
// Pre-Gloas: payloadStatus is always FULL → payloadPresent = true
|
|
222
|
-
// Post-Gloas: FULL → true, EMPTY → false, PENDING → false (conservative, treat as block state)
|
|
223
|
-
const payloadPresent = headBlock.payloadStatus === PayloadStatus.FULL;
|
|
224
|
-
const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch, payloadPresent);
|
|
220
|
+
const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch);
|
|
225
221
|
if (previousHits === 0) {
|
|
226
222
|
this.metrics?.precomputeNextEpochTransition.waste.inc();
|
|
227
223
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataAvailabilityStatus,
|
|
3
3
|
ExecutionPayloadStatus,
|
|
4
|
-
G2_POINT_AT_INFINITY,
|
|
5
4
|
IBeaconStateView,
|
|
6
|
-
IBeaconStateViewGloas,
|
|
7
5
|
StateHashTreeRootSource,
|
|
8
6
|
} from "@lodestar/state-transition";
|
|
9
|
-
import {BeaconBlock, BlindedBeaconBlock, Gwei, Root
|
|
7
|
+
import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types";
|
|
10
8
|
import {ZERO_HASH} from "../../constants/index.js";
|
|
11
9
|
import {Metrics} from "../../metrics/index.js";
|
|
12
10
|
|
|
@@ -19,11 +17,11 @@ export function computeNewStateRoot(
|
|
|
19
17
|
metrics: Metrics | null,
|
|
20
18
|
state: IBeaconStateView,
|
|
21
19
|
block: BeaconBlock | BlindedBeaconBlock
|
|
22
|
-
): {newStateRoot: Root; proposerReward: Gwei;
|
|
20
|
+
): {newStateRoot: Root; proposerReward: Gwei; postState: IBeaconStateView} {
|
|
23
21
|
// Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
|
|
24
22
|
const blockEmptySig = {message: block, signature: ZERO_HASH};
|
|
25
23
|
|
|
26
|
-
const
|
|
24
|
+
const postState = state.stateTransition(
|
|
27
25
|
blockEmptySig,
|
|
28
26
|
{
|
|
29
27
|
// ExecutionPayloadStatus.valid: Assume payload valid, it has been produced by a trusted EL
|
|
@@ -42,49 +40,14 @@ export function computeNewStateRoot(
|
|
|
42
40
|
{metrics}
|
|
43
41
|
);
|
|
44
42
|
|
|
45
|
-
const {attestations, syncAggregate, slashing} =
|
|
43
|
+
const {attestations, syncAggregate, slashing} = postState.proposerRewards;
|
|
46
44
|
const proposerReward = BigInt(attestations + syncAggregate + slashing);
|
|
47
45
|
|
|
48
46
|
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
49
47
|
source: StateHashTreeRootSource.computeNewStateRoot,
|
|
50
48
|
});
|
|
51
|
-
const newStateRoot =
|
|
49
|
+
const newStateRoot = postState.hashTreeRoot();
|
|
52
50
|
hashTreeRootTimer?.();
|
|
53
51
|
|
|
54
|
-
return {newStateRoot, proposerReward,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Compute the state root after processing an execution payload envelope.
|
|
59
|
-
* Similar to `computeNewStateRoot` but for payload envelope processing.
|
|
60
|
-
*
|
|
61
|
-
*/
|
|
62
|
-
export function computePayloadEnvelopeStateRoot(
|
|
63
|
-
metrics: Metrics | null,
|
|
64
|
-
postBlockState: IBeaconStateViewGloas,
|
|
65
|
-
envelope: gloas.ExecutionPayloadEnvelope
|
|
66
|
-
): Root {
|
|
67
|
-
const signedEnvelope: gloas.SignedExecutionPayloadEnvelope = {
|
|
68
|
-
message: envelope,
|
|
69
|
-
signature: G2_POINT_AT_INFINITY,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
|
|
73
|
-
const postPayloadState = postBlockState.processExecutionPayloadEnvelope(signedEnvelope, {
|
|
74
|
-
// Signature is zero-ed (G2_POINT_AT_INFINITY), skip verification
|
|
75
|
-
verifySignature: false,
|
|
76
|
-
// State root is being computed here, the envelope doesn't have it yet
|
|
77
|
-
verifyStateRoot: false,
|
|
78
|
-
// Preserve cache in source state, since the resulting state is not added to the state cache
|
|
79
|
-
dontTransferCache: true,
|
|
80
|
-
});
|
|
81
|
-
processEnvelopeTimer?.();
|
|
82
|
-
|
|
83
|
-
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
84
|
-
source: StateHashTreeRootSource.computePayloadEnvelopeStateRoot,
|
|
85
|
-
});
|
|
86
|
-
const stateRoot = postPayloadState.hashTreeRoot();
|
|
87
|
-
hashTreeRootTimer?.();
|
|
88
|
-
|
|
89
|
-
return stateRoot;
|
|
52
|
+
return {newStateRoot, proposerReward, postState};
|
|
90
53
|
}
|
|
@@ -111,12 +111,6 @@ export type ProduceFullGloas = {
|
|
|
111
111
|
executionRequests: electra.ExecutionRequests;
|
|
112
112
|
blobsBundle: BlobsBundle<ForkPostGloas>;
|
|
113
113
|
cells: fulu.Cell[][];
|
|
114
|
-
/**
|
|
115
|
-
* Cached payload envelope state root computed during block production.
|
|
116
|
-
* This is the state root after running `processExecutionPayloadEnvelope` on the
|
|
117
|
-
* post-block state, and later used to construct the `ExecutionPayloadEnvelope`.
|
|
118
|
-
*/
|
|
119
|
-
payloadEnvelopeStateRoot: Root;
|
|
120
114
|
};
|
|
121
115
|
export type ProduceFullFulu = {
|
|
122
116
|
type: BlockType.Full;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
2
1
|
import {Root, RootHex, Slot} from "@lodestar/types";
|
|
3
2
|
|
|
4
3
|
export enum RegenErrorCode {
|
|
@@ -10,8 +9,6 @@ export enum RegenErrorCode {
|
|
|
10
9
|
BLOCK_NOT_IN_DB = "REGEN_ERROR_BLOCK_NOT_IN_DB",
|
|
11
10
|
STATE_TRANSITION_ERROR = "REGEN_ERROR_STATE_TRANSITION_ERROR",
|
|
12
11
|
INVALID_STATE_ROOT = "REGEN_ERROR_INVALID_STATE_ROOT",
|
|
13
|
-
UNEXPECTED_PAYLOAD_STATUS = "REGEN_ERROR_UNEXPECTED_PAYLOAD_STATUS",
|
|
14
|
-
INTERNAL_ERROR = "REGEN_ERROR_INTERNAL_ERROR",
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
export type RegenErrorType =
|
|
@@ -22,9 +19,7 @@ export type RegenErrorType =
|
|
|
22
19
|
| {code: RegenErrorCode.TOO_MANY_BLOCK_PROCESSED; stateRoot: RootHex | Root}
|
|
23
20
|
| {code: RegenErrorCode.BLOCK_NOT_IN_DB; blockRoot: RootHex | Root}
|
|
24
21
|
| {code: RegenErrorCode.STATE_TRANSITION_ERROR; error: Error}
|
|
25
|
-
| {code: RegenErrorCode.INVALID_STATE_ROOT; slot: Slot; expected: RootHex; actual: RootHex}
|
|
26
|
-
| {code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS; blockRoot: RootHex | Root; payloadStatus: PayloadStatus}
|
|
27
|
-
| {code: RegenErrorCode.INTERNAL_ERROR; message: string};
|
|
22
|
+
| {code: RegenErrorCode.INVALID_STATE_ROOT; slot: Slot; expected: RootHex; actual: RootHex};
|
|
28
23
|
|
|
29
24
|
export class RegenError extends Error {
|
|
30
25
|
type: RegenErrorType;
|
|
@@ -2,7 +2,7 @@ import {routes} from "@lodestar/api";
|
|
|
2
2
|
import {ProtoBlock} from "@lodestar/fork-choice";
|
|
3
3
|
import {IBeaconStateView} from "@lodestar/state-transition";
|
|
4
4
|
import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
|
|
5
|
-
import {
|
|
5
|
+
import {CheckpointHex} from "../stateCache/types.js";
|
|
6
6
|
|
|
7
7
|
export enum RegenCaller {
|
|
8
8
|
getDuties = "getDuties",
|
|
@@ -40,20 +40,15 @@ export interface IStateRegenerator extends IStateRegeneratorInternal {
|
|
|
40
40
|
dumpCacheSummary(): routes.lodestar.StateCacheItem[];
|
|
41
41
|
getStateSync(stateRoot: RootHex): IBeaconStateView | null;
|
|
42
42
|
getPreStateSync(block: BeaconBlock): IBeaconStateView | null;
|
|
43
|
-
getCheckpointStateOrBytes(cp:
|
|
44
|
-
getCheckpointStateSync(cp:
|
|
43
|
+
getCheckpointStateOrBytes(cp: CheckpointHex): Promise<IBeaconStateView | Uint8Array | null>;
|
|
44
|
+
getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null;
|
|
45
45
|
getClosestHeadState(head: ProtoBlock): IBeaconStateView | null;
|
|
46
46
|
pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void;
|
|
47
47
|
pruneOnFinalized(finalizedEpoch: Epoch): void;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* payloadPresent is true if this is payload state, false if block state.
|
|
52
|
-
* payloadPresent is always true for pre-gloas.
|
|
53
|
-
*/
|
|
54
|
-
addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, payloadPresent: boolean): void;
|
|
48
|
+
processState(blockRootHex: RootHex, postState: IBeaconStateView): void;
|
|
49
|
+
addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void;
|
|
55
50
|
updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void;
|
|
56
|
-
updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch
|
|
51
|
+
updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null;
|
|
57
52
|
}
|
|
58
53
|
|
|
59
54
|
/**
|
|
@@ -5,7 +5,7 @@ import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lo
|
|
|
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";
|
|
8
|
-
import {BlockStateCache,
|
|
8
|
+
import {BlockStateCache, CheckpointHex, CheckpointStateCache} from "../stateCache/types.js";
|
|
9
9
|
import {RegenError, RegenErrorCode} from "./errors.js";
|
|
10
10
|
import {
|
|
11
11
|
IStateRegenerator,
|
|
@@ -125,14 +125,14 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
125
125
|
return null;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
async getCheckpointStateOrBytes(cp:
|
|
128
|
+
async getCheckpointStateOrBytes(cp: CheckpointHex): Promise<IBeaconStateView | Uint8Array | null> {
|
|
129
129
|
return this.checkpointStateCache.getStateOrBytes(cp);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* Get checkpoint state from cache
|
|
134
134
|
*/
|
|
135
|
-
getCheckpointStateSync(cp:
|
|
135
|
+
getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null {
|
|
136
136
|
return this.checkpointStateCache.get(cp);
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -153,22 +153,14 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
153
153
|
this.blockStateCache.deleteAllBeforeEpoch(finalizedEpoch);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
processState(blockRootHex: RootHex, postState: IBeaconStateView): void {
|
|
157
157
|
this.blockStateCache.add(postState);
|
|
158
158
|
this.checkpointStateCache.processState(blockRootHex, postState).catch((e) => {
|
|
159
159
|
this.logger.debug("Error processing block state", {blockRootHex, slot: postState.slot}, e);
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
* Process payload state for caching after importing execution payload.
|
|
165
|
-
*/
|
|
166
|
-
processPayloadState(payloadState: IBeaconStateView): void {
|
|
167
|
-
// Add payload state to block state cache (keyed by payload state root)
|
|
168
|
-
this.blockStateCache.add(payloadState);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, _payloadPresent: boolean): void {
|
|
163
|
+
addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void {
|
|
172
164
|
this.checkpointStateCache.add(cp, item);
|
|
173
165
|
}
|
|
174
166
|
|
|
@@ -205,7 +197,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
205
197
|
}
|
|
206
198
|
}
|
|
207
199
|
|
|
208
|
-
updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch
|
|
200
|
+
updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null {
|
|
209
201
|
return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch);
|
|
210
202
|
}
|
|
211
203
|
|
package/src/chain/regen/regen.ts
CHANGED
|
@@ -332,11 +332,6 @@ async function processSlotsByCheckpoint(
|
|
|
332
332
|
* emitting "checkpoint" events after every epoch processed.
|
|
333
333
|
*
|
|
334
334
|
* Stops processing after no more full epochs can be processed.
|
|
335
|
-
*
|
|
336
|
-
* Output state variant:
|
|
337
|
-
* - Post-Gloas: If slots are processed, returns block state (payloadPresent=false).
|
|
338
|
-
* If no slots processed, returns preState as-is (preserves variant).
|
|
339
|
-
* - Pre-Gloas: Always payloadPresent=true (no block/payload distinction).
|
|
340
335
|
*/
|
|
341
336
|
export async function processSlotsToNearestCheckpoint(
|
|
342
337
|
modules: {
|
|
@@ -380,9 +375,6 @@ export async function processSlotsToNearestCheckpoint(
|
|
|
380
375
|
// This may becomes the "official" checkpoint state if the 1st block of epoch is skipped
|
|
381
376
|
const checkpointState = postState;
|
|
382
377
|
const cp = getCheckpointFromState(checkpointState);
|
|
383
|
-
// processSlots() only does epoch transitions, never processes payloads
|
|
384
|
-
// Pre-Gloas: payloadPresent is always true (execution payload embedded in block)
|
|
385
|
-
// Post-Gloas: result is a block state (payloadPresent=false)
|
|
386
378
|
checkpointStateCache.add(cp, checkpointState);
|
|
387
379
|
// consumers should not mutate state ever
|
|
388
380
|
emitter?.emit(ChainEvent.checkpoint, cp, checkpointState);
|
|
@@ -9,7 +9,7 @@ import {IClock} from "../../util/clock.js";
|
|
|
9
9
|
import {serializeState} from "../serializeState.js";
|
|
10
10
|
import {CPStateDatastore, DatastoreKey} from "./datastore/index.js";
|
|
11
11
|
import {MapTracker} from "./mapMetrics.js";
|
|
12
|
-
import {BlockStateCache, CacheItemType, CheckpointHex,
|
|
12
|
+
import {BlockStateCache, CacheItemType, CheckpointHex, CheckpointStateCache} from "./types.js";
|
|
13
13
|
|
|
14
14
|
export type PersistentCheckpointStateCacheOpts = {
|
|
15
15
|
/** Keep max n state epochs in memory, persist the rest to disk */
|
|
@@ -843,19 +843,6 @@ export function toCheckpointHex(checkpoint: phase0.Checkpoint): CheckpointHex {
|
|
|
843
843
|
};
|
|
844
844
|
}
|
|
845
845
|
|
|
846
|
-
/** TODO GLOAS: remove after rolling back regen dual-state changes */
|
|
847
|
-
export function fcCheckpointToHexPayload(checkpoint: {
|
|
848
|
-
epoch: Epoch;
|
|
849
|
-
rootHex: RootHex;
|
|
850
|
-
payloadStatus?: number;
|
|
851
|
-
}): CheckpointHexPayload {
|
|
852
|
-
return {
|
|
853
|
-
epoch: checkpoint.epoch,
|
|
854
|
-
rootHex: checkpoint.rootHex,
|
|
855
|
-
payloadPresent: true,
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
|
|
859
846
|
export function toCheckpointKey(cp: CheckpointHex): string {
|
|
860
847
|
return `${cp.rootHex}:${cp.epoch}`;
|
|
861
848
|
}
|
|
@@ -4,9 +4,6 @@ import {Epoch, RootHex, phase0} from "@lodestar/types";
|
|
|
4
4
|
|
|
5
5
|
export type CheckpointHex = {epoch: Epoch; rootHex: RootHex};
|
|
6
6
|
|
|
7
|
-
/** TODO GLOAS: payloadPresent is ignored — remove after rolling back regen dual-state changes */
|
|
8
|
-
export type CheckpointHexPayload = {epoch: Epoch; rootHex: RootHex; payloadPresent: boolean};
|
|
9
|
-
|
|
10
7
|
/**
|
|
11
8
|
* Lodestar currently keeps two state caches around.
|
|
12
9
|
*
|
|
@@ -90,6 +90,19 @@ async function validateAggregateAndProof(
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// [REJECT] If `aggregate.data.index == 1` (payload present for a past
|
|
94
|
+
// block), the execution payload for `block` passes validation.
|
|
95
|
+
// [IGNORE] When `aggregate.data.index == 1` (payload present for a past block),
|
|
96
|
+
// the corresponding execution payload for `block` has been seen (a client MAY queue
|
|
97
|
+
// attestations for processing once the payload is retrieved and SHOULD request the
|
|
98
|
+
// payload envelope via `ExecutionPayloadEnvelopesByRoot`).
|
|
99
|
+
if (block !== null && attData.index === 1 && !chain.seenPayloadEnvelope(toRootHex(attData.beaconBlockRoot))) {
|
|
100
|
+
throw new AttestationError(GossipAction.IGNORE, {
|
|
101
|
+
code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN,
|
|
102
|
+
beaconBlockRoot: toRootHex(attData.beaconBlockRoot),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
// [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate)
|
|
94
107
|
committeeIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
|
|
95
108
|
if (committeeIndex === null) {
|
|
@@ -315,6 +315,19 @@ async function validateAttestationNoSignatureCheck(
|
|
|
315
315
|
code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT,
|
|
316
316
|
});
|
|
317
317
|
}
|
|
318
|
+
|
|
319
|
+
// [REJECT] If `attestation.data.index == 1` (payload present for a past
|
|
320
|
+
// block), the execution payload for `block` passes validation.
|
|
321
|
+
// [IGNORE] When `attestation.data.index == 1` (payload present for a past block),
|
|
322
|
+
// the corresponding execution payload for `block` has been seen (a client MAY queue
|
|
323
|
+
// attestations for processing once the payload is retrieved and SHOULD request the
|
|
324
|
+
// payload envelope via `ExecutionPayloadEnvelopesByRoot`).
|
|
325
|
+
if (block !== null && attData.index === 1 && !chain.seenPayloadEnvelope(toRootHex(attData.beaconBlockRoot))) {
|
|
326
|
+
throw new AttestationError(GossipAction.IGNORE, {
|
|
327
|
+
code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN,
|
|
328
|
+
beaconBlockRoot: toRootHex(attData.beaconBlockRoot),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
318
331
|
} else {
|
|
319
332
|
// [REJECT] attestation.data.index == 0
|
|
320
333
|
if (attData.index !== 0) {
|
|
@@ -32,7 +32,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
32
32
|
const {payload} = envelope;
|
|
33
33
|
const blockRootHex = toRootHex(envelope.beaconBlockRoot);
|
|
34
34
|
|
|
35
|
-
// [IGNORE] The envelope's block root `envelope.
|
|
35
|
+
// [IGNORE] The envelope's block root `envelope.beacon_block_root` has been seen (via
|
|
36
36
|
// gossip or non-gossip sources) (a client MAY queue payload for processing once
|
|
37
37
|
// the block is retrieved).
|
|
38
38
|
// TODO GLOAS: Need to review this, we should queue the envelope for later
|
|
@@ -107,7 +107,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
// Get the
|
|
110
|
+
// Get the block state to verify the builder's signature.
|
|
111
111
|
const blockState = await chain.regen
|
|
112
112
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
113
113
|
.catch(() => {
|
|
@@ -745,13 +745,29 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
745
745
|
});
|
|
746
746
|
}
|
|
747
747
|
|
|
748
|
-
chain.processExecutionPayload
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
);
|
|
754
|
-
|
|
748
|
+
// NOTE: we do NOT call chain.processExecutionPayload here. That is triggered only by
|
|
749
|
+
// envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
|
|
750
|
+
// payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
|
|
751
|
+
|
|
752
|
+
if (!payloadInput.isComplete()) {
|
|
753
|
+
const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
|
|
754
|
+
// do not await here to not delay gossip validation
|
|
755
|
+
payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch((_e) => {
|
|
756
|
+
chain.logger.debug(
|
|
757
|
+
"Waited for envelope and data after receiving gossip column. Cut-off reached so emitting incompletePayloadEnvelope",
|
|
758
|
+
{
|
|
759
|
+
dataColumnIndex: index,
|
|
760
|
+
...payloadInputMeta,
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
// TODO GLOAS: UnknownBlockSync to handle this event
|
|
764
|
+
chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
|
|
765
|
+
payloadInput,
|
|
766
|
+
peer: peerIdStr,
|
|
767
|
+
source: BlockInputSource.gossip,
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
}
|
|
755
771
|
} else {
|
|
756
772
|
if (config.getForkSeq(dataColumnSlot) < ForkSeq.fulu) {
|
|
757
773
|
throw new GossipActionError(GossipAction.REJECT, {code: "PRE_FULU_BLOCK"});
|