@lodestar/beacon-node 1.43.0-dev.a142c56215 → 1.43.0-dev.a691e9b4dd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +13 -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/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +0 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +68 -2
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +4 -1
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +23 -22
- 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 +25 -14
- 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 +36 -21
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +4 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +9 -2
- 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/types.d.ts +2 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +8 -0
- package/lib/chain/blocks/utils/chainSegment.js.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 +30 -12
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +7 -4
- 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 +40 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/errors/proposerPreferences.js +14 -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 +12 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +34 -22
- 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/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +1 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +1 -4
- package/lib/chain/regen/regen.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 +20 -4
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +16 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
- package/lib/chain/seenCache/seenProposerPreferences.js +31 -0
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
- package/lib/chain/validation/executionPayloadBid.js +11 -8
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- 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 +91 -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 +29 -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 +22 -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/node/nodejs.js +2 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/notifier.js +1 -7
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/range/batch.d.ts +11 -0
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +83 -21
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +23 -5
- package/lib/sync/range/chain.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/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +36 -21
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +10 -0
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +8 -6
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +16 -3
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/validator/index.ts +82 -1
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +23 -39
- package/src/chain/blocks/importExecutionPayload.ts +33 -14
- package/src/chain/blocks/index.ts +34 -15
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +10 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
- package/src/chain/blocks/types.ts +2 -1
- package/src/chain/blocks/utils/chainSegment.ts +8 -0
- package/src/chain/blocks/verifyBlock.ts +45 -13
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +9 -4
- 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 +47 -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 +45 -27
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/regen/queued.ts +2 -7
- package/src/chain/regen/regen.ts +2 -7
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +25 -5
- package/src/chain/seenCache/seenProposerPreferences.ts +37 -0
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/proposerPreferences.ts +110 -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 +31 -16
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +6 -5
- package/src/node/nodejs.ts +2 -2
- package/src/node/notifier.ts +1 -8
- package/src/sync/range/batch.ts +108 -24
- package/src/sync/range/chain.ts +25 -5
- package/src/sync/unknownBlock.ts +0 -50
- package/src/sync/utils/downloadByRange.ts +37 -21
- package/src/sync/utils/downloadByRoot.ts +12 -0
- package/src/util/sszBytes.ts +8 -6
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
ssz,
|
|
50
50
|
} from "@lodestar/types";
|
|
51
51
|
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
52
|
-
import {
|
|
52
|
+
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
53
53
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
54
54
|
import {
|
|
55
55
|
IExecutionBuilder,
|
|
@@ -111,6 +111,7 @@ export type ProduceFullGloas = {
|
|
|
111
111
|
executionRequests: electra.ExecutionRequests;
|
|
112
112
|
blobsBundle: BlobsBundle<ForkPostGloas>;
|
|
113
113
|
cells: fulu.Cell[][];
|
|
114
|
+
parentBlockRoot: Root;
|
|
114
115
|
};
|
|
115
116
|
export type ProduceFullFulu = {
|
|
116
117
|
type: BlockType.Full;
|
|
@@ -214,19 +215,19 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
214
215
|
});
|
|
215
216
|
|
|
216
217
|
// Get execution payload from EL
|
|
218
|
+
let parentBlockHash: Bytes32;
|
|
219
|
+
let parentExecutionRequests: electra.ExecutionRequests;
|
|
220
|
+
// Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
|
|
221
|
+
let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
|
|
217
222
|
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)) {
|
|
223
|
+
if (isExtendingPayload) {
|
|
225
224
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
225
|
+
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
226
|
+
stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
|
|
227
|
+
} else {
|
|
228
|
+
parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
229
|
+
parentExecutionRequests = ssz.electra.ExecutionRequests.defaultValue();
|
|
226
230
|
}
|
|
227
|
-
const parentExecutionRequests = isExtendingPayload
|
|
228
|
-
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
229
|
-
: ssz.electra.ExecutionRequests.defaultValue();
|
|
230
231
|
const prepareRes = await prepareExecutionPayload(
|
|
231
232
|
this,
|
|
232
233
|
this.logger,
|
|
@@ -235,9 +236,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
235
236
|
parentBlockHash,
|
|
236
237
|
safeBlockHash,
|
|
237
238
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
238
|
-
|
|
239
|
-
feeRecipient
|
|
240
|
-
parentExecutionRequests
|
|
239
|
+
stateAfterParentPayload,
|
|
240
|
+
feeRecipient
|
|
241
241
|
);
|
|
242
242
|
|
|
243
243
|
const {prepType, payloadId} = prepareRes;
|
|
@@ -291,9 +291,19 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
291
291
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
292
292
|
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
293
293
|
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
295
|
+
parentBlock.blockRoot,
|
|
296
|
+
blockSlot - 1
|
|
297
|
+
);
|
|
296
298
|
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
299
|
+
// Drop voluntary exits that parent_execution_requests have invalidated (e.g. a withdrawal
|
|
300
|
+
// request initiating an exit on the same validator). Op pool selected against the unapplied
|
|
301
|
+
// state, so re-validate against the post-apply state to avoid producing an invalid block.
|
|
302
|
+
if (isExtendingPayload && commonBlockBody.voluntaryExits.length > 0) {
|
|
303
|
+
gloasBody.voluntaryExits = commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
|
|
304
|
+
stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
297
307
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
298
308
|
|
|
299
309
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -302,6 +312,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
302
312
|
gloasResult.executionRequests = executionRequests;
|
|
303
313
|
gloasResult.blobsBundle = blobsBundle;
|
|
304
314
|
gloasResult.cells = cells;
|
|
315
|
+
gloasResult.parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
305
316
|
|
|
306
317
|
const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
|
|
307
318
|
this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
|
|
@@ -629,9 +640,12 @@ export async function prepareExecutionPayload(
|
|
|
629
640
|
parentBlockHash: Bytes32,
|
|
630
641
|
safeBlockHash: RootHex,
|
|
631
642
|
finalizedBlockHash: RootHex,
|
|
643
|
+
/**
|
|
644
|
+
* Post-gloas, when extending a full parent, callers must apply
|
|
645
|
+
* parent execution payload first (see `withParentPayloadApplied`).
|
|
646
|
+
*/
|
|
632
647
|
state: IBeaconStateViewBellatrix,
|
|
633
|
-
suggestedFeeRecipient: string
|
|
634
|
-
parentExecutionRequests?: electra.ExecutionRequests
|
|
648
|
+
suggestedFeeRecipient: string
|
|
635
649
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
636
650
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
637
651
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
@@ -668,7 +682,6 @@ export async function prepareExecutionPayload(
|
|
|
668
682
|
parentBlockRoot,
|
|
669
683
|
parentBlockHash,
|
|
670
684
|
feeRecipient: suggestedFeeRecipient,
|
|
671
|
-
parentExecutionRequests,
|
|
672
685
|
});
|
|
673
686
|
|
|
674
687
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
@@ -727,14 +740,16 @@ export function getPayloadAttributesForSSE(
|
|
|
727
740
|
parentBlockRoot,
|
|
728
741
|
parentBlockHash,
|
|
729
742
|
feeRecipient,
|
|
730
|
-
parentExecutionRequests,
|
|
731
743
|
}: {
|
|
744
|
+
/**
|
|
745
|
+
* Post-gloas, when extending a full parent, callers must apply
|
|
746
|
+
* parent execution payload first (see `withParentPayloadApplied`).
|
|
747
|
+
*/
|
|
732
748
|
prepareState: IBeaconStateViewBellatrix;
|
|
733
749
|
prepareSlot: Slot;
|
|
734
750
|
parentBlockRoot: Root;
|
|
735
751
|
parentBlockHash: Bytes32;
|
|
736
752
|
feeRecipient: string;
|
|
737
|
-
parentExecutionRequests?: electra.ExecutionRequests;
|
|
738
753
|
}
|
|
739
754
|
): SSEPayloadAttributes {
|
|
740
755
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
@@ -743,7 +758,6 @@ export function getPayloadAttributesForSSE(
|
|
|
743
758
|
parentBlockRoot,
|
|
744
759
|
parentBlockHash,
|
|
745
760
|
feeRecipient,
|
|
746
|
-
parentExecutionRequests,
|
|
747
761
|
});
|
|
748
762
|
|
|
749
763
|
let parentBlockNumber: number;
|
|
@@ -782,14 +796,16 @@ function preparePayloadAttributes(
|
|
|
782
796
|
parentBlockRoot,
|
|
783
797
|
parentBlockHash,
|
|
784
798
|
feeRecipient,
|
|
785
|
-
parentExecutionRequests,
|
|
786
799
|
}: {
|
|
800
|
+
/**
|
|
801
|
+
* Post-gloas, when extending a full parent, callers must apply
|
|
802
|
+
* parent execution payload first (see `withParentPayloadApplied`).
|
|
803
|
+
*/
|
|
787
804
|
prepareState: IBeaconStateViewBellatrix;
|
|
788
805
|
prepareSlot: Slot;
|
|
789
806
|
parentBlockRoot: Root;
|
|
790
807
|
parentBlockHash: Bytes32;
|
|
791
808
|
feeRecipient: string;
|
|
792
|
-
parentExecutionRequests?: electra.ExecutionRequests;
|
|
793
809
|
}
|
|
794
810
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
795
811
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -808,11 +824,13 @@ function preparePayloadAttributes(
|
|
|
808
824
|
if (isStatePostGloas(prepareState)) {
|
|
809
825
|
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
810
826
|
if (isExtendingPayload) {
|
|
811
|
-
|
|
812
|
-
|
|
827
|
+
// applyParentExecutionPayload sets latestBlockHash = parentBid.blockHash, so a mismatch
|
|
828
|
+
// here means the caller did not apply parent payload to prepareState
|
|
829
|
+
if (!byteArrayEquals(prepareState.latestBlockHash, prepareState.latestExecutionPayloadBid.blockHash)) {
|
|
830
|
+
throw new Error("Expected state with parent execution payload applied for withdrawals");
|
|
813
831
|
}
|
|
814
832
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
815
|
-
prepareState.
|
|
833
|
+
prepareState.getExpectedWithdrawals().expectedWithdrawals;
|
|
816
834
|
} else {
|
|
817
835
|
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
818
836
|
// 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
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
3
3
|
import {IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
4
|
-
import {BeaconBlock, Epoch, RootHex, Slot,
|
|
4
|
+
import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
|
|
5
5
|
import {Logger, toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {Metrics} from "../../metrics/index.js";
|
|
7
7
|
import {JobItemQueue} from "../../util/queue/index.js";
|
|
@@ -88,12 +88,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
88
88
|
*/
|
|
89
89
|
getPreStateSync(block: BeaconBlock): IBeaconStateView | null {
|
|
90
90
|
const parentRoot = toRootHex(block.parentRoot);
|
|
91
|
-
const parentBlock =
|
|
92
|
-
? this.forkChoice.getBlockHexAndBlockHash(
|
|
93
|
-
parentRoot,
|
|
94
|
-
toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
95
|
-
)
|
|
96
|
-
: this.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
91
|
+
const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
97
92
|
if (!parentBlock) {
|
|
98
93
|
throw new RegenError({
|
|
99
94
|
code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
|
package/src/chain/regen/regen.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
computeEpochAtSlot,
|
|
10
10
|
computeStartSlotAtEpoch,
|
|
11
11
|
} from "@lodestar/state-transition";
|
|
12
|
-
import {BeaconBlock, RootHex, SignedBeaconBlock, Slot
|
|
12
|
+
import {BeaconBlock, RootHex, SignedBeaconBlock, Slot} from "@lodestar/types";
|
|
13
13
|
import {Logger, fromHex, toRootHex} from "@lodestar/utils";
|
|
14
14
|
import {IBeaconDb} from "../../db/index.js";
|
|
15
15
|
import {Metrics} from "../../metrics/index.js";
|
|
@@ -57,12 +57,7 @@ export class StateRegenerator implements IStateRegeneratorInternal {
|
|
|
57
57
|
regenCaller: RegenCaller
|
|
58
58
|
): Promise<IBeaconStateView> {
|
|
59
59
|
const parentRoot = toRootHex(block.parentRoot);
|
|
60
|
-
const parentBlock =
|
|
61
|
-
? this.modules.forkChoice.getBlockHexAndBlockHash(
|
|
62
|
-
parentRoot,
|
|
63
|
-
toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
64
|
-
)
|
|
65
|
-
: this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
60
|
+
const parentBlock = this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
66
61
|
if (!parentBlock) {
|
|
67
62
|
throw new RegenError({
|
|
68
63
|
code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
|
|
@@ -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,13 +77,24 @@ export class SeenPayloadEnvelopeInput {
|
|
|
68
77
|
this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
|
|
69
78
|
};
|
|
70
79
|
|
|
71
|
-
add(props: CreateFromBlockProps): PayloadEnvelopeInput {
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
add(props: Omit<CreateFromBlockProps, "daOutOfRange">): PayloadEnvelopeInput {
|
|
81
|
+
const existing = this.payloadInputs.get(props.blockRootHex);
|
|
82
|
+
if (existing !== undefined) {
|
|
83
|
+
this.logger?.verbose("SeenPayloadEnvelopeInput.add reused existing entry", {
|
|
84
|
+
slot: existing.slot,
|
|
85
|
+
root: props.blockRootHex,
|
|
86
|
+
});
|
|
87
|
+
return existing;
|
|
74
88
|
}
|
|
75
|
-
const
|
|
89
|
+
const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.block.message.slot, this.clock.currentEpoch);
|
|
90
|
+
const input = PayloadEnvelopeInput.createFromBlock({...props, daOutOfRange});
|
|
76
91
|
this.payloadInputs.set(props.blockRootHex, input);
|
|
77
92
|
this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
|
|
93
|
+
this.logger?.verbose("SeenPayloadEnvelopeInput.add created new entry", {
|
|
94
|
+
slot: input.slot,
|
|
95
|
+
root: props.blockRootHex,
|
|
96
|
+
daOutOfRange,
|
|
97
|
+
});
|
|
78
98
|
return input;
|
|
79
99
|
}
|
|
80
100
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {RootHex, Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
+
import {MapDef} from "@lodestar/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
6
|
+
*/
|
|
7
|
+
export class SeenProposerPreferences {
|
|
8
|
+
private readonly validatorBySlotByDependentRoot = new MapDef<RootHex, Map<Slot, ValidatorIndex>>(
|
|
9
|
+
() => new Map<Slot, ValidatorIndex>()
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
isKnown(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean {
|
|
13
|
+
return this.validatorBySlotByDependentRoot.get(dependentRoot)?.get(proposalSlot) === validatorIndex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
add(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): void {
|
|
17
|
+
this.validatorBySlotByDependentRoot.getOrDefault(dependentRoot).set(proposalSlot, validatorIndex);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
22
|
+
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
23
|
+
* on each slot tick.
|
|
24
|
+
*/
|
|
25
|
+
prune(currentSlot: Slot): void {
|
|
26
|
+
for (const [dependentRoot, slotMap] of this.validatorBySlotByDependentRoot.entries()) {
|
|
27
|
+
for (const slot of slotMap.keys()) {
|
|
28
|
+
if (slot < currentSlot) {
|
|
29
|
+
slotMap.delete(slot);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (slotMap.size === 0) {
|
|
33
|
+
this.validatorBySlotByDependentRoot.delete(dependentRoot);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -48,9 +48,12 @@ async function validateExecutionPayloadBid(
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// [IGNORE]
|
|
52
|
-
//
|
|
53
|
-
//
|
|
51
|
+
// [IGNORE] A `SignedProposerPreferences` matching `bid.slot` and the bid's branch has been
|
|
52
|
+
// seen — i.e. `proposal_slot == bid.slot` AND `dependent_root ==
|
|
53
|
+
// get_proposer_dependent_root(parent_state, compute_epoch_at_slot(bid.slot))`,
|
|
54
|
+
// where `parent_state` is the post-state of `bid.parent_block_root`.
|
|
55
|
+
// This is the message referenced as `proposer_preferences` in the following REJECT rules.
|
|
56
|
+
// TODO GLOAS: Implement once a ProposerPreferencesPool exists.
|
|
54
57
|
|
|
55
58
|
// [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
|
|
56
59
|
// `is_active_builder(state, bid.builder_index)` returns `True`.
|
|
@@ -71,11 +74,11 @@ async function validateExecutionPayloadBid(
|
|
|
71
74
|
});
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
// [REJECT] `bid.fee_recipient
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
// TODO GLOAS: Implement
|
|
77
|
+
// [REJECT] `bid.fee_recipient == proposer_preferences.fee_recipient`.
|
|
78
|
+
// [REJECT] `bid.gas_limit == proposer_preferences.gas_limit`.
|
|
79
|
+
// Both compared against the matching `proposer_preferences` defined above (same branch
|
|
80
|
+
// via dependent_root, same proposal_slot).
|
|
81
|
+
// TODO GLOAS: Implement once a ProposerPreferencesPool exists.
|
|
79
82
|
|
|
80
83
|
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
81
84
|
// consensus layer -- i.e. validate that
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
|
+
import {
|
|
3
|
+
computeEpochAtSlot,
|
|
4
|
+
createSingleSignatureSetFromComponents,
|
|
5
|
+
getProposerPreferencesSigningRoot,
|
|
6
|
+
} from "@lodestar/state-transition";
|
|
7
|
+
import {ValidatorIndex, gloas} from "@lodestar/types";
|
|
8
|
+
import {toRootHex} from "@lodestar/utils";
|
|
9
|
+
import {GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode} from "../errors/index.js";
|
|
10
|
+
import {IBeaconChain} from "../index.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validates a gossiped `SignedProposerPreferences` per
|
|
14
|
+
* https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md#proposer_preferences
|
|
15
|
+
*/
|
|
16
|
+
export async function validateGossipProposerPreferences(
|
|
17
|
+
chain: IBeaconChain,
|
|
18
|
+
signedProposerPreferences: gloas.SignedProposerPreferences
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const preferences = signedProposerPreferences.message;
|
|
21
|
+
const {proposalSlot, validatorIndex, dependentRoot} = preferences;
|
|
22
|
+
const dependentRootHex = toRootHex(dependentRoot);
|
|
23
|
+
const proposalEpoch = computeEpochAtSlot(proposalSlot);
|
|
24
|
+
|
|
25
|
+
// [IGNORE] `preferences.proposal_slot` is in the current or next epoch.
|
|
26
|
+
const currentEpoch = chain.clock.currentEpoch;
|
|
27
|
+
if (proposalEpoch < currentEpoch || proposalEpoch > currentEpoch + 1) {
|
|
28
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
29
|
+
code: ProposerPreferencesErrorCode.INVALID_EPOCH,
|
|
30
|
+
proposalSlot,
|
|
31
|
+
currentEpoch,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// [IGNORE] `preferences.proposal_slot` has not already passed.
|
|
36
|
+
const currentSlot = chain.clock.currentSlot;
|
|
37
|
+
if (proposalSlot <= currentSlot) {
|
|
38
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
39
|
+
code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED,
|
|
40
|
+
proposalSlot,
|
|
41
|
+
currentSlot,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// [IGNORE] The block with root `dependent_root` has been seen by the node.
|
|
46
|
+
// Resolve the proposer lookahead for the message's branch via head state (fast path) or
|
|
47
|
+
// the previous-root checkpoint state (populated by `processSlotsToNearestCheckpoint` for
|
|
48
|
+
// any imported branch crossing into `proposalEpoch - 1`). The head-state path also handles
|
|
49
|
+
// narrow timing windows where the checkpoint state isn't yet populated.
|
|
50
|
+
const headState = chain.getHeadState();
|
|
51
|
+
let proposers: ValidatorIndex[] | null = null;
|
|
52
|
+
if (headState.epoch === proposalEpoch && headState.currentDecisionRoot === dependentRootHex) {
|
|
53
|
+
proposers = headState.currentProposers;
|
|
54
|
+
} else if (headState.epoch === proposalEpoch - 1 && headState.nextDecisionRoot === dependentRootHex) {
|
|
55
|
+
proposers = headState.nextProposers;
|
|
56
|
+
} else {
|
|
57
|
+
// Sync lookup only to not trigger disk reload from gossip input.
|
|
58
|
+
const checkpointState = chain.regen.getCheckpointStateSync({epoch: proposalEpoch - 1, rootHex: dependentRootHex});
|
|
59
|
+
if (checkpointState !== null) {
|
|
60
|
+
// State is at `proposalEpoch - 1`, so proposers for `proposalSlot` (next epoch from
|
|
61
|
+
// the state's perspective) live in `nextProposers`.
|
|
62
|
+
proposers = checkpointState.nextProposers;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (proposers === null) {
|
|
66
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
67
|
+
code: ProposerPreferencesErrorCode.UNKNOWN_DEPENDENT_ROOT,
|
|
68
|
+
proposalSlot,
|
|
69
|
+
dependentRoot: dependentRootHex,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// [REJECT] `is_valid_proposal_slot(state, preferences)` returns True.
|
|
74
|
+
if (proposers[proposalSlot % SLOTS_PER_EPOCH] !== validatorIndex) {
|
|
75
|
+
throw new ProposerPreferencesError(GossipAction.REJECT, {
|
|
76
|
+
code: ProposerPreferencesErrorCode.INVALID_PROPOSER,
|
|
77
|
+
proposalSlot,
|
|
78
|
+
validatorIndex,
|
|
79
|
+
dependentRoot: dependentRootHex,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// [IGNORE] First valid message for (dependent_root, proposal_slot, validator_index).
|
|
84
|
+
if (chain.seenProposerPreferences.isKnown(dependentRootHex, proposalSlot, validatorIndex)) {
|
|
85
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
86
|
+
code: ProposerPreferencesErrorCode.ALREADY_KNOWN,
|
|
87
|
+
proposalSlot,
|
|
88
|
+
validatorIndex,
|
|
89
|
+
dependentRoot: dependentRootHex,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// [REJECT] `signed_proposer_preferences.signature` is valid with respect to the validator's public key.
|
|
94
|
+
const signatureSet = createSingleSignatureSetFromComponents(
|
|
95
|
+
chain.pubkeyCache.getOrThrow(validatorIndex),
|
|
96
|
+
getProposerPreferencesSigningRoot(chain.config, preferences),
|
|
97
|
+
signedProposerPreferences.signature
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true}))) {
|
|
101
|
+
throw new ProposerPreferencesError(GossipAction.REJECT, {
|
|
102
|
+
code: ProposerPreferencesErrorCode.INVALID_SIGNATURE,
|
|
103
|
+
proposalSlot,
|
|
104
|
+
validatorIndex,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Valid
|
|
109
|
+
chain.seenProposerPreferences.add(dependentRootHex, proposalSlot, validatorIndex);
|
|
110
|
+
}
|
|
@@ -41,6 +41,7 @@ export enum GossipType {
|
|
|
41
41
|
execution_payload = "execution_payload",
|
|
42
42
|
payload_attestation_message = "payload_attestation_message",
|
|
43
43
|
execution_payload_bid = "execution_payload_bid",
|
|
44
|
+
proposer_preferences = "proposer_preferences",
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export type SequentialGossipType = Exclude<GossipType, GossipType.beacon_attestation>;
|
|
@@ -78,6 +79,7 @@ export type GossipTopicTypeMap = {
|
|
|
78
79
|
[GossipType.execution_payload]: {type: GossipType.execution_payload};
|
|
79
80
|
[GossipType.payload_attestation_message]: {type: GossipType.payload_attestation_message};
|
|
80
81
|
[GossipType.execution_payload_bid]: {type: GossipType.execution_payload_bid};
|
|
82
|
+
[GossipType.proposer_preferences]: {type: GossipType.proposer_preferences};
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
export type GossipTopicMap = {
|
|
@@ -110,6 +112,7 @@ export type GossipTypeMap = {
|
|
|
110
112
|
[GossipType.execution_payload]: gloas.SignedExecutionPayloadEnvelope;
|
|
111
113
|
[GossipType.payload_attestation_message]: gloas.PayloadAttestationMessage;
|
|
112
114
|
[GossipType.execution_payload_bid]: gloas.SignedExecutionPayloadBid;
|
|
115
|
+
[GossipType.proposer_preferences]: gloas.SignedProposerPreferences;
|
|
113
116
|
};
|
|
114
117
|
|
|
115
118
|
export type GossipFnByType = {
|
|
@@ -141,6 +144,9 @@ export type GossipFnByType = {
|
|
|
141
144
|
payloadAttestationMessage: gloas.PayloadAttestationMessage
|
|
142
145
|
) => Promise<void> | void;
|
|
143
146
|
[GossipType.execution_payload_bid]: (executionPayloadBid: gloas.SignedExecutionPayloadBid) => Promise<void> | void;
|
|
147
|
+
[GossipType.proposer_preferences]: (
|
|
148
|
+
signedProposerPreferences: gloas.SignedProposerPreferences
|
|
149
|
+
) => Promise<void> | void;
|
|
144
150
|
};
|
|
145
151
|
|
|
146
152
|
export type GossipFn = GossipFnByType[keyof GossipFnByType];
|
|
@@ -27,6 +27,7 @@ const BLS_TO_EXECUTION_CHANGE_WEIGHT = 0.05;
|
|
|
27
27
|
const EXECUTION_PAYLOAD_WEIGHT = 0.5;
|
|
28
28
|
const PAYLOAD_ATTESTATION_WEIGHT = 0.05;
|
|
29
29
|
const EXECUTION_PAYLOAD_BID_WEIGHT = 0.05;
|
|
30
|
+
const PROPOSER_PREFERENCES_WEIGHT = 0.05;
|
|
30
31
|
|
|
31
32
|
const beaconAttestationSubnetWeight = 1 / ATTESTATION_SUBNET_COUNT;
|
|
32
33
|
const maxPositiveScore =
|
|
@@ -40,7 +41,8 @@ const maxPositiveScore =
|
|
|
40
41
|
BLS_TO_EXECUTION_CHANGE_WEIGHT +
|
|
41
42
|
EXECUTION_PAYLOAD_WEIGHT +
|
|
42
43
|
PAYLOAD_ATTESTATION_WEIGHT +
|
|
43
|
-
EXECUTION_PAYLOAD_BID_WEIGHT
|
|
44
|
+
EXECUTION_PAYLOAD_BID_WEIGHT +
|
|
45
|
+
PROPOSER_PREFERENCES_WEIGHT);
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
48
|
* The following params is implemented by Lighthouse at
|
|
@@ -198,6 +200,17 @@ function getAllTopicsScoreParams(
|
|
|
198
200
|
expectedMessageRate: 1024, // TODO GLOAS: Need an estimate for this
|
|
199
201
|
firstMessageDecayTime: epochDurationMs * 100,
|
|
200
202
|
});
|
|
203
|
+
topicsParams[
|
|
204
|
+
stringifyGossipTopic(config, {
|
|
205
|
+
type: GossipType.proposer_preferences,
|
|
206
|
+
boundary,
|
|
207
|
+
})
|
|
208
|
+
] = getTopicScoreParams(config, precomputedParams, {
|
|
209
|
+
topicWeight: PROPOSER_PREFERENCES_WEIGHT,
|
|
210
|
+
// Upper bound ~64 messages per epoch: one per proposer across the current + next epoch.
|
|
211
|
+
expectedMessageRate: 64 / SLOTS_PER_EPOCH,
|
|
212
|
+
firstMessageDecayTime: epochDurationMs * 100,
|
|
213
|
+
});
|
|
201
214
|
|
|
202
215
|
// other topics
|
|
203
216
|
topicsParams[
|
|
@@ -73,6 +73,7 @@ function stringifyGossipTopicType(topic: GossipTopic): string {
|
|
|
73
73
|
case GossipType.execution_payload:
|
|
74
74
|
case GossipType.payload_attestation_message:
|
|
75
75
|
case GossipType.execution_payload_bid:
|
|
76
|
+
case GossipType.proposer_preferences:
|
|
76
77
|
return topic.type;
|
|
77
78
|
case GossipType.beacon_attestation:
|
|
78
79
|
case GossipType.sync_committee:
|
|
@@ -124,6 +125,8 @@ export function getGossipSSZType(topic: GossipTopic) {
|
|
|
124
125
|
return ssz.gloas.PayloadAttestationMessage;
|
|
125
126
|
case GossipType.execution_payload_bid:
|
|
126
127
|
return ssz.gloas.SignedExecutionPayloadBid;
|
|
128
|
+
case GossipType.proposer_preferences:
|
|
129
|
+
return ssz.gloas.SignedProposerPreferences;
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
|
|
@@ -203,6 +206,7 @@ export function parseGossipTopic(forkDigestContext: ForkDigestContext, topicStr:
|
|
|
203
206
|
case GossipType.execution_payload:
|
|
204
207
|
case GossipType.payload_attestation_message:
|
|
205
208
|
case GossipType.execution_payload_bid:
|
|
209
|
+
case GossipType.proposer_preferences:
|
|
206
210
|
return {type: gossipTypeStr, boundary, encoding};
|
|
207
211
|
}
|
|
208
212
|
|
|
@@ -257,6 +261,7 @@ export function getCoreTopicsAtFork(
|
|
|
257
261
|
topics.push({type: GossipType.execution_payload});
|
|
258
262
|
topics.push({type: GossipType.payload_attestation_message});
|
|
259
263
|
topics.push({type: GossipType.execution_payload_bid});
|
|
264
|
+
topics.push({type: GossipType.proposer_preferences});
|
|
260
265
|
}
|
|
261
266
|
|
|
262
267
|
// After fulu also track data_column_sidecar_{index}
|
|
@@ -351,4 +356,5 @@ export const gossipTopicIgnoreDuplicatePublishError: Record<GossipType, boolean>
|
|
|
351
356
|
[GossipType.execution_payload]: true,
|
|
352
357
|
[GossipType.payload_attestation_message]: true,
|
|
353
358
|
[GossipType.execution_payload_bid]: true,
|
|
359
|
+
[GossipType.proposer_preferences]: true,
|
|
354
360
|
};
|
package/src/network/interface.ts
CHANGED
|
@@ -112,6 +112,7 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
112
112
|
publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
|
|
113
113
|
publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
|
|
114
114
|
publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
|
|
115
|
+
publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number>;
|
|
115
116
|
|
|
116
117
|
// Debug
|
|
117
118
|
dumpGossipQueue(gossipType: GossipType): Promise<PendingGossipsubMessage[]>;
|
package/src/network/network.ts
CHANGED
|
@@ -515,6 +515,17 @@ export class Network implements INetwork {
|
|
|
515
515
|
);
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
async publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number> {
|
|
519
|
+
const epoch = computeEpochAtSlot(payloadAttestationMessage.data.slot);
|
|
520
|
+
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
521
|
+
|
|
522
|
+
return this.publishGossip<GossipType.payload_attestation_message>(
|
|
523
|
+
{type: GossipType.payload_attestation_message, boundary},
|
|
524
|
+
payloadAttestationMessage,
|
|
525
|
+
{ignoreDuplicatePublishError: true}
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
518
529
|
private async publishGossip<K extends GossipType>(
|
|
519
530
|
topic: GossipTopicMap[K],
|
|
520
531
|
object: GossipTypeMap[K],
|