@lodestar/beacon-node 1.43.0-dev.a142c56215 → 1.43.0-dev.a6d8600051
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.js +3 -3
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +45 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +66 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +1 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +5 -3
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +23 -10
- 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 +7 -5
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +4 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +2 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +26 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +5 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/index.d.ts +1 -0
- package/lib/chain/errors/index.d.ts.map +1 -1
- package/lib/chain/errors/index.js +1 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +33 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/errors/proposerPreferences.js +13 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -0
- package/lib/chain/interface.d.ts +2 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +26 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +15 -17
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +38 -21
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +1 -0
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +1 -0
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +15 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
- package/lib/chain/seenCache/seenProposerPreferences.js +25 -0
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
- package/lib/chain/validation/proposerPreferences.d.ts +8 -0
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/validation/proposerPreferences.js +69 -0
- package/lib/chain/validation/proposerPreferences.js.map +1 -0
- package/lib/network/gossip/interface.d.ts +7 -1
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/interface.js +1 -0
- package/lib/network/gossip/interface.js.map +1 -1
- package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
- package/lib/network/gossip/scoringParameters.js +12 -1
- package/lib/network/gossip/scoringParameters.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +27 -766
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +6 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +5 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +12 -8
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +5 -0
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +1 -0
- package/lib/network/processor/index.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +3 -3
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/validator/index.ts +80 -0
- package/src/chain/blocks/importBlock.ts +0 -1
- package/src/chain/blocks/importExecutionPayload.ts +31 -10
- package/src/chain/blocks/index.ts +14 -6
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +5 -1
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
- package/src/chain/blocks/verifyBlock.ts +39 -9
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +5 -0
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/proposerPreferences.ts +39 -0
- package/src/chain/interface.ts +2 -0
- package/src/chain/opPools/payloadAttestationPool.ts +29 -8
- package/src/chain/prepareNextSlot.ts +20 -28
- package/src/chain/produceBlock/produceBlockBody.ts +48 -26
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +13 -3
- package/src/chain/seenCache/seenProposerPreferences.ts +29 -0
- package/src/chain/validation/proposerPreferences.ts +91 -0
- package/src/network/gossip/interface.ts +6 -0
- package/src/network/gossip/scoringParameters.ts +14 -1
- package/src/network/gossip/topic.ts +6 -0
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +19 -9
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +1 -0
|
@@ -41,7 +41,8 @@ export async function verifyBlocksInEpoch(
|
|
|
41
41
|
postStates: IBeaconStateView[];
|
|
42
42
|
proposerBalanceDeltas: number[];
|
|
43
43
|
segmentExecStatus: SegmentExecStatus;
|
|
44
|
-
|
|
44
|
+
blockDAStatuses: DataAvailabilityStatus[];
|
|
45
|
+
payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
|
|
45
46
|
indexedAttestationsByBlock: IndexedAttestation[][];
|
|
46
47
|
}> {
|
|
47
48
|
const blocks = blockInputs.map((blockInput) => blockInput.getBlock());
|
|
@@ -116,8 +117,12 @@ export async function verifyBlocksInEpoch(
|
|
|
116
117
|
|
|
117
118
|
// Pick the data-availability source by fork:
|
|
118
119
|
// - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
|
|
119
|
-
// - Post-Gloas: verifyPayloadsDataAvailability
|
|
120
|
-
const daAvailabilityPromise
|
|
120
|
+
// - Post-Gloas: verifyPayloadsDataAvailability (payload-level DA, keyed by slot).
|
|
121
|
+
const daAvailabilityPromise: Promise<{
|
|
122
|
+
blockDAStatuses: DataAvailabilityStatus[];
|
|
123
|
+
payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
|
|
124
|
+
availableTime: number;
|
|
125
|
+
}> =
|
|
121
126
|
fork >= ForkSeq.gloas
|
|
122
127
|
? (async () => {
|
|
123
128
|
const payloadInputsForDa: PayloadEnvelopeInput[] = [];
|
|
@@ -125,19 +130,37 @@ export async function verifyBlocksInEpoch(
|
|
|
125
130
|
const pi = payloadEnvelopes?.get(input.slot);
|
|
126
131
|
if (pi !== undefined) payloadInputsForDa.push(pi);
|
|
127
132
|
}
|
|
128
|
-
await verifyPayloadsDataAvailability(
|
|
133
|
+
const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
|
|
134
|
+
payloadInputsForDa,
|
|
135
|
+
abortController.signal
|
|
136
|
+
);
|
|
137
|
+
const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
|
|
138
|
+
for (let i = 0; i < payloadInputsForDa.length; i++) {
|
|
139
|
+
payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
|
|
140
|
+
}
|
|
129
141
|
return {
|
|
130
142
|
// post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
blockDAStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
|
|
144
|
+
payloadDAStatuses,
|
|
145
|
+
availableTime,
|
|
133
146
|
};
|
|
134
147
|
})()
|
|
135
|
-
:
|
|
148
|
+
: (async () => {
|
|
149
|
+
const {dataAvailabilityStatuses, availableTime} = await verifyBlocksDataAvailability(
|
|
150
|
+
blockInputs,
|
|
151
|
+
abortController.signal
|
|
152
|
+
);
|
|
153
|
+
return {
|
|
154
|
+
blockDAStatuses: dataAvailabilityStatuses,
|
|
155
|
+
payloadDAStatuses: new Map<Slot, DataAvailabilityStatus>(),
|
|
156
|
+
availableTime,
|
|
157
|
+
};
|
|
158
|
+
})();
|
|
136
159
|
|
|
137
160
|
// batch all I/O operations to reduce overhead
|
|
138
161
|
const [
|
|
139
162
|
segmentExecStatus,
|
|
140
|
-
{
|
|
163
|
+
{blockDAStatuses, payloadDAStatuses, availableTime},
|
|
141
164
|
{postStates, proposerBalanceDeltas, verifyStateTime},
|
|
142
165
|
{verifySignaturesTime},
|
|
143
166
|
] = await Promise.all([
|
|
@@ -258,7 +281,14 @@ export async function verifyBlocksInEpoch(
|
|
|
258
281
|
);
|
|
259
282
|
}
|
|
260
283
|
|
|
261
|
-
return {
|
|
284
|
+
return {
|
|
285
|
+
postStates,
|
|
286
|
+
blockDAStatuses,
|
|
287
|
+
payloadDAStatuses,
|
|
288
|
+
proposerBalanceDeltas,
|
|
289
|
+
segmentExecStatus,
|
|
290
|
+
indexedAttestationsByBlock,
|
|
291
|
+
};
|
|
262
292
|
} finally {
|
|
263
293
|
abortController.abort();
|
|
264
294
|
}
|
|
@@ -32,8 +32,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
32
32
|
const payload = envelope.payload;
|
|
33
33
|
|
|
34
34
|
// Verify consistency with the beacon block.
|
|
35
|
-
// Compute header root on a
|
|
36
|
-
const headerValue =
|
|
35
|
+
// Compute header root on a clone of latestBlockHeader to avoid mutating state.
|
|
36
|
+
const headerValue = ssz.phase0.BeaconBlockHeader.clone(state.latestBlockHeader);
|
|
37
37
|
if (byteArrayEquals(headerValue.stateRoot, ssz.Root.defaultValue())) {
|
|
38
38
|
headerValue.stateRoot = state.hashTreeRoot();
|
|
39
39
|
}
|
|
@@ -28,11 +28,14 @@ export async function verifyPayloadsDataAvailability(
|
|
|
28
28
|
await Promise.all(promises);
|
|
29
29
|
|
|
30
30
|
const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
|
|
31
|
-
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
|
|
32
|
-
payloadInput.
|
|
31
|
+
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) => {
|
|
32
|
+
if (payloadInput.daOutOfRange) {
|
|
33
|
+
return DataAvailabilityStatus.OutOfRange;
|
|
34
|
+
}
|
|
35
|
+
return payloadInput.getBlobKzgCommitments().length === 0
|
|
33
36
|
? DataAvailabilityStatus.NotRequired
|
|
34
|
-
: DataAvailabilityStatus.Available
|
|
35
|
-
);
|
|
37
|
+
: DataAvailabilityStatus.Available;
|
|
38
|
+
});
|
|
36
39
|
|
|
37
40
|
return {dataAvailabilityStatuses, availableTime};
|
|
38
41
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -106,6 +106,7 @@ import {
|
|
|
106
106
|
SeenExecutionPayloadBids,
|
|
107
107
|
SeenPayloadAttesters,
|
|
108
108
|
SeenPayloadEnvelopeInput,
|
|
109
|
+
SeenProposerPreferences,
|
|
109
110
|
SeenSyncCommitteeMessages,
|
|
110
111
|
} from "./seenCache/index.js";
|
|
111
112
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -186,6 +187,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
186
187
|
readonly seenPayloadAttesters = new SeenPayloadAttesters();
|
|
187
188
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
188
189
|
readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
|
|
190
|
+
readonly seenProposerPreferences = new SeenProposerPreferences();
|
|
189
191
|
readonly seenBlockProposers = new SeenBlockProposers();
|
|
190
192
|
readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
|
|
191
193
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -334,6 +336,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
334
336
|
logger,
|
|
335
337
|
});
|
|
336
338
|
this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
|
|
339
|
+
config,
|
|
340
|
+
clock,
|
|
337
341
|
chainEvents: emitter,
|
|
338
342
|
signal,
|
|
339
343
|
serializedCache: this.serializedCache,
|
|
@@ -1437,6 +1441,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1437
1441
|
this.payloadAttestationPool.prune(slot);
|
|
1438
1442
|
this.executionPayloadBidPool.prune(slot);
|
|
1439
1443
|
this.seenExecutionPayloadBids.prune(slot);
|
|
1444
|
+
this.seenProposerPreferences.prune(slot);
|
|
1440
1445
|
this.seenAttestationDatas.onSlot(slot);
|
|
1441
1446
|
this.reprocessController.onSlot(slot);
|
|
1442
1447
|
|
|
@@ -8,6 +8,7 @@ export * from "./executionPayloadBid.js";
|
|
|
8
8
|
export * from "./executionPayloadEnvelope.js";
|
|
9
9
|
export * from "./gossipValidation.js";
|
|
10
10
|
export * from "./payloadAttestation.js";
|
|
11
|
+
export * from "./proposerPreferences.js";
|
|
11
12
|
export * from "./proposerSlashingError.js";
|
|
12
13
|
export * from "./syncCommitteeError.js";
|
|
13
14
|
export * from "./voluntaryExitError.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
+
import {GossipActionError} from "./gossipValidation.js";
|
|
3
|
+
|
|
4
|
+
export enum ProposerPreferencesErrorCode {
|
|
5
|
+
INVALID_EPOCH = "PROPOSER_PREFERENCES_ERROR_INVALID_EPOCH",
|
|
6
|
+
PROPOSAL_SLOT_PASSED = "PROPOSER_PREFERENCES_ERROR_PROPOSAL_SLOT_PASSED",
|
|
7
|
+
INVALID_PROPOSER = "PROPOSER_PREFERENCES_ERROR_INVALID_PROPOSER",
|
|
8
|
+
ALREADY_KNOWN = "PROPOSER_PREFERENCES_ERROR_ALREADY_KNOWN",
|
|
9
|
+
INVALID_SIGNATURE = "PROPOSER_PREFERENCES_ERROR_INVALID_SIGNATURE",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ProposerPreferencesErrorType =
|
|
13
|
+
| {
|
|
14
|
+
code: ProposerPreferencesErrorCode.INVALID_EPOCH;
|
|
15
|
+
proposalSlot: Slot;
|
|
16
|
+
currentEpoch: number;
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED;
|
|
20
|
+
proposalSlot: Slot;
|
|
21
|
+
currentSlot: Slot;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
code: ProposerPreferencesErrorCode.INVALID_PROPOSER;
|
|
25
|
+
proposalSlot: Slot;
|
|
26
|
+
validatorIndex: ValidatorIndex;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
code: ProposerPreferencesErrorCode.ALREADY_KNOWN;
|
|
30
|
+
proposalSlot: Slot;
|
|
31
|
+
validatorIndex: ValidatorIndex;
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
code: ProposerPreferencesErrorCode.INVALID_SIGNATURE;
|
|
35
|
+
proposalSlot: Slot;
|
|
36
|
+
validatorIndex: ValidatorIndex;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class ProposerPreferencesError extends GossipActionError<ProposerPreferencesErrorType> {}
|
package/src/chain/interface.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
SeenContributionAndProof,
|
|
62
62
|
SeenExecutionPayloadBids,
|
|
63
63
|
SeenPayloadAttesters,
|
|
64
|
+
SeenProposerPreferences,
|
|
64
65
|
SeenSyncCommitteeMessages,
|
|
65
66
|
} from "./seenCache/index.js";
|
|
66
67
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -131,6 +132,7 @@ export interface IBeaconChain {
|
|
|
131
132
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
132
133
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
133
134
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
135
|
+
readonly seenProposerPreferences: SeenProposerPreferences;
|
|
134
136
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
135
137
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
136
138
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Signature, aggregateSignatures} from "@chainsafe/blst";
|
|
2
2
|
import {BitArray} from "@chainsafe/ssz";
|
|
3
3
|
import {ChainForkConfig} from "@lodestar/config";
|
|
4
|
-
import {MAX_COMMITTEES_PER_SLOT, PTC_SIZE} from "@lodestar/params";
|
|
4
|
+
import {MAX_COMMITTEES_PER_SLOT, MAX_PAYLOAD_ATTESTATIONS, PTC_SIZE} from "@lodestar/params";
|
|
5
5
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
6
6
|
import {MapDef, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {Metrics} from "../../metrics/metrics.js";
|
|
@@ -95,13 +95,9 @@ export class PayloadAttestationPool {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Get payload attestations to be included in a block.
|
|
98
|
-
* Pick the top `
|
|
98
|
+
* Pick the top `MAX_PAYLOAD_ATTESTATIONS` aggregates with the most votes.
|
|
99
99
|
*/
|
|
100
|
-
getPayloadAttestationsForBlock(
|
|
101
|
-
beaconBlockRoot: BlockRootHex,
|
|
102
|
-
slot: Slot,
|
|
103
|
-
maxAttestation: number
|
|
104
|
-
): gloas.PayloadAttestation[] {
|
|
100
|
+
getPayloadAttestationsForBlock(beaconBlockRoot: BlockRootHex, slot: Slot): gloas.PayloadAttestation[] {
|
|
105
101
|
const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
|
|
106
102
|
|
|
107
103
|
if (!aggregateByDataRootByBlockRoot) {
|
|
@@ -119,7 +115,32 @@ export class PayloadAttestationPool {
|
|
|
119
115
|
return Array.from(aggregateByDataRoot.values())
|
|
120
116
|
.slice()
|
|
121
117
|
.sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
|
|
122
|
-
.slice(0,
|
|
118
|
+
.slice(0, MAX_PAYLOAD_ATTESTATIONS)
|
|
119
|
+
.map(fastToPayloadAttestation);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getAll(slot?: Slot): gloas.PayloadAttestation[] {
|
|
123
|
+
const aggregates: AggregateFast[] = [];
|
|
124
|
+
|
|
125
|
+
const addAggregates = (aggregateByDataRootByBlockRoot: Map<BlockRootHex, Map<DataRootHex, AggregateFast>>) => {
|
|
126
|
+
for (const aggregateByDataRoot of aggregateByDataRootByBlockRoot.values()) {
|
|
127
|
+
aggregates.push(...aggregateByDataRoot.values());
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (slot !== undefined) {
|
|
132
|
+
const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
|
|
133
|
+
if (aggregateByDataRootByBlockRoot) {
|
|
134
|
+
addAggregates(aggregateByDataRootByBlockRoot);
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
for (const aggregateByDataRootByBlockRoot of this.aggregateByDataRootByBlockRootBySlot.values()) {
|
|
138
|
+
addAggregates(aggregateByDataRootByBlockRoot);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return aggregates
|
|
143
|
+
.sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
|
|
123
144
|
.map(fastToPayloadAttestation);
|
|
124
145
|
}
|
|
125
146
|
|
|
@@ -4,13 +4,14 @@ import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
|
4
4
|
import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
|
|
5
5
|
import {
|
|
6
6
|
IBeaconStateView,
|
|
7
|
+
IBeaconStateViewBellatrix,
|
|
7
8
|
StateHashTreeRootSource,
|
|
8
9
|
computeEpochAtSlot,
|
|
9
10
|
computeTimeAtSlot,
|
|
10
11
|
isStatePostBellatrix,
|
|
11
12
|
isStatePostGloas,
|
|
12
13
|
} from "@lodestar/state-transition";
|
|
13
|
-
import {Bytes32, Slot
|
|
14
|
+
import {Bytes32, Slot} from "@lodestar/types";
|
|
14
15
|
import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
|
|
15
16
|
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
|
|
16
17
|
import {BuilderStatus} from "../execution/builder/http.js";
|
|
@@ -165,19 +166,26 @@ export class PrepareNextSlotScheduler {
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
let parentBlockHash: Bytes32;
|
|
168
|
-
|
|
169
|
+
// Apply parent payload once here as it's reused by EL prep and SSE emit below
|
|
170
|
+
let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
|
|
169
171
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
if (this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot)) {
|
|
173
|
+
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
|
|
174
|
+
// Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
|
|
175
|
+
if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
|
|
176
|
+
const parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
177
|
+
updatedHead.slot,
|
|
178
|
+
updatedHead.blockRoot
|
|
179
|
+
);
|
|
180
|
+
stateAfterParentPayload = updatedPrepareState.withParentPayloadApplied(parentExecutionRequests);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
|
|
184
|
+
}
|
|
174
185
|
} else {
|
|
175
186
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
|
|
176
187
|
}
|
|
177
188
|
|
|
178
|
-
// Reused by the SSE emit below to avoid a second DB lookup on cache miss
|
|
179
|
-
let parentExecutionRequests: electra.ExecutionRequests | undefined;
|
|
180
|
-
|
|
181
189
|
if (feeRecipient) {
|
|
182
190
|
const preparationTime =
|
|
183
191
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
@@ -187,13 +195,6 @@ export class PrepareNextSlotScheduler {
|
|
|
187
195
|
const finalizedBlockHash =
|
|
188
196
|
this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
189
197
|
|
|
190
|
-
if (isExtendingPayload) {
|
|
191
|
-
parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
192
|
-
updatedHead.slot,
|
|
193
|
-
updatedHead.blockRoot
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
198
|
// awaiting here instead of throwing an async call because there is no other task
|
|
198
199
|
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
199
200
|
// try/catch wrapper here.
|
|
@@ -205,9 +206,8 @@ export class PrepareNextSlotScheduler {
|
|
|
205
206
|
parentBlockHash,
|
|
206
207
|
safeBlockHash,
|
|
207
208
|
finalizedBlockHash,
|
|
208
|
-
|
|
209
|
-
feeRecipient
|
|
210
|
-
parentExecutionRequests
|
|
209
|
+
stateAfterParentPayload,
|
|
210
|
+
feeRecipient
|
|
211
211
|
);
|
|
212
212
|
this.logger.verbose("PrepareNextSlotScheduler prepared new payload", {
|
|
213
213
|
prepareSlot,
|
|
@@ -234,20 +234,12 @@ export class PrepareNextSlotScheduler {
|
|
|
234
234
|
(feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
|
|
235
235
|
this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
|
|
236
236
|
) {
|
|
237
|
-
// if we didn't fetch above (not proposing), SSE still needs it here
|
|
238
|
-
if (!parentExecutionRequests && isExtendingPayload) {
|
|
239
|
-
parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
240
|
-
updatedHead.slot,
|
|
241
|
-
updatedHead.blockRoot
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
237
|
const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
|
|
245
|
-
prepareState:
|
|
238
|
+
prepareState: stateAfterParentPayload,
|
|
246
239
|
prepareSlot,
|
|
247
240
|
parentBlockRoot: fromHex(updatedHead.blockRoot),
|
|
248
241
|
parentBlockHash,
|
|
249
242
|
feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
250
|
-
parentExecutionRequests,
|
|
251
243
|
});
|
|
252
244
|
this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
|
|
253
245
|
}
|
|
@@ -214,19 +214,25 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
// Get execution payload from EL
|
|
217
|
+
let parentBlockHash: Bytes32;
|
|
218
|
+
let parentExecutionRequests: electra.ExecutionRequests;
|
|
219
|
+
// Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
|
|
220
|
+
let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
|
|
217
221
|
const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
|
|
218
|
-
|
|
219
|
-
? currentState.latestExecutionPayloadBid.blockHash
|
|
220
|
-
: currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
221
|
-
// At gloas genesis the committed bid has no prior EL block to reference
|
|
222
|
-
// (`bid.parentBlockHash` is zero). Fall back to `bid.blockHash` (= eth1 genesis hash) so the
|
|
223
|
-
// FCU to the EL carries a valid head. Post-genesis bids always reference a non-zero parent.
|
|
224
|
-
if (isStatePostGloas(currentState) && byteArrayEquals(parentBlockHash, ZERO_HASH)) {
|
|
222
|
+
if (isExtendingPayload) {
|
|
225
223
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
224
|
+
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
225
|
+
stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
|
|
226
|
+
} else {
|
|
227
|
+
parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
228
|
+
// At gloas genesis the committed bid has no prior EL block to reference
|
|
229
|
+
// (`bid.parentBlockHash` is zero). Fall back to `bid.blockHash` (= eth1 genesis hash) so the
|
|
230
|
+
// FCU to the EL carries a valid head. Post-genesis bids always reference a non-zero parent.
|
|
231
|
+
if (byteArrayEquals(parentBlockHash, ZERO_HASH)) {
|
|
232
|
+
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
233
|
+
}
|
|
234
|
+
parentExecutionRequests = ssz.electra.ExecutionRequests.defaultValue();
|
|
226
235
|
}
|
|
227
|
-
const parentExecutionRequests = isExtendingPayload
|
|
228
|
-
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
229
|
-
: ssz.electra.ExecutionRequests.defaultValue();
|
|
230
236
|
const prepareRes = await prepareExecutionPayload(
|
|
231
237
|
this,
|
|
232
238
|
this.logger,
|
|
@@ -235,9 +241,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
235
241
|
parentBlockHash,
|
|
236
242
|
safeBlockHash,
|
|
237
243
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
238
|
-
|
|
239
|
-
feeRecipient
|
|
240
|
-
parentExecutionRequests
|
|
244
|
+
stateAfterParentPayload,
|
|
245
|
+
feeRecipient
|
|
241
246
|
);
|
|
242
247
|
|
|
243
248
|
const {prepType, payloadId} = prepareRes;
|
|
@@ -291,9 +296,19 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
291
296
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
292
297
|
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
293
298
|
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
300
|
+
parentBlock.blockRoot,
|
|
301
|
+
blockSlot - 1
|
|
302
|
+
);
|
|
296
303
|
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
304
|
+
// Drop voluntary exits that parent_execution_requests have invalidated (e.g. a withdrawal
|
|
305
|
+
// request initiating an exit on the same validator). Op pool selected against the unapplied
|
|
306
|
+
// state, so re-validate against the post-apply state to avoid producing an invalid block.
|
|
307
|
+
if (isExtendingPayload && commonBlockBody.voluntaryExits.length > 0) {
|
|
308
|
+
gloasBody.voluntaryExits = commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
|
|
309
|
+
stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
310
|
+
);
|
|
311
|
+
}
|
|
297
312
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
298
313
|
|
|
299
314
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -629,9 +644,12 @@ export async function prepareExecutionPayload(
|
|
|
629
644
|
parentBlockHash: Bytes32,
|
|
630
645
|
safeBlockHash: RootHex,
|
|
631
646
|
finalizedBlockHash: RootHex,
|
|
647
|
+
/**
|
|
648
|
+
* Post-gloas, when extending a full parent, callers must apply
|
|
649
|
+
* parent execution payload first (see `withParentPayloadApplied`).
|
|
650
|
+
*/
|
|
632
651
|
state: IBeaconStateViewBellatrix,
|
|
633
|
-
suggestedFeeRecipient: string
|
|
634
|
-
parentExecutionRequests?: electra.ExecutionRequests
|
|
652
|
+
suggestedFeeRecipient: string
|
|
635
653
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
636
654
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
637
655
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
@@ -668,7 +686,6 @@ export async function prepareExecutionPayload(
|
|
|
668
686
|
parentBlockRoot,
|
|
669
687
|
parentBlockHash,
|
|
670
688
|
feeRecipient: suggestedFeeRecipient,
|
|
671
|
-
parentExecutionRequests,
|
|
672
689
|
});
|
|
673
690
|
|
|
674
691
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
@@ -727,14 +744,16 @@ export function getPayloadAttributesForSSE(
|
|
|
727
744
|
parentBlockRoot,
|
|
728
745
|
parentBlockHash,
|
|
729
746
|
feeRecipient,
|
|
730
|
-
parentExecutionRequests,
|
|
731
747
|
}: {
|
|
748
|
+
/**
|
|
749
|
+
* Post-gloas, when extending a full parent, callers must apply
|
|
750
|
+
* parent execution payload first (see `withParentPayloadApplied`).
|
|
751
|
+
*/
|
|
732
752
|
prepareState: IBeaconStateViewBellatrix;
|
|
733
753
|
prepareSlot: Slot;
|
|
734
754
|
parentBlockRoot: Root;
|
|
735
755
|
parentBlockHash: Bytes32;
|
|
736
756
|
feeRecipient: string;
|
|
737
|
-
parentExecutionRequests?: electra.ExecutionRequests;
|
|
738
757
|
}
|
|
739
758
|
): SSEPayloadAttributes {
|
|
740
759
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
@@ -743,7 +762,6 @@ export function getPayloadAttributesForSSE(
|
|
|
743
762
|
parentBlockRoot,
|
|
744
763
|
parentBlockHash,
|
|
745
764
|
feeRecipient,
|
|
746
|
-
parentExecutionRequests,
|
|
747
765
|
});
|
|
748
766
|
|
|
749
767
|
let parentBlockNumber: number;
|
|
@@ -782,14 +800,16 @@ function preparePayloadAttributes(
|
|
|
782
800
|
parentBlockRoot,
|
|
783
801
|
parentBlockHash,
|
|
784
802
|
feeRecipient,
|
|
785
|
-
parentExecutionRequests,
|
|
786
803
|
}: {
|
|
804
|
+
/**
|
|
805
|
+
* Post-gloas, when extending a full parent, callers must apply
|
|
806
|
+
* parent execution payload first (see `withParentPayloadApplied`).
|
|
807
|
+
*/
|
|
787
808
|
prepareState: IBeaconStateViewBellatrix;
|
|
788
809
|
prepareSlot: Slot;
|
|
789
810
|
parentBlockRoot: Root;
|
|
790
811
|
parentBlockHash: Bytes32;
|
|
791
812
|
feeRecipient: string;
|
|
792
|
-
parentExecutionRequests?: electra.ExecutionRequests;
|
|
793
813
|
}
|
|
794
814
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
795
815
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -808,11 +828,13 @@ function preparePayloadAttributes(
|
|
|
808
828
|
if (isStatePostGloas(prepareState)) {
|
|
809
829
|
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
810
830
|
if (isExtendingPayload) {
|
|
811
|
-
|
|
812
|
-
|
|
831
|
+
// applyParentExecutionPayload sets latestBlockHash = parentBid.blockHash, so a mismatch
|
|
832
|
+
// here means the caller did not apply parent payload to prepareState
|
|
833
|
+
if (!byteArrayEquals(prepareState.latestBlockHash, prepareState.latestExecutionPayloadBid.blockHash)) {
|
|
834
|
+
throw new Error("Expected state with parent execution payload applied for withdrawals");
|
|
813
835
|
}
|
|
814
836
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
815
|
-
prepareState.
|
|
837
|
+
prepareState.getExpectedWithdrawals().expectedWithdrawals;
|
|
816
838
|
} else {
|
|
817
839
|
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
818
840
|
// already deducted from CL balances but never credited on the EL (the envelope
|
|
@@ -21,6 +21,7 @@ export enum RegenCaller {
|
|
|
21
21
|
validateGossipAttestation = "validateGossipAttestation",
|
|
22
22
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
23
23
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
24
|
+
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
24
25
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
25
26
|
restApi = "restApi",
|
|
26
27
|
}
|
|
@@ -5,3 +5,4 @@ export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
|
|
|
5
5
|
export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
|
|
6
6
|
export {SeenBlockInput} from "./seenGossipBlockInput.js";
|
|
7
7
|
export {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenPayloadEnvelopeInput.js";
|
|
8
|
+
export {SeenProposerPreferences} from "./seenProposerPreferences.js";
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import {ChainForkConfig} from "@lodestar/config";
|
|
1
2
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
2
3
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
4
|
import {RootHex, Slot} from "@lodestar/types";
|
|
4
5
|
import {Logger} from "@lodestar/utils";
|
|
5
6
|
import {Metrics} from "../../metrics/metrics.js";
|
|
7
|
+
import {IClock} from "../../util/clock.js";
|
|
6
8
|
import {SerializedCache} from "../../util/serializedCache.js";
|
|
9
|
+
import {isDaOutOfRange} from "../blocks/blockInput/index.js";
|
|
7
10
|
import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
8
11
|
import {ChainEvent, ChainEventEmitter} from "../emitter.js";
|
|
9
12
|
|
|
@@ -11,6 +14,8 @@ export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/ind
|
|
|
11
14
|
export {PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
12
15
|
|
|
13
16
|
export type SeenPayloadEnvelopeInputModules = {
|
|
17
|
+
config: ChainForkConfig;
|
|
18
|
+
clock: IClock;
|
|
14
19
|
chainEvents: ChainEventEmitter;
|
|
15
20
|
signal: AbortSignal;
|
|
16
21
|
serializedCache: SerializedCache;
|
|
@@ -32,6 +37,8 @@ export type SeenPayloadEnvelopeInputModules = {
|
|
|
32
37
|
* ticks; subsequent ticks settle it back.
|
|
33
38
|
*/
|
|
34
39
|
export class SeenPayloadEnvelopeInput {
|
|
40
|
+
private readonly config: ChainForkConfig;
|
|
41
|
+
private readonly clock: IClock;
|
|
35
42
|
private readonly chainEvents: ChainEventEmitter;
|
|
36
43
|
private readonly signal: AbortSignal;
|
|
37
44
|
private readonly serializedCache: SerializedCache;
|
|
@@ -39,7 +46,9 @@ export class SeenPayloadEnvelopeInput {
|
|
|
39
46
|
private readonly logger?: Logger;
|
|
40
47
|
private payloadInputs = new Map<RootHex, PayloadEnvelopeInput>();
|
|
41
48
|
|
|
42
|
-
constructor({chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
|
|
49
|
+
constructor({config, clock, chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
|
|
50
|
+
this.config = config;
|
|
51
|
+
this.clock = clock;
|
|
43
52
|
this.chainEvents = chainEvents;
|
|
44
53
|
this.signal = signal;
|
|
45
54
|
this.serializedCache = serializedCache;
|
|
@@ -68,11 +77,12 @@ export class SeenPayloadEnvelopeInput {
|
|
|
68
77
|
this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
|
|
69
78
|
};
|
|
70
79
|
|
|
71
|
-
add(props: CreateFromBlockProps): PayloadEnvelopeInput {
|
|
80
|
+
add(props: Omit<CreateFromBlockProps, "daOutOfRange">): PayloadEnvelopeInput {
|
|
72
81
|
if (this.payloadInputs.has(props.blockRootHex)) {
|
|
73
82
|
throw new Error(`PayloadEnvelopeInput already exists for block ${props.blockRootHex}`);
|
|
74
83
|
}
|
|
75
|
-
const
|
|
84
|
+
const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.block.message.slot, this.clock.currentEpoch);
|
|
85
|
+
const input = PayloadEnvelopeInput.createFromBlock({...props, daOutOfRange});
|
|
76
86
|
this.payloadInputs.set(props.blockRootHex, input);
|
|
77
87
|
this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
|
|
78
88
|
return input;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
+
import {MapDef} from "@lodestar/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tracks signed proposer preferences we've already seen per (proposal_slot, validator_index).
|
|
6
|
+
*/
|
|
7
|
+
export class SeenProposerPreferences {
|
|
8
|
+
private readonly validatorIndexesBySlot = new MapDef<Slot, Set<ValidatorIndex>>(() => new Set<ValidatorIndex>());
|
|
9
|
+
|
|
10
|
+
isKnown(proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean {
|
|
11
|
+
return this.validatorIndexesBySlot.get(proposalSlot)?.has(validatorIndex) === true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
add(proposalSlot: Slot, validatorIndex: ValidatorIndex): void {
|
|
15
|
+
this.validatorIndexesBySlot.getOrDefault(proposalSlot).add(validatorIndex);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Entries are only load-bearing while `proposal_slot > state.slot`. Once the slot has passed the
|
|
20
|
+
* `[IGNORE] proposal_slot > state.slot` gossip rule takes over, so drop them on each slot tick.
|
|
21
|
+
*/
|
|
22
|
+
prune(currentSlot: Slot): void {
|
|
23
|
+
for (const slot of this.validatorIndexesBySlot.keys()) {
|
|
24
|
+
if (slot < currentSlot) {
|
|
25
|
+
this.validatorIndexesBySlot.delete(slot);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|