@lodestar/beacon-node 1.43.0-dev.433e692fd9 → 1.43.0-dev.4358217e12
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 +7 -9
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -2
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +0 -4
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts +1 -1
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js +1 -2
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +4 -4
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +9 -10
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +26 -13
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +86 -84
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +5 -3
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +28 -10
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +7 -5
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +14 -20
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +81 -12
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +3 -2
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +30 -5
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +15 -4
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +24 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +76 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +8 -6
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +19 -6
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +16 -4
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +8 -1
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -0
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
- package/lib/chain/errors/executionPayloadEnvelope.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/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +11 -19
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +7 -5
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +2 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +4 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +46 -16
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +56 -26
- 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 +11 -4
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -18
- 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/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +1 -0
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +13 -1
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +19 -9
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +4 -3
- package/lib/chain/validation/payloadAttestationMessage.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 +69 -0
- package/lib/chain/validation/proposerPreferences.js.map +1 -0
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +21 -14
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -0
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +6 -0
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +20 -0
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +18 -0
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- 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 +11 -2
- 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/network.js +1 -1
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +32 -12
- 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/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +4 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/notifier.js +7 -1
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/range/batch.d.ts +12 -2
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +56 -30
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +6 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +4 -3
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +17 -6
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/types.d.ts +34 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +34 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +24 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +649 -53
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +46 -10
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +147 -24
- 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 +6 -2
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.js +0 -9
- package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +16 -3
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +17 -16
- package/src/api/impl/beacon/blocks/index.ts +9 -9
- package/src/api/impl/beacon/state/utils.ts +2 -2
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +0 -4
- package/src/chain/GetBlobsTracker.ts +1 -2
- package/src/chain/archiveStore/archiveStore.ts +5 -5
- package/src/chain/archiveStore/interface.ts +4 -4
- package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +4 -4
- package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
- package/src/chain/blocks/importBlock.ts +7 -13
- package/src/chain/blocks/importExecutionPayload.ts +106 -103
- package/src/chain/blocks/index.ts +44 -13
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +7 -6
- package/src/chain/blocks/types.ts +14 -25
- package/src/chain/blocks/utils/chainSegment.ts +106 -17
- package/src/chain/blocks/verifyBlock.ts +35 -6
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -7
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +129 -0
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
- package/src/chain/chain.ts +36 -19
- package/src/chain/emitter.ts +15 -3
- package/src/chain/errors/blockError.ts +4 -1
- package/src/chain/errors/executionPayloadBid.ts +6 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/proposerPreferences.ts +39 -0
- package/src/chain/forkChoice/index.ts +8 -24
- package/src/chain/interface.ts +11 -3
- package/src/chain/opPools/payloadAttestationPool.ts +4 -8
- package/src/chain/prepareNextSlot.ts +60 -17
- package/src/chain/produceBlock/produceBlockBody.ts +77 -23
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
- package/src/chain/seenCache/seenProposerPreferences.ts +29 -0
- package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
- package/src/chain/validation/block.ts +1 -0
- package/src/chain/validation/executionPayloadBid.ts +14 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +20 -10
- package/src/chain/validation/payloadAttestationMessage.ts +5 -3
- package/src/chain/validation/proposerPreferences.ts +91 -0
- package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
- package/src/execution/engine/http.ts +21 -14
- package/src/execution/engine/interface.ts +1 -0
- package/src/execution/engine/mock.ts +8 -1
- package/src/execution/engine/types.ts +41 -0
- package/src/metrics/metrics/lodestar.ts +4 -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/network.ts +1 -1
- package/src/network/processor/gossipHandlers.ts +41 -16
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +1 -0
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
- package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
- package/src/node/nodejs.ts +4 -2
- package/src/node/notifier.ts +8 -1
- package/src/sync/range/batch.ts +90 -35
- package/src/sync/range/chain.ts +13 -5
- package/src/sync/range/range.ts +18 -6
- package/src/sync/types.ts +72 -0
- package/src/sync/unknownBlock.ts +810 -57
- package/src/sync/utils/downloadByRange.ts +256 -39
- package/src/sync/utils/downloadByRoot.ts +12 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
- package/src/util/sszBytes.ts +21 -3
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
IBeaconStateView,
|
|
20
20
|
type IBeaconStateViewBellatrix,
|
|
21
21
|
computeTimeAtSlot,
|
|
22
|
-
isParentBlockFull,
|
|
23
22
|
isStatePostBellatrix,
|
|
24
23
|
isStatePostCapella,
|
|
25
24
|
isStatePostGloas,
|
|
@@ -47,9 +46,10 @@ import {
|
|
|
47
46
|
electra,
|
|
48
47
|
fulu,
|
|
49
48
|
gloas,
|
|
49
|
+
ssz,
|
|
50
50
|
} from "@lodestar/types";
|
|
51
|
-
import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
52
|
-
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
51
|
+
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
52
|
+
import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js";
|
|
53
53
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
54
54
|
import {
|
|
55
55
|
IExecutionBuilder,
|
|
@@ -214,15 +214,30 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
// Get execution payload from EL
|
|
217
|
+
const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
|
|
218
|
+
let parentBlockHash = isExtendingPayload
|
|
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)) {
|
|
225
|
+
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
226
|
+
}
|
|
227
|
+
const parentExecutionRequests = isExtendingPayload
|
|
228
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
229
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
217
230
|
const prepareRes = await prepareExecutionPayload(
|
|
218
231
|
this,
|
|
219
232
|
this.logger,
|
|
220
233
|
fork,
|
|
221
234
|
parentBlockRoot,
|
|
235
|
+
parentBlockHash,
|
|
222
236
|
safeBlockHash,
|
|
223
237
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
224
238
|
currentState,
|
|
225
|
-
feeRecipient
|
|
239
|
+
feeRecipient,
|
|
240
|
+
parentExecutionRequests
|
|
226
241
|
);
|
|
227
242
|
|
|
228
243
|
const {prepType, payloadId} = prepareRes;
|
|
@@ -255,8 +270,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
255
270
|
|
|
256
271
|
// Create self-build execution payload bid
|
|
257
272
|
const bid: gloas.ExecutionPayloadBid = {
|
|
258
|
-
parentBlockHash
|
|
259
|
-
parentBlockRoot
|
|
273
|
+
parentBlockHash,
|
|
274
|
+
parentBlockRoot,
|
|
260
275
|
blockHash: executionPayload.blockHash,
|
|
261
276
|
prevRandao: currentState.getRandaoMix(currentState.epoch),
|
|
262
277
|
feeRecipient: executionPayload.feeRecipient,
|
|
@@ -266,6 +281,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
266
281
|
value: 0,
|
|
267
282
|
executionPayment: 0,
|
|
268
283
|
blobKzgCommitments: blobsBundle.commitments,
|
|
284
|
+
executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
|
|
269
285
|
};
|
|
270
286
|
const signedBid: gloas.SignedExecutionPayloadBid = {
|
|
271
287
|
message: bid,
|
|
@@ -275,8 +291,11 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
275
291
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
276
292
|
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
277
293
|
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
278
|
-
|
|
279
|
-
|
|
294
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
295
|
+
parentBlock.blockRoot,
|
|
296
|
+
blockSlot - 1
|
|
297
|
+
);
|
|
298
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
280
299
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
281
300
|
|
|
282
301
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -334,6 +353,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
334
353
|
this.logger,
|
|
335
354
|
fork,
|
|
336
355
|
parentBlockRoot,
|
|
356
|
+
currentState.latestExecutionPayloadHeader.blockHash,
|
|
337
357
|
safeBlockHash,
|
|
338
358
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
339
359
|
currentState,
|
|
@@ -442,6 +462,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
442
462
|
this.logger,
|
|
443
463
|
fork,
|
|
444
464
|
parentBlockRoot,
|
|
465
|
+
currentState.latestExecutionPayloadHeader.blockHash,
|
|
445
466
|
safeBlockHash,
|
|
446
467
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
447
468
|
currentState,
|
|
@@ -607,17 +628,18 @@ export async function prepareExecutionPayload(
|
|
|
607
628
|
logger: Logger,
|
|
608
629
|
fork: ForkPostBellatrix,
|
|
609
630
|
parentBlockRoot: Root,
|
|
631
|
+
parentBlockHash: Bytes32,
|
|
610
632
|
safeBlockHash: RootHex,
|
|
611
633
|
finalizedBlockHash: RootHex,
|
|
612
634
|
state: IBeaconStateViewBellatrix,
|
|
613
|
-
suggestedFeeRecipient: string
|
|
635
|
+
suggestedFeeRecipient: string,
|
|
636
|
+
parentExecutionRequests?: electra.ExecutionRequests
|
|
614
637
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
615
|
-
const parentHash = state.latestBlockHash;
|
|
616
638
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
617
639
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
618
640
|
|
|
619
641
|
const payloadIdCached = chain.executionEngine.payloadIdCache.get({
|
|
620
|
-
headBlockHash: toRootHex(
|
|
642
|
+
headBlockHash: toRootHex(parentBlockHash),
|
|
621
643
|
finalizedBlockHash,
|
|
622
644
|
timestamp: numToQuantity(timestamp),
|
|
623
645
|
prevRandao: toHex(prevRandao),
|
|
@@ -646,12 +668,14 @@ export async function prepareExecutionPayload(
|
|
|
646
668
|
prepareState: state,
|
|
647
669
|
prepareSlot: state.slot,
|
|
648
670
|
parentBlockRoot,
|
|
671
|
+
parentBlockHash,
|
|
649
672
|
feeRecipient: suggestedFeeRecipient,
|
|
673
|
+
parentExecutionRequests,
|
|
650
674
|
});
|
|
651
675
|
|
|
652
676
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
653
677
|
fork,
|
|
654
|
-
toRootHex(
|
|
678
|
+
toRootHex(parentBlockHash),
|
|
655
679
|
safeBlockHash,
|
|
656
680
|
finalizedBlockHash,
|
|
657
681
|
attributes
|
|
@@ -703,20 +727,33 @@ export function getPayloadAttributesForSSE(
|
|
|
703
727
|
prepareState,
|
|
704
728
|
prepareSlot,
|
|
705
729
|
parentBlockRoot,
|
|
730
|
+
parentBlockHash,
|
|
706
731
|
feeRecipient,
|
|
707
|
-
|
|
732
|
+
parentExecutionRequests,
|
|
733
|
+
}: {
|
|
734
|
+
prepareState: IBeaconStateViewBellatrix;
|
|
735
|
+
prepareSlot: Slot;
|
|
736
|
+
parentBlockRoot: Root;
|
|
737
|
+
parentBlockHash: Bytes32;
|
|
738
|
+
feeRecipient: string;
|
|
739
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
740
|
+
}
|
|
708
741
|
): SSEPayloadAttributes {
|
|
709
|
-
const parentHash = prepareState.latestBlockHash;
|
|
710
742
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
711
743
|
prepareState,
|
|
712
744
|
prepareSlot,
|
|
713
745
|
parentBlockRoot,
|
|
746
|
+
parentBlockHash,
|
|
714
747
|
feeRecipient,
|
|
748
|
+
parentExecutionRequests,
|
|
715
749
|
});
|
|
716
750
|
|
|
717
751
|
let parentBlockNumber: number;
|
|
718
752
|
if (isForkPostGloas(fork)) {
|
|
719
|
-
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
753
|
+
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
754
|
+
toRootHex(parentBlockRoot),
|
|
755
|
+
toRootHex(parentBlockHash)
|
|
756
|
+
);
|
|
720
757
|
if (parentBlock?.executionPayloadBlockHash == null) {
|
|
721
758
|
throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
|
|
722
759
|
}
|
|
@@ -730,7 +767,7 @@ export function getPayloadAttributesForSSE(
|
|
|
730
767
|
proposalSlot: prepareSlot,
|
|
731
768
|
parentBlockNumber,
|
|
732
769
|
parentBlockRoot,
|
|
733
|
-
parentBlockHash
|
|
770
|
+
parentBlockHash,
|
|
734
771
|
payloadAttributes,
|
|
735
772
|
};
|
|
736
773
|
return ssePayloadAttributes;
|
|
@@ -745,12 +782,16 @@ function preparePayloadAttributes(
|
|
|
745
782
|
prepareState,
|
|
746
783
|
prepareSlot,
|
|
747
784
|
parentBlockRoot,
|
|
785
|
+
parentBlockHash,
|
|
748
786
|
feeRecipient,
|
|
787
|
+
parentExecutionRequests,
|
|
749
788
|
}: {
|
|
750
789
|
prepareState: IBeaconStateViewBellatrix;
|
|
751
790
|
prepareSlot: Slot;
|
|
752
791
|
parentBlockRoot: Root;
|
|
792
|
+
parentBlockHash: Bytes32;
|
|
753
793
|
feeRecipient: string;
|
|
794
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
754
795
|
}
|
|
755
796
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
756
797
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -766,13 +807,22 @@ function preparePayloadAttributes(
|
|
|
766
807
|
throw new Error("Expected Capella state for withdrawals");
|
|
767
808
|
}
|
|
768
809
|
|
|
769
|
-
if (isStatePostGloas(prepareState)
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
810
|
+
if (isStatePostGloas(prepareState)) {
|
|
811
|
+
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
812
|
+
if (isExtendingPayload) {
|
|
813
|
+
if (parentExecutionRequests === undefined) {
|
|
814
|
+
throw new Error("parentExecutionRequests required when extending full parent");
|
|
815
|
+
}
|
|
816
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
817
|
+
prepareState.getExpectedWithdrawalsForFullParent(parentExecutionRequests);
|
|
818
|
+
} else {
|
|
819
|
+
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
820
|
+
// already deducted from CL balances but never credited on the EL (the envelope
|
|
821
|
+
// was not delivered). The next payload must carry those same withdrawals to
|
|
822
|
+
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
823
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
824
|
+
prepareState.payloadExpectedWithdrawals;
|
|
825
|
+
}
|
|
776
826
|
} else {
|
|
777
827
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
778
828
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
@@ -784,6 +834,10 @@ function preparePayloadAttributes(
|
|
|
784
834
|
(payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot;
|
|
785
835
|
}
|
|
786
836
|
|
|
837
|
+
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
838
|
+
(payloadAttributes as gloas.SSEPayloadAttributes["payloadAttributes"]).slotNumber = prepareSlot;
|
|
839
|
+
}
|
|
840
|
+
|
|
787
841
|
return payloadAttributes;
|
|
788
842
|
}
|
|
789
843
|
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
2
2
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
|
-
import {RootHex} from "@lodestar/types";
|
|
3
|
+
import {RootHex, Slot} from "@lodestar/types";
|
|
4
4
|
import {Logger} from "@lodestar/utils";
|
|
5
5
|
import {Metrics} from "../../metrics/metrics.js";
|
|
6
6
|
import {SerializedCache} from "../../util/serializedCache.js";
|
|
@@ -21,8 +21,15 @@ export type SeenPayloadEnvelopeInputModules = {
|
|
|
21
21
|
/**
|
|
22
22
|
* Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
|
|
23
23
|
*
|
|
24
|
-
* Created during block import when a block is processed.
|
|
25
|
-
*
|
|
24
|
+
* Created during block import when a block is processed. Two pruning paths:
|
|
25
|
+
* - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build
|
|
26
|
+
* on is known.
|
|
27
|
+
* - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup.
|
|
28
|
+
*
|
|
29
|
+
* Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
|
|
30
|
+
* (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
|
|
31
|
+
* transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
|
|
32
|
+
* ticks; subsequent ticks settle it back.
|
|
26
33
|
*/
|
|
27
34
|
export class SeenPayloadEnvelopeInput {
|
|
28
35
|
private readonly chainEvents: ChainEventEmitter;
|
|
@@ -58,16 +65,7 @@ export class SeenPayloadEnvelopeInput {
|
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
private onFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
61
|
-
|
|
62
|
-
const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
|
|
63
|
-
let deletedCount = 0;
|
|
64
|
-
for (const [, input] of this.payloadInputs) {
|
|
65
|
-
if (input.slot < finalizedSlot) {
|
|
66
|
-
this.evictPayloadInput(input);
|
|
67
|
-
deletedCount++;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
|
|
68
|
+
this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
|
|
71
69
|
};
|
|
72
70
|
|
|
73
71
|
add(props: CreateFromBlockProps): PayloadEnvelopeInput {
|
|
@@ -88,17 +86,21 @@ export class SeenPayloadEnvelopeInput {
|
|
|
88
86
|
return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false;
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
prune(blockRootHex: RootHex): void {
|
|
92
|
-
const payloadInput = this.payloadInputs.get(blockRootHex);
|
|
93
|
-
if (payloadInput) {
|
|
94
|
-
this.evictPayloadInput(payloadInput);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
89
|
size(): number {
|
|
99
90
|
return this.payloadInputs.size;
|
|
100
91
|
}
|
|
101
92
|
|
|
93
|
+
pruneBelow(slot: Slot): void {
|
|
94
|
+
let deletedCount = 0;
|
|
95
|
+
for (const [, input] of this.payloadInputs) {
|
|
96
|
+
if (input.slot < slot) {
|
|
97
|
+
this.evictPayloadInput(input);
|
|
98
|
+
deletedCount++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.logger?.debug("SeenPayloadEnvelopeInput.pruneBelow deleted entries", {slot, deletedCount});
|
|
102
|
+
}
|
|
103
|
+
|
|
102
104
|
private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
|
|
103
105
|
this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
|
|
104
106
|
this.payloadInputs.delete(payloadInput.blockRootHex);
|
|
@@ -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
|
+
}
|
|
@@ -226,7 +226,10 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
|
|
226
226
|
}
|
|
227
227
|
sszTimer?.();
|
|
228
228
|
const timer = this.metrics?.cpStateCache.stateReloadDuration.startTimer();
|
|
229
|
-
|
|
229
|
+
// preload validators and balances for faster state transition
|
|
230
|
+
const newCachedState = seedState.loadOtherState(stateBytes, validatorsBytes, {
|
|
231
|
+
preloadValidatorsAndBalances: true,
|
|
232
|
+
});
|
|
230
233
|
// hashTreeRoot() calls the commit() inside
|
|
231
234
|
// there is no modification inside the state, it's just that we want to compute and cache all roots
|
|
232
235
|
const stateRoot = toRootHex(newCachedState.hashTreeRoot());
|
|
@@ -103,6 +103,7 @@ export async function validateGossipBlock(
|
|
|
103
103
|
if (chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHashHex) === null) {
|
|
104
104
|
throw new BlockGossipError(GossipAction.IGNORE, {
|
|
105
105
|
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
106
|
+
parentRoot,
|
|
106
107
|
parentBlockHash: parentBlockHashHex,
|
|
107
108
|
});
|
|
108
109
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
2
|
import {
|
|
3
|
+
computeEpochAtSlot,
|
|
3
4
|
createSingleSignatureSetFromComponents,
|
|
4
5
|
getExecutionPayloadBidSigningRoot,
|
|
5
6
|
isActiveBuilder,
|
|
@@ -76,6 +77,19 @@ async function validateExecutionPayloadBid(
|
|
|
76
77
|
// `SignedProposerPreferences` associated with `bid.slot`.
|
|
77
78
|
// TODO GLOAS: Implement this along with proposer preference
|
|
78
79
|
|
|
80
|
+
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
81
|
+
// consensus layer -- i.e. validate that
|
|
82
|
+
// `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
|
|
83
|
+
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
84
|
+
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
85
|
+
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
86
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
87
|
+
code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
|
|
88
|
+
blobKzgCommitmentsLen,
|
|
89
|
+
commitmentLimit: maxBlobsPerBlock,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
// [IGNORE] this is the first signed bid seen with a valid signature from the given builder for this slot.
|
|
80
94
|
if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex)) {
|
|
81
95
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
getExecutionPayloadEnvelopeSignatureSet,
|
|
5
5
|
isStatePostGloas,
|
|
6
6
|
} from "@lodestar/state-transition";
|
|
7
|
-
import {gloas} from "@lodestar/types";
|
|
8
|
-
import {toRootHex} from "@lodestar/utils";
|
|
7
|
+
import {gloas, ssz} from "@lodestar/types";
|
|
8
|
+
import {byteArrayEquals, toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
11
|
import {RegenCaller} from "../regen/index.js";
|
|
@@ -53,7 +53,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
53
53
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
54
54
|
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
|
|
55
55
|
blockRoot: blockRootHex,
|
|
56
|
-
slot:
|
|
56
|
+
slot: payload.slotNumber,
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -65,13 +65,13 @@ async function validateExecutionPayloadEnvelope(
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `
|
|
68
|
+
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `payload.slotNumber >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
|
|
69
69
|
const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
|
|
70
70
|
const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
|
|
71
|
-
if (
|
|
71
|
+
if (payload.slotNumber < finalizedSlot) {
|
|
72
72
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
73
73
|
code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK,
|
|
74
|
-
envelopeSlot:
|
|
74
|
+
envelopeSlot: payload.slotNumber,
|
|
75
75
|
finalizedSlot,
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -80,11 +80,11 @@ async function validateExecutionPayloadEnvelope(
|
|
|
80
80
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
81
81
|
// it is possible that the block didn't pass the validation
|
|
82
82
|
|
|
83
|
-
// [REJECT] `block.slot` equals `
|
|
84
|
-
if (block.slot !==
|
|
83
|
+
// [REJECT] `block.slot` equals `payload.slotNumber`.
|
|
84
|
+
if (block.slot !== payload.slotNumber) {
|
|
85
85
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
86
86
|
code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
|
|
87
|
-
envelopeSlot:
|
|
87
|
+
envelopeSlot: payload.slotNumber,
|
|
88
88
|
blockSlot: block.slot,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
@@ -107,6 +107,16 @@ async function validateExecutionPayloadEnvelope(
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
|
|
111
|
+
const requestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
112
|
+
if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
|
|
113
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
114
|
+
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
|
|
115
|
+
envelopeRequestsRoot: toRootHex(requestsRoot),
|
|
116
|
+
bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
110
120
|
// Get the block state to verify the builder's signature.
|
|
111
121
|
const blockState = await chain.regen
|
|
112
122
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
@@ -114,7 +124,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
114
124
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
115
125
|
code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE,
|
|
116
126
|
blockRoot: blockRootHex,
|
|
117
|
-
slot:
|
|
127
|
+
slot: payload.slotNumber,
|
|
118
128
|
});
|
|
119
129
|
});
|
|
120
130
|
if (!isStatePostGloas(blockState)) {
|
|
@@ -18,7 +18,8 @@ export async function validateApiPayloadAttestationMessage(
|
|
|
18
18
|
chain: IBeaconChain,
|
|
19
19
|
payloadAttestationMessage: gloas.PayloadAttestationMessage
|
|
20
20
|
): Promise<PayloadAttestationValidationResult> {
|
|
21
|
-
|
|
21
|
+
const prioritizeBls = true;
|
|
22
|
+
return validatePayloadAttestationMessage(chain, payloadAttestationMessage, prioritizeBls);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export async function validateGossipPayloadAttestationMessage(
|
|
@@ -30,7 +31,8 @@ export async function validateGossipPayloadAttestationMessage(
|
|
|
30
31
|
|
|
31
32
|
async function validatePayloadAttestationMessage(
|
|
32
33
|
chain: IBeaconChain,
|
|
33
|
-
payloadAttestationMessage: gloas.PayloadAttestationMessage
|
|
34
|
+
payloadAttestationMessage: gloas.PayloadAttestationMessage,
|
|
35
|
+
prioritizeBls = false
|
|
34
36
|
): Promise<PayloadAttestationValidationResult> {
|
|
35
37
|
const {data, validatorIndex} = payloadAttestationMessage;
|
|
36
38
|
const epoch = computeEpochAtSlot(data.slot);
|
|
@@ -102,7 +104,7 @@ async function validatePayloadAttestationMessage(
|
|
|
102
104
|
payloadAttestationMessage.signature
|
|
103
105
|
);
|
|
104
106
|
|
|
105
|
-
if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
|
|
107
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true, priority: prioritizeBls}))) {
|
|
106
108
|
throw new PayloadAttestationError(GossipAction.REJECT, {
|
|
107
109
|
code: PayloadAttestationErrorCode.INVALID_SIGNATURE,
|
|
108
110
|
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
|
+
import {
|
|
3
|
+
computeEpochAtSlot,
|
|
4
|
+
createSingleSignatureSetFromComponents,
|
|
5
|
+
getProposerPreferencesSigningRoot,
|
|
6
|
+
isStatePostGloas,
|
|
7
|
+
} from "@lodestar/state-transition";
|
|
8
|
+
import {gloas} from "@lodestar/types";
|
|
9
|
+
import {GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode} from "../errors/index.js";
|
|
10
|
+
import {IBeaconChain} from "../index.js";
|
|
11
|
+
import {RegenCaller} from "../regen/index.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validates a gossiped `SignedProposerPreferences` per
|
|
15
|
+
* https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md#proposer_preferences
|
|
16
|
+
*/
|
|
17
|
+
export async function validateGossipProposerPreferences(
|
|
18
|
+
chain: IBeaconChain,
|
|
19
|
+
signedProposerPreferences: gloas.SignedProposerPreferences
|
|
20
|
+
): Promise<void> {
|
|
21
|
+
const preferences = signedProposerPreferences.message;
|
|
22
|
+
const {proposalSlot, validatorIndex} = preferences;
|
|
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
|
+
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipProposerPreferences);
|
|
46
|
+
if (!isStatePostGloas(state)) {
|
|
47
|
+
throw new Error(`Expected gloas+ state for proposer preferences validation, got fork=${state.forkName}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// [REJECT] `preferences.validator_index` is present at the correct slot in the current or next
|
|
51
|
+
// epoch's portion of `state.proposer_lookahead` — i.e. `is_valid_proposal_slot(state, preferences)`
|
|
52
|
+
// returns True.
|
|
53
|
+
const epochOffset = proposalEpoch - state.epoch;
|
|
54
|
+
const proposers = epochOffset === 0 ? state.currentProposers : state.nextProposers;
|
|
55
|
+
const expectedProposer = proposers[proposalSlot % SLOTS_PER_EPOCH];
|
|
56
|
+
if (epochOffset < 0 || epochOffset > 1 || expectedProposer !== validatorIndex) {
|
|
57
|
+
throw new ProposerPreferencesError(GossipAction.REJECT, {
|
|
58
|
+
code: ProposerPreferencesErrorCode.INVALID_PROPOSER,
|
|
59
|
+
proposalSlot,
|
|
60
|
+
validatorIndex,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// [IGNORE] The `signed_proposer_preferences` is the first valid message received from the validator
|
|
65
|
+
// with index `preferences.validator_index` and the given slot `preferences.proposal_slot`.
|
|
66
|
+
if (chain.seenProposerPreferences.isKnown(proposalSlot, validatorIndex)) {
|
|
67
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
68
|
+
code: ProposerPreferencesErrorCode.ALREADY_KNOWN,
|
|
69
|
+
proposalSlot,
|
|
70
|
+
validatorIndex,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// [REJECT] `signed_proposer_preferences.signature` is valid with respect to the validator's public key.
|
|
75
|
+
const signatureSet = createSingleSignatureSetFromComponents(
|
|
76
|
+
chain.pubkeyCache.getOrThrow(validatorIndex),
|
|
77
|
+
getProposerPreferencesSigningRoot(chain.config, preferences),
|
|
78
|
+
signedProposerPreferences.signature
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true}))) {
|
|
82
|
+
throw new ProposerPreferencesError(GossipAction.REJECT, {
|
|
83
|
+
code: ProposerPreferencesErrorCode.INVALID_SIGNATURE,
|
|
84
|
+
proposalSlot,
|
|
85
|
+
validatorIndex,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Valid
|
|
90
|
+
chain.seenProposerPreferences.add(proposalSlot, validatorIndex);
|
|
91
|
+
}
|
|
@@ -19,7 +19,7 @@ export class ExecutionPayloadEnvelopeArchiveRepository extends Repository<Slot,
|
|
|
19
19
|
* Id is the slot from the envelope
|
|
20
20
|
*/
|
|
21
21
|
getId(value: gloas.SignedExecutionPayloadEnvelope): Slot {
|
|
22
|
-
return value.message.
|
|
22
|
+
return value.message.payload.slotNumber;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
encodeKey(id: Slot): Uint8Array {
|