@lodestar/beacon-node 1.43.0-dev.1540a78cd0 → 1.43.0-dev.1629c3a59e
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/emitter.d.ts +0 -11
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +0 -4
- package/lib/chain/emitter.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 +41 -18
- 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 -15
- 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 +6 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +0 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +0 -47
- package/lib/sync/unknownBlock.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/emitter.ts +0 -11
- 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 +51 -23
- 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 -16
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +6 -5
- package/src/sync/unknownBlock.ts +0 -50
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ApplicationMethods} from "@lodestar/api/server";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ForkPostElectra,
|
|
5
|
+
ForkPreElectra,
|
|
6
|
+
SYNC_COMMITTEE_SUBNET_SIZE,
|
|
7
|
+
isForkPostElectra,
|
|
8
|
+
isForkPostGloas,
|
|
9
|
+
} from "@lodestar/params";
|
|
4
10
|
import {isStatePostAltair} from "@lodestar/state-transition";
|
|
5
11
|
import {Attestation, Epoch, SingleAttestation, isElectraAttestation, ssz, sszTypesFor} from "@lodestar/types";
|
|
12
|
+
import {toRootHex} from "@lodestar/utils";
|
|
6
13
|
import {
|
|
7
14
|
AttestationError,
|
|
8
15
|
AttestationErrorCode,
|
|
9
16
|
GossipAction,
|
|
17
|
+
PayloadAttestationError,
|
|
18
|
+
PayloadAttestationErrorCode,
|
|
10
19
|
SyncCommitteeError,
|
|
11
20
|
} from "../../../../chain/errors/index.js";
|
|
12
21
|
import {validateApiAttesterSlashing} from "../../../../chain/validation/attesterSlashing.js";
|
|
13
22
|
import {validateApiBlsToExecutionChange} from "../../../../chain/validation/blsToExecutionChange.js";
|
|
14
23
|
import {toElectraSingleAttestation, validateApiAttestation} from "../../../../chain/validation/index.js";
|
|
24
|
+
import {validateApiPayloadAttestationMessage} from "../../../../chain/validation/payloadAttestationMessage.js";
|
|
15
25
|
import {validateApiProposerSlashing} from "../../../../chain/validation/proposerSlashing.js";
|
|
16
26
|
import {validateApiSyncCommittee} from "../../../../chain/validation/syncCommittee.js";
|
|
17
27
|
import {validateApiVoluntaryExit} from "../../../../chain/validation/voluntaryExit.js";
|
|
@@ -62,6 +72,15 @@ export function getBeaconPoolApi({
|
|
|
62
72
|
return {data: attestations, meta: {version: fork}};
|
|
63
73
|
},
|
|
64
74
|
|
|
75
|
+
async getPoolPayloadAttestations({slot}) {
|
|
76
|
+
const fork = chain.config.getForkName(slot ?? chain.clock.currentSlot);
|
|
77
|
+
if (!isForkPostGloas(fork)) {
|
|
78
|
+
throw new ApiError(400, `Payload attestation pool is not supported before Gloas fork=${fork}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {data: chain.payloadAttestationPool.getAll(slot), meta: {version: fork}};
|
|
82
|
+
},
|
|
83
|
+
|
|
65
84
|
async getPoolAttesterSlashings() {
|
|
66
85
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
67
86
|
|
|
@@ -231,6 +250,69 @@ export function getBeaconPoolApi({
|
|
|
231
250
|
}
|
|
232
251
|
},
|
|
233
252
|
|
|
253
|
+
async submitPayloadAttestationMessages({payloadAttestationMessages}) {
|
|
254
|
+
const failures: FailureList = [];
|
|
255
|
+
|
|
256
|
+
await Promise.all(
|
|
257
|
+
payloadAttestationMessages.map(async (payloadAttestationMessage, i) => {
|
|
258
|
+
try {
|
|
259
|
+
const validateFn = () => validateApiPayloadAttestationMessage(chain, payloadAttestationMessage);
|
|
260
|
+
const {slot, beaconBlockRoot} = payloadAttestationMessage.data;
|
|
261
|
+
const {attDataRootHex, validatorCommitteeIndex} = await validateGossipFnRetryUnknownRoot(
|
|
262
|
+
validateFn,
|
|
263
|
+
network,
|
|
264
|
+
chain,
|
|
265
|
+
slot,
|
|
266
|
+
beaconBlockRoot
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const insertOutcome = chain.payloadAttestationPool.add(
|
|
270
|
+
payloadAttestationMessage,
|
|
271
|
+
attDataRootHex,
|
|
272
|
+
validatorCommitteeIndex
|
|
273
|
+
);
|
|
274
|
+
metrics?.opPool.payloadAttestationPool.apiInsertOutcome.inc({insertOutcome});
|
|
275
|
+
|
|
276
|
+
chain.forkChoice.notifyPtcMessages(
|
|
277
|
+
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
278
|
+
[validatorCommitteeIndex],
|
|
279
|
+
payloadAttestationMessage.data.payloadPresent
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await network.publishPayloadAttestationMessage(payloadAttestationMessage);
|
|
283
|
+
} catch (e) {
|
|
284
|
+
const logCtx = {
|
|
285
|
+
slot: payloadAttestationMessage.data.slot,
|
|
286
|
+
validatorIndex: payloadAttestationMessage.validatorIndex,
|
|
287
|
+
beaconBlockRoot: toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
if (
|
|
291
|
+
e instanceof PayloadAttestationError &&
|
|
292
|
+
e.type.code === PayloadAttestationErrorCode.PAYLOAD_ATTESTATION_ALREADY_KNOWN
|
|
293
|
+
) {
|
|
294
|
+
logger.debug("Ignoring known payload attestation message", logCtx);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
failures.push({index: i, message: (e as Error).message});
|
|
299
|
+
logger.verbose(`Error on submitPayloadAttestationMessages [${i}]`, logCtx, e as Error);
|
|
300
|
+
if (e instanceof PayloadAttestationError && e.action === GossipAction.REJECT) {
|
|
301
|
+
chain.persistInvalidSszValue(
|
|
302
|
+
ssz.gloas.PayloadAttestationMessage,
|
|
303
|
+
payloadAttestationMessage,
|
|
304
|
+
"api_reject"
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
if (failures.length > 0) {
|
|
312
|
+
throw new IndexedError("Error processing payload attestation messages", failures);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
|
|
234
316
|
/**
|
|
235
317
|
* POST `/eth/v1/beacon/pool/sync_committees`
|
|
236
318
|
*
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
computeTimeAtSlot,
|
|
27
27
|
getCurrentSlot,
|
|
28
28
|
isStatePostAltair,
|
|
29
|
+
isStatePostGloas,
|
|
29
30
|
proposerShufflingDecisionRoot,
|
|
30
31
|
} from "@lodestar/state-transition";
|
|
31
32
|
import {
|
|
@@ -1049,6 +1050,36 @@ export function getValidatorApi(
|
|
|
1049
1050
|
};
|
|
1050
1051
|
},
|
|
1051
1052
|
|
|
1053
|
+
async producePayloadAttestationData({slot}) {
|
|
1054
|
+
const fork = config.getForkName(slot);
|
|
1055
|
+
if (!isForkPostGloas(fork)) {
|
|
1056
|
+
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
notWhileSyncing();
|
|
1060
|
+
await waitForSlot(slot);
|
|
1061
|
+
|
|
1062
|
+
const block = chain.forkChoice.getCanonicalBlockClosestLteSlot(slot);
|
|
1063
|
+
if (!block) {
|
|
1064
|
+
throw new ApiError(404, `No canonical block found at or before slot=${slot}`);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
const blockIsForSlot = block.slot === slot;
|
|
1068
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
1069
|
+
const payloadPresent = blockIsForSlot && (payloadInput?.hasPayloadEnvelope() ?? false);
|
|
1070
|
+
const blobDataAvailable = blockIsForSlot && (payloadInput?.hasAllData() ?? false);
|
|
1071
|
+
|
|
1072
|
+
return {
|
|
1073
|
+
data: {
|
|
1074
|
+
beaconBlockRoot: fromHex(block.blockRoot),
|
|
1075
|
+
slot,
|
|
1076
|
+
payloadPresent,
|
|
1077
|
+
blobDataAvailable,
|
|
1078
|
+
},
|
|
1079
|
+
meta: {version: fork},
|
|
1080
|
+
};
|
|
1081
|
+
},
|
|
1082
|
+
|
|
1052
1083
|
/**
|
|
1053
1084
|
* GET `/eth/v1/validator/sync_committee_contribution`
|
|
1054
1085
|
*
|
|
@@ -1263,6 +1294,55 @@ export function getValidatorApi(
|
|
|
1263
1294
|
};
|
|
1264
1295
|
},
|
|
1265
1296
|
|
|
1297
|
+
async getPtcDuties({epoch, indices}) {
|
|
1298
|
+
notWhileSyncing();
|
|
1299
|
+
|
|
1300
|
+
if (indices.length === 0) {
|
|
1301
|
+
throw new ApiError(400, "No validator to get PTC duties");
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
1305
|
+
const fork = config.getForkName(startSlot);
|
|
1306
|
+
if (!isForkPostGloas(fork)) {
|
|
1307
|
+
throw new ApiError(400, `PTC duties are not supported before Gloas fork=${fork}`);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
await waitForNextClosestEpoch();
|
|
1311
|
+
|
|
1312
|
+
if (epoch > chain.clock.currentEpoch + 1) {
|
|
1313
|
+
throw new ApiError(400, "Cannot get PTC duties for epoch more than one ahead");
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
const head = chain.forkChoice.getHead();
|
|
1317
|
+
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
1318
|
+
if (!isStatePostGloas(state)) {
|
|
1319
|
+
throw new ApiError(400, `PTC duties are not available before Gloas fork=${state.forkName}`);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
const pubkeys = getPubkeysForIndices(state, indices);
|
|
1323
|
+
const ptcs = state.getEpochPTCs(epoch);
|
|
1324
|
+
const duties: routes.validator.PtcDuty[] = [];
|
|
1325
|
+
for (let i = 0, len = indices.length; i < len; i++) {
|
|
1326
|
+
const validatorIndex = indices[i];
|
|
1327
|
+
for (let j = 0; j < SLOTS_PER_EPOCH; j++) {
|
|
1328
|
+
if (ptcs[j].indexOf(validatorIndex) !== -1) {
|
|
1329
|
+
duties.push({pubkey: pubkeys[i], validatorIndex, slot: j + startSlot});
|
|
1330
|
+
break;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const dependentRoot = fromHex(state.getShufflingDecisionRoot(epoch)) || (await getGenesisBlockRoot(state));
|
|
1336
|
+
|
|
1337
|
+
return {
|
|
1338
|
+
data: duties,
|
|
1339
|
+
meta: {
|
|
1340
|
+
dependentRoot: toRootHex(dependentRoot),
|
|
1341
|
+
executionOptimistic: isOptimisticBlock(head),
|
|
1342
|
+
},
|
|
1343
|
+
};
|
|
1344
|
+
},
|
|
1345
|
+
|
|
1266
1346
|
/**
|
|
1267
1347
|
* `POST /eth/v1/validator/duties/sync/{epoch}`
|
|
1268
1348
|
*
|
|
@@ -128,7 +128,6 @@ export async function importBlock(
|
|
|
128
128
|
blockDelaySec,
|
|
129
129
|
currentSlot,
|
|
130
130
|
fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus,
|
|
131
|
-
// TODO GLOAS: this is not useful post-gloas, may need to remove it?
|
|
132
131
|
dataAvailabilityStatus
|
|
133
132
|
);
|
|
134
133
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
|
-
import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
|
|
3
|
-
import {isStatePostGloas} from "@lodestar/state-transition";
|
|
4
|
-
import {fromHex} from "@lodestar/utils";
|
|
2
|
+
import {ExecutionStatus, PayloadExecutionStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
3
|
+
import {DataAvailabilityStatus, isStatePostGloas} from "@lodestar/state-transition";
|
|
4
|
+
import {fromHex, isErrorAborted} from "@lodestar/utils";
|
|
5
|
+
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
5
6
|
import {ExecutionPayloadStatus} from "../../execution/index.js";
|
|
6
7
|
import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
7
8
|
import {BeaconChain} from "../chain.js";
|
|
@@ -85,12 +86,14 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
85
86
|
* 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
|
|
86
87
|
* 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
|
|
87
88
|
* 6. Update fork choice (transitions the block's PENDING variant to FULL)
|
|
88
|
-
* 7.
|
|
89
|
-
* 8.
|
|
89
|
+
* 7. Queue notifyForkchoiceUpdate to engine api
|
|
90
|
+
* 8. Record metrics for payload envelope and column sources
|
|
91
|
+
* 9. Emit `execution_payload` event
|
|
90
92
|
*/
|
|
91
93
|
export async function importExecutionPayload(
|
|
92
94
|
this: BeaconChain,
|
|
93
95
|
payloadInput: PayloadEnvelopeInput,
|
|
96
|
+
dataAvailabilityStatus: DataAvailabilityStatus,
|
|
94
97
|
opts: ImportPayloadOpts = {}
|
|
95
98
|
): Promise<void> {
|
|
96
99
|
const signedEnvelope = payloadInput.getPayloadEnvelope();
|
|
@@ -219,15 +222,33 @@ export async function importExecutionPayload(
|
|
|
219
222
|
|
|
220
223
|
// 6. Update fork choice, transitions the block's PENDING variant to FULL
|
|
221
224
|
const execStatus = toForkChoiceExecutionStatus(execResult.status);
|
|
222
|
-
this.forkChoice.onExecutionPayload(
|
|
225
|
+
this.forkChoice.onExecutionPayload(
|
|
226
|
+
blockRootHex,
|
|
227
|
+
blockHashHex,
|
|
228
|
+
envelope.payload.blockNumber,
|
|
229
|
+
execStatus,
|
|
230
|
+
dataAvailabilityStatus
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// 7. Queue notifyForkchoiceUpdate to engine api
|
|
234
|
+
const head = this.forkChoice.getHead();
|
|
235
|
+
if (!this.opts.disableImportExecutionFcU && blockRootHex === head.blockRoot) {
|
|
236
|
+
const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
|
|
237
|
+
const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
238
|
+
this.executionEngine.notifyForkchoiceUpdate(fork, blockHashHex, safeBlockHash, finalizedBlockHash).catch((e) => {
|
|
239
|
+
if (!isErrorAborted(e) && !isQueueErrorAborted(e)) {
|
|
240
|
+
this.logger.error("Error pushing notifyForkchoiceUpdate()", {blockHashHex, finalizedBlockHash}, e);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
223
244
|
|
|
224
|
-
//
|
|
245
|
+
// 8. Record metrics for payload envelope and column sources
|
|
225
246
|
this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
|
|
226
247
|
for (const {source} of payloadInput.getSampledColumnsWithSource()) {
|
|
227
248
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
228
249
|
}
|
|
229
250
|
|
|
230
|
-
//
|
|
251
|
+
// 9. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
|
|
231
252
|
if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
232
253
|
this.emitter.emit(routes.events.EventType.executionPayload, {
|
|
233
254
|
slot,
|
|
@@ -261,6 +282,6 @@ export async function processExecutionPayload(
|
|
|
261
282
|
signal: AbortSignal,
|
|
262
283
|
opts: ImportPayloadOpts = {}
|
|
263
284
|
): Promise<void> {
|
|
264
|
-
await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
265
|
-
await importExecutionPayload.call(this, payloadInput, opts);
|
|
285
|
+
const {dataAvailabilityStatuses} = await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
286
|
+
await importExecutionPayload.call(this, payloadInput, dataAvailabilityStatuses[0], opts);
|
|
266
287
|
}
|
|
@@ -90,8 +90,14 @@ export async function processBlocks(
|
|
|
90
90
|
|
|
91
91
|
// Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
|
|
92
92
|
// states in the state cache through regen.
|
|
93
|
-
const {
|
|
94
|
-
|
|
93
|
+
const {
|
|
94
|
+
postStates,
|
|
95
|
+
blockDAStatuses,
|
|
96
|
+
payloadDAStatuses,
|
|
97
|
+
proposerBalanceDeltas,
|
|
98
|
+
segmentExecStatus,
|
|
99
|
+
indexedAttestationsByBlock,
|
|
100
|
+
} = await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
|
|
95
101
|
|
|
96
102
|
// If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
|
|
97
103
|
// and we need to further propagate
|
|
@@ -110,7 +116,7 @@ export async function processBlocks(
|
|
|
110
116
|
parentBlockSlot: parentSlots[i],
|
|
111
117
|
executionStatus: executionStatuses[i],
|
|
112
118
|
// start supporting optimistic syncing/processing
|
|
113
|
-
dataAvailabilityStatus:
|
|
119
|
+
dataAvailabilityStatus: blockDAStatuses[i],
|
|
114
120
|
proposerBalanceDelta: proposerBalanceDeltas[i],
|
|
115
121
|
indexedAttestations: indexedAttestationsByBlock[i],
|
|
116
122
|
// TODO: Make this param mandatory and capture in gossip
|
|
@@ -130,9 +136,11 @@ export async function processBlocks(
|
|
|
130
136
|
throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
|
|
131
137
|
}
|
|
132
138
|
// we already awaited DA in verifyBlocksInEpoch for this segment
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
const payloadDA = payloadDAStatuses.get(slot);
|
|
140
|
+
if (payloadDA === undefined) {
|
|
141
|
+
throw new Error(`Missing payload DA status for slot ${slot}`);
|
|
142
|
+
}
|
|
143
|
+
await importExecutionPayload.call(this, payloadInput, payloadDA, {validSignature: false});
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
await nextEventLoop();
|
|
@@ -64,6 +64,7 @@ export class PayloadEnvelopeInput {
|
|
|
64
64
|
readonly proposerIndex: ValidatorIndex;
|
|
65
65
|
readonly bid: gloas.ExecutionPayloadBid;
|
|
66
66
|
readonly versionedHashes: VersionedHashes;
|
|
67
|
+
readonly daOutOfRange: boolean;
|
|
67
68
|
|
|
68
69
|
private columnsCache = new Map<ColumnIndex, ColumnWithSource>();
|
|
69
70
|
|
|
@@ -87,6 +88,7 @@ export class PayloadEnvelopeInput {
|
|
|
87
88
|
sampledColumns: ColumnIndex[];
|
|
88
89
|
custodyColumns: ColumnIndex[];
|
|
89
90
|
timeCreatedSec: number;
|
|
91
|
+
daOutOfRange: boolean;
|
|
90
92
|
}) {
|
|
91
93
|
this.blockRootHex = props.blockRootHex;
|
|
92
94
|
this.slot = props.slot;
|
|
@@ -97,13 +99,14 @@ export class PayloadEnvelopeInput {
|
|
|
97
99
|
this.sampledColumns = props.sampledColumns;
|
|
98
100
|
this.custodyColumns = props.custodyColumns;
|
|
99
101
|
this.timeCreatedSec = props.timeCreatedSec;
|
|
102
|
+
this.daOutOfRange = props.daOutOfRange;
|
|
100
103
|
this.payloadEnvelopeDataPromise = createPromise();
|
|
101
104
|
this.allDataPromise = createPromise();
|
|
102
105
|
this.columnsDataPromise = createPromise();
|
|
103
106
|
|
|
104
107
|
const noBlobs = props.bid.blobKzgCommitments.length === 0;
|
|
105
108
|
const noSampledColumns = props.sampledColumns.length === 0;
|
|
106
|
-
const hasAllData = noBlobs || noSampledColumns;
|
|
109
|
+
const hasAllData = props.daOutOfRange || noBlobs || noSampledColumns;
|
|
107
110
|
|
|
108
111
|
if (hasAllData) {
|
|
109
112
|
this.state = {hasPayload: false, hasAllData: true, hasComputedAllData: true};
|
|
@@ -125,6 +128,7 @@ export class PayloadEnvelopeInput {
|
|
|
125
128
|
sampledColumns: props.sampledColumns,
|
|
126
129
|
custodyColumns: props.custodyColumns,
|
|
127
130
|
timeCreatedSec: props.timeCreatedSec,
|
|
131
|
+
daOutOfRange: props.daOutOfRange,
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -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
|
|
package/src/chain/emitter.ts
CHANGED
|
@@ -4,7 +4,6 @@ import {routes} from "@lodestar/api";
|
|
|
4
4
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
5
5
|
import {IBeaconStateView} from "@lodestar/state-transition";
|
|
6
6
|
import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
|
|
7
|
-
import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
|
|
8
7
|
import {PeerIdStr} from "../util/peerId.js";
|
|
9
8
|
import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
|
|
10
9
|
import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
@@ -60,10 +59,6 @@ export enum ChainEvent {
|
|
|
60
59
|
* Post-gloas, missing parent could be a SignedBeaconBlock and/or a SignedExecutionPayloadEnvelope
|
|
61
60
|
*/
|
|
62
61
|
blockUnknownParent = "blockUnknownParent",
|
|
63
|
-
/**
|
|
64
|
-
* Trigger BlockInputSync to find a SignedBeaconBlock given a SignedExecutionPayloadEnvelop received
|
|
65
|
-
*/
|
|
66
|
-
envelopeUnknownBlock = "envelopeUnknownBlock",
|
|
67
62
|
/**
|
|
68
63
|
* Trigger BlockInputSync to find a SignedBeaconBlock with specified block root.
|
|
69
64
|
*/
|
|
@@ -92,11 +87,6 @@ type ApiEvents = {[K in routes.events.EventType]: (data: routes.events.EventData
|
|
|
92
87
|
|
|
93
88
|
export type ChainEventData = {
|
|
94
89
|
[ChainEvent.blockUnknownParent]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
|
|
95
|
-
[ChainEvent.envelopeUnknownBlock]: {
|
|
96
|
-
envelope: SignedExecutionPayloadEnvelope;
|
|
97
|
-
peer?: PeerIdStr;
|
|
98
|
-
source: BlockInputSource;
|
|
99
|
-
};
|
|
100
90
|
[ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
|
|
101
91
|
[ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
|
|
102
92
|
[ChainEvent.incompletePayloadEnvelope]: {
|
|
@@ -124,7 +114,6 @@ export type IChainEvents = ApiEvents & {
|
|
|
124
114
|
// Sync events that are chain->chain. Initiated from network requests but do not cross the network
|
|
125
115
|
// barrier so are considered ChainEvent(s).
|
|
126
116
|
[ChainEvent.blockUnknownParent]: (data: ChainEventData[ChainEvent.blockUnknownParent]) => void;
|
|
127
|
-
[ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
|
|
128
117
|
[ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
|
|
129
118
|
[ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
|
|
130
119
|
[ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
|
|
@@ -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;
|