@lodestar/beacon-node 1.45.0 → 1.46.0-dev.18a1510f34
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 +186 -57
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +1 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +2 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +2 -3
- 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 +173 -89
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +6 -3
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +17 -3
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +2 -17
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +13 -5
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +0 -1
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +44 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +48 -6
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +25 -4
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +11 -4
- 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 +11 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +14 -1
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +2 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +11 -6
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +28 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/lightClient/types.d.ts +3 -1
- package/lib/chain/lightClient/types.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +2 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -4
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts +6 -5
- package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts.map +1 -1
- package/lib/chain/seenCache/seenExecutionPayloadBids.js +16 -8
- package/lib/chain/seenCache/seenExecutionPayloadBids.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +10 -6
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +10 -6
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/blobSidecar.js +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +53 -4
- 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 +100 -26
- 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 +29 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +9 -2
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +65 -13
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
- package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
- package/lib/chain/validatorMonitor.js +1 -1
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -1
- package/lib/execution/engine/types.js +1 -1
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/nodeJsMetrics.js +1 -1
- package/lib/metrics/nodeJsMetrics.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.js +2 -7
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/discv5/index.d.ts.map +1 -1
- package/lib/network/discv5/index.js +1 -4
- package/lib/network/discv5/index.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts +2 -2
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +15 -9
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +2 -2
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +79 -236
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +24 -1
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +4 -1
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/libp2p/index.d.ts.map +1 -1
- package/lib/network/libp2p/index.js +2 -9
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.d.ts +4 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +19 -11
- package/lib/network/network.js.map +1 -1
- package/lib/network/peers/datastore.d.ts +1 -1
- package/lib/network/peers/datastore.d.ts.map +1 -1
- package/lib/network/peers/datastore.js +1 -1
- package/lib/network/peers/datastore.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +12 -6
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +2 -1
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +20 -11
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientFinalityUpdate.js +1 -1
- package/lib/network/reqresp/handlers/lightClientFinalityUpdate.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientOptimisticUpdate.js +1 -1
- package/lib/network/reqresp/handlers/lightClientOptimisticUpdate.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +6 -2
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
- package/lib/network/util.d.ts +1 -0
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +4 -0
- package/lib/network/util.js.map +1 -1
- package/lib/sync/constants.d.ts +6 -5
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +6 -5
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +10 -1
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +28 -10
- 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 +30 -15
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/utils/batches.d.ts +18 -0
- package/lib/sync/range/utils/batches.d.ts.map +1 -1
- package/lib/sync/range/utils/batches.js +39 -0
- package/lib/sync/range/utils/batches.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +7 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +7 -2
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/execution.js +1 -1
- package/lib/util/execution.js.map +1 -1
- package/lib/util/profile.d.ts +3 -0
- package/lib/util/profile.d.ts.map +1 -1
- package/lib/util/profile.js +1 -27
- package/lib/util/profile.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/package.json +29 -38
- package/src/api/impl/beacon/blocks/index.ts +221 -63
- package/src/api/impl/config/constants.ts +2 -0
- package/src/api/impl/lodestar/index.ts +2 -3
- package/src/api/impl/validator/index.ts +207 -99
- package/src/api/rest/base.ts +8 -3
- package/src/chain/archiveStore/archiveStore.ts +20 -3
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +0 -1
- package/src/chain/blocks/importBlock.ts +0 -17
- package/src/chain/blocks/utils/chainSegment.ts +16 -5
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -3
- package/src/chain/bls/multithread/index.ts +0 -1
- package/src/chain/chain.ts +61 -2
- package/src/chain/errors/blockError.ts +44 -7
- package/src/chain/errors/executionPayloadBid.ts +12 -4
- package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
- package/src/chain/errors/proposerPreferences.ts +16 -1
- package/src/chain/lightClient/index.ts +12 -7
- package/src/chain/lightClient/proofs.ts +36 -1
- package/src/chain/lightClient/types.ts +3 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +4 -2
- package/src/chain/opPools/proposerPreferencesPool.ts +1 -1
- package/src/chain/prepareNextSlot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +5 -5
- package/src/chain/seenCache/seenExecutionPayloadBids.ts +22 -9
- package/src/chain/validation/aggregateAndProof.ts +12 -6
- package/src/chain/validation/attestation.ts +12 -6
- package/src/chain/validation/blobSidecar.ts +1 -1
- package/src/chain/validation/block.ts +69 -4
- package/src/chain/validation/executionPayloadBid.ts +119 -28
- package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
- package/src/chain/validation/proposerPreferences.ts +75 -13
- package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
- package/src/chain/validatorMonitor.ts +1 -1
- package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
- package/src/execution/engine/interface.ts +1 -1
- package/src/execution/engine/types.ts +1 -1
- package/src/metrics/nodeJsMetrics.ts +1 -1
- package/src/network/core/networkCoreWorkerHandler.ts +2 -6
- package/src/network/discv5/index.ts +1 -4
- package/src/network/gossip/encoding.ts +13 -7
- package/src/network/gossip/gossipsub.ts +7 -2
- package/src/network/gossip/topic.ts +30 -0
- package/src/network/interface.ts +3 -1
- package/src/network/libp2p/index.ts +2 -10
- package/src/network/network.ts +24 -15
- package/src/network/peers/datastore.ts +1 -1
- package/src/network/processor/gossipHandlers.ts +12 -6
- package/src/network/processor/index.ts +2 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
- package/src/network/reqresp/handlers/index.ts +21 -12
- package/src/network/reqresp/handlers/lightClientFinalityUpdate.ts +1 -1
- package/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts +1 -1
- package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -2
- package/src/network/util.ts +5 -0
- package/src/sync/constants.ts +6 -5
- package/src/sync/range/batch.ts +31 -10
- package/src/sync/range/chain.ts +36 -16
- package/src/sync/range/utils/batches.ts +44 -0
- package/src/sync/sync.ts +10 -0
- package/src/sync/unknownBlock.ts +8 -2
- package/src/util/execution.ts +1 -1
- package/src/util/profile.ts +1 -31
- package/src/util/sszBytes.ts +1 -1
- package/lib/bun-wrappers/prometheus-gc-stats.d.ts +0 -2
- package/lib/bun-wrappers/prometheus-gc-stats.d.ts.map +0 -1
- package/lib/bun-wrappers/prometheus-gc-stats.js +0 -8
- package/lib/bun-wrappers/prometheus-gc-stats.js.map +0 -1
- package/lib/network/peers/datastore_bun.d.ts +0 -3
- package/lib/network/peers/datastore_bun.d.ts.map +0 -1
- package/lib/network/peers/datastore_bun.js +0 -3
- package/lib/network/peers/datastore_bun.js.map +0 -1
- package/src/bun-wrappers/prometheus-gc-stats.ts +0 -7
- package/src/network/peers/datastore_bun.ts +0 -3
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {ExecutionStatus} from "@lodestar/fork-choice";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ForkName,
|
|
5
|
+
MAX_ATTESTATIONS_ELECTRA,
|
|
6
|
+
MAX_ATTESTER_SLASHINGS_ELECTRA,
|
|
7
|
+
MAX_BLS_TO_EXECUTION_CHANGES,
|
|
8
|
+
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
9
|
+
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
|
|
10
|
+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
11
|
+
MAX_PAYLOAD_ATTESTATIONS,
|
|
12
|
+
MAX_PROPOSER_SLASHINGS,
|
|
13
|
+
MAX_VOLUNTARY_EXITS,
|
|
14
|
+
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
|
|
15
|
+
isForkPostBellatrix,
|
|
16
|
+
isForkPostDeneb,
|
|
17
|
+
isForkPostGloas,
|
|
18
|
+
} from "@lodestar/params";
|
|
4
19
|
import {
|
|
5
20
|
computeEpochAtSlot,
|
|
6
21
|
computeStartSlotAtEpoch,
|
|
@@ -88,8 +103,8 @@ export async function validateGossipBlock(
|
|
|
88
103
|
// 2. The parent is unknown to us, we probably want to download it since it might actually
|
|
89
104
|
// descend from the finalized root.
|
|
90
105
|
// (Non-Lighthouse): Since we prune all blocks non-descendant from finalized checking the `db.block` database won't be useful to guard
|
|
91
|
-
// against known bad fork blocks, so we throw
|
|
92
|
-
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.
|
|
106
|
+
// against known bad fork blocks, so we throw PARENT_BLOCK_UNKNOWN for cases (1) and (2)
|
|
107
|
+
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN, parentRoot});
|
|
93
108
|
}
|
|
94
109
|
|
|
95
110
|
// [IGNORE] The block's parent (defined by `block.parent_root`) passes all validation
|
|
@@ -164,6 +179,56 @@ export async function validateGossipBlock(
|
|
|
164
179
|
});
|
|
165
180
|
}
|
|
166
181
|
|
|
182
|
+
// [REJECT] The counts of `block.body.parent_execution_requests` are within
|
|
183
|
+
// their respective limits -- i.e. validate that
|
|
184
|
+
// `len(block.body.parent_execution_requests.withdrawals) <= MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD`,
|
|
185
|
+
// `len(block.body.parent_execution_requests.consolidations) <= MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD`,
|
|
186
|
+
// `len(block.body.parent_execution_requests.builder_deposits) <= MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD`,
|
|
187
|
+
// and
|
|
188
|
+
// `len(block.body.parent_execution_requests.builder_exits) <= MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD`.
|
|
189
|
+
// [REJECT] The counts of the block body operations are within their respective
|
|
190
|
+
// limits -- i.e. validate that
|
|
191
|
+
// `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`,
|
|
192
|
+
// `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS_ELECTRA`,
|
|
193
|
+
// `len(block.body.attestations) <= MAX_ATTESTATIONS_ELECTRA`,
|
|
194
|
+
// `len(block.body.deposits) == 0`,
|
|
195
|
+
// `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`,
|
|
196
|
+
// `len(block.body.bls_to_execution_changes) <= MAX_BLS_TO_EXECUTION_CHANGES`,
|
|
197
|
+
// and `len(block.body.payload_attestations) <= MAX_PAYLOAD_ATTESTATIONS`.
|
|
198
|
+
const body = (block as gloas.BeaconBlock).body;
|
|
199
|
+
const requests = body.parentExecutionRequests;
|
|
200
|
+
const countLimits: [string, number, number][] = [
|
|
201
|
+
["parentExecutionRequests.withdrawals", requests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
|
|
202
|
+
[
|
|
203
|
+
"parentExecutionRequests.consolidations",
|
|
204
|
+
requests.consolidations.length,
|
|
205
|
+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
206
|
+
],
|
|
207
|
+
[
|
|
208
|
+
"parentExecutionRequests.builderDeposits",
|
|
209
|
+
requests.builderDeposits.length,
|
|
210
|
+
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
211
|
+
],
|
|
212
|
+
["parentExecutionRequests.builderExits", requests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
|
|
213
|
+
["proposerSlashings", body.proposerSlashings.length, MAX_PROPOSER_SLASHINGS],
|
|
214
|
+
["attesterSlashings", body.attesterSlashings.length, MAX_ATTESTER_SLASHINGS_ELECTRA],
|
|
215
|
+
["attestations", body.attestations.length, MAX_ATTESTATIONS_ELECTRA],
|
|
216
|
+
["deposits", body.deposits.length, 0],
|
|
217
|
+
["voluntaryExits", body.voluntaryExits.length, MAX_VOLUNTARY_EXITS],
|
|
218
|
+
["blsToExecutionChanges", body.blsToExecutionChanges.length, MAX_BLS_TO_EXECUTION_CHANGES],
|
|
219
|
+
["payloadAttestations", body.payloadAttestations.length, MAX_PAYLOAD_ATTESTATIONS],
|
|
220
|
+
];
|
|
221
|
+
for (const [name, count, limit] of countLimits) {
|
|
222
|
+
if (count > limit) {
|
|
223
|
+
throw new BlockGossipError(GossipAction.REJECT, {
|
|
224
|
+
code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS,
|
|
225
|
+
name,
|
|
226
|
+
count,
|
|
227
|
+
limit,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
167
232
|
// TODO GLOAS: [REJECT] The block's execution payload parent (defined by bid.parent_block_hash) passes all validation
|
|
168
233
|
// This requires execution engine integration to verify the parent block hash
|
|
169
234
|
}
|
|
@@ -176,7 +241,7 @@ export async function validateGossipBlock(
|
|
|
176
241
|
const blockState = await chain.regen
|
|
177
242
|
.getPreState(block, {dontTransferCache: true}, RegenCaller.validateGossipBlock)
|
|
178
243
|
.catch(() => {
|
|
179
|
-
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.
|
|
244
|
+
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN, parentRoot});
|
|
180
245
|
});
|
|
181
246
|
|
|
182
247
|
// in forky condition, make sure to populate ShufflingCache with regened state
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
3
|
import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
|
|
3
4
|
import {
|
|
4
5
|
computeEpochAtSlot,
|
|
@@ -8,13 +9,73 @@ import {
|
|
|
8
9
|
isGasLimitTargetCompatible,
|
|
9
10
|
isStatePostGloas,
|
|
10
11
|
} from "@lodestar/state-transition";
|
|
11
|
-
import {ValidatorIndex, gloas} from "@lodestar/types";
|
|
12
|
+
import {RootHex, Slot, ValidatorIndex, gloas} from "@lodestar/types";
|
|
12
13
|
import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
|
|
13
14
|
import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
|
|
14
15
|
import {ExecutionPayloadBidError, ExecutionPayloadBidErrorCode, GossipAction} from "../errors/index.js";
|
|
15
16
|
import {IBeaconChain} from "../index.js";
|
|
16
17
|
import {RegenCaller} from "../regen/index.js";
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Relative increment over the current highest bid required to forward a bid, in basis points.
|
|
21
|
+
* With 3%, laddering from 1 gwei to 1 ETH takes ~256 bids given the floor and cap below, see
|
|
22
|
+
* https://github.com/ethereum/consensus-specs/pull/4792#issuecomment-3714553549.
|
|
23
|
+
*/
|
|
24
|
+
const BID_INCREMENT_BPS = 300;
|
|
25
|
+
/**
|
|
26
|
+
* Minimum absolute increment (0.0001 ETH). Covers low bid values where the relative increment
|
|
27
|
+
* is tiny and provides weak spam protection.
|
|
28
|
+
*/
|
|
29
|
+
const BID_INCREMENT_FLOOR_GWEI = 100_000;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum absolute increment (0.01 ETH). Bounds the barrier for legitimate competition on
|
|
32
|
+
* high value blocks where the relative increment would suppress closely competing bids.
|
|
33
|
+
*/
|
|
34
|
+
const BID_INCREMENT_CAP_GWEI = 10_000_000;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Return the minimum value a new bid must have to be forwarded given the current highest bid.
|
|
38
|
+
* Division before multiplication to stay within safe integer range for max gwei values.
|
|
39
|
+
*/
|
|
40
|
+
function getMinBidValue(currentHighestBid: number): number {
|
|
41
|
+
const relativeIncrement = Math.floor(currentHighestBid / 10_000) * BID_INCREMENT_BPS;
|
|
42
|
+
const increment = Math.min(Math.max(BID_INCREMENT_FLOOR_GWEI, relativeIncrement), BID_INCREMENT_CAP_GWEI);
|
|
43
|
+
return currentHighestBid + increment;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check whether a bid builds on one of the paths compatible with the local head branch.
|
|
48
|
+
*
|
|
49
|
+
* The direct parent path is always allowed for proposer-boost reorgs. Otherwise the bid
|
|
50
|
+
* must build on the local head's full or empty payload variant, as selected for its slot.
|
|
51
|
+
*/
|
|
52
|
+
function isBidCompatibleWithHead(
|
|
53
|
+
forkChoice: IForkChoice,
|
|
54
|
+
head: ProtoBlock,
|
|
55
|
+
bidSlot: Slot,
|
|
56
|
+
bidParentBlockRoot: RootHex,
|
|
57
|
+
bidParentBlockHash: RootHex
|
|
58
|
+
): boolean {
|
|
59
|
+
const buildsOnParentBlock = bidParentBlockRoot === head.parentRoot;
|
|
60
|
+
const buildsOnParentPayload = bidParentBlockHash === head.parentBlockHash;
|
|
61
|
+
|
|
62
|
+
if (buildsOnParentBlock && buildsOnParentPayload) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (bidParentBlockRoot !== head.blockRoot) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const buildsOnHeadPayload = bidParentBlockHash === head.executionPayloadBlockHash;
|
|
71
|
+
|
|
72
|
+
if (forkChoice.shouldBuildOnFull(head, bidSlot)) {
|
|
73
|
+
return buildsOnHeadPayload;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return buildsOnParentPayload;
|
|
77
|
+
}
|
|
78
|
+
|
|
18
79
|
export async function validateApiExecutionPayloadBid(
|
|
19
80
|
chain: IBeaconChain,
|
|
20
81
|
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
@@ -34,8 +95,8 @@ async function validateExecutionPayloadBid(
|
|
|
34
95
|
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
35
96
|
): Promise<{proposerIndex: ValidatorIndex}> {
|
|
36
97
|
const bid = signedExecutionPayloadBid.message;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
98
|
+
const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
|
|
99
|
+
const bidParentBlockHash = toRootHex(bid.parentBlockHash);
|
|
39
100
|
|
|
40
101
|
// [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
41
102
|
if (
|
|
@@ -49,14 +110,40 @@ async function validateExecutionPayloadBid(
|
|
|
49
110
|
});
|
|
50
111
|
}
|
|
51
112
|
|
|
113
|
+
// [IGNORE] The bid is compatible with the current head branch.
|
|
114
|
+
const head = chain.forkChoice.getHead();
|
|
115
|
+
if (!isBidCompatibleWithHead(chain.forkChoice, head, bid.slot, bidParentBlockRoot, bidParentBlockHash)) {
|
|
116
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
117
|
+
code: ExecutionPayloadBidErrorCode.INCOMPATIBLE_WITH_HEAD,
|
|
118
|
+
slot: bid.slot,
|
|
119
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
120
|
+
parentBlockHash: bidParentBlockHash,
|
|
121
|
+
headBlockRoot: head.blockRoot,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// [IGNORE] this is the first signed bid seen with a valid signature from the given builder for
|
|
126
|
+
// the tuple `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
|
|
127
|
+
// Entries are only added after signature verification, so known tuples can be dropped before
|
|
128
|
+
// state regeneration and the other expensive validation steps.
|
|
129
|
+
if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex, bidParentBlockHash, bidParentBlockRoot)) {
|
|
130
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
131
|
+
code: ExecutionPayloadBidErrorCode.BID_ALREADY_KNOWN,
|
|
132
|
+
builderIndex: bid.builderIndex,
|
|
133
|
+
slot: bid.slot,
|
|
134
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
135
|
+
parentBlockHash: bidParentBlockHash,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
52
139
|
// [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon block in fork choice.
|
|
53
140
|
// Moved earlier than the spec ordering so we can derive the proposer dependent root for the
|
|
54
141
|
// proposer-preferences lookup below from a known fork-choice block.
|
|
55
|
-
const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(
|
|
142
|
+
const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(bidParentBlockRoot);
|
|
56
143
|
if (parentBlock === null) {
|
|
57
144
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
58
145
|
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
59
|
-
parentBlockRoot:
|
|
146
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
60
147
|
});
|
|
61
148
|
}
|
|
62
149
|
|
|
@@ -95,7 +182,7 @@ async function validateExecutionPayloadBid(
|
|
|
95
182
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
96
183
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES,
|
|
97
184
|
slot: bid.slot,
|
|
98
|
-
parentBlockRoot:
|
|
185
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
99
186
|
dependentRoot: "unknown",
|
|
100
187
|
});
|
|
101
188
|
}
|
|
@@ -105,7 +192,7 @@ async function validateExecutionPayloadBid(
|
|
|
105
192
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
106
193
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES,
|
|
107
194
|
slot: bid.slot,
|
|
108
|
-
parentBlockRoot:
|
|
195
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
109
196
|
dependentRoot: dependentRootHex,
|
|
110
197
|
});
|
|
111
198
|
}
|
|
@@ -116,7 +203,7 @@ async function validateExecutionPayloadBid(
|
|
|
116
203
|
.catch(() => {
|
|
117
204
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
118
205
|
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
119
|
-
parentBlockRoot:
|
|
206
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
120
207
|
});
|
|
121
208
|
});
|
|
122
209
|
|
|
@@ -157,7 +244,7 @@ async function validateExecutionPayloadBid(
|
|
|
157
244
|
}
|
|
158
245
|
|
|
159
246
|
// [REJECT] `bid.execution_payment` is zero.
|
|
160
|
-
if (bid.executionPayment !==
|
|
247
|
+
if (bid.executionPayment !== 0n) {
|
|
161
248
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
162
249
|
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
|
|
163
250
|
builderIndex: bid.builderIndex,
|
|
@@ -165,9 +252,9 @@ async function validateExecutionPayloadBid(
|
|
|
165
252
|
});
|
|
166
253
|
}
|
|
167
254
|
|
|
168
|
-
// [
|
|
255
|
+
// [IGNORE] `bid.fee_recipient == proposer_preferences.fee_recipient`.
|
|
169
256
|
if (!byteArrayEquals(bid.feeRecipient, proposerPreferences.message.feeRecipient)) {
|
|
170
|
-
throw new ExecutionPayloadBidError(GossipAction.
|
|
257
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
171
258
|
code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH,
|
|
172
259
|
builderIndex: bid.builderIndex,
|
|
173
260
|
bidFeeRecipient: toHex(bid.feeRecipient),
|
|
@@ -181,11 +268,11 @@ async function validateExecutionPayloadBid(
|
|
|
181
268
|
// payload's hash) and EMPTY parents (EMPTY/PENDING variants carry the inherited parent
|
|
182
269
|
// payload's hash, since the new block doesn't have its own payload). Variant carries the
|
|
183
270
|
// executed payload's gas_limit, which we use as `parent_gas_limit` below.
|
|
184
|
-
const parentPayloadVariant = chain.forkChoice.getBlockHexAndBlockHash(
|
|
271
|
+
const parentPayloadVariant = chain.forkChoice.getBlockHexAndBlockHash(bidParentBlockRoot, bidParentBlockHash);
|
|
185
272
|
if (parentPayloadVariant === null || parentPayloadVariant.executionPayloadBlockHash === null) {
|
|
186
273
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
187
274
|
code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH,
|
|
188
|
-
parentBlockHash:
|
|
275
|
+
parentBlockHash: bidParentBlockHash,
|
|
189
276
|
});
|
|
190
277
|
}
|
|
191
278
|
|
|
@@ -193,7 +280,7 @@ async function validateExecutionPayloadBid(
|
|
|
193
280
|
// where `parent_gas_limit` is the `gas_limit` of the parent execution payload and
|
|
194
281
|
// `target_gas_limit` is `proposer_preferences.target_gas_limit`.
|
|
195
282
|
const bidGasLimit = bid.gasLimit;
|
|
196
|
-
const parentGasLimit = parentPayloadVariant.executionPayloadGasLimit;
|
|
283
|
+
const parentGasLimit = BigInt(parentPayloadVariant.executionPayloadGasLimit);
|
|
197
284
|
const targetGasLimit = proposerPreferences.message.targetGasLimit;
|
|
198
285
|
if (!isGasLimitTargetCompatible(parentGasLimit, bidGasLimit, targetGasLimit)) {
|
|
199
286
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
@@ -218,21 +305,13 @@ async function validateExecutionPayloadBid(
|
|
|
218
305
|
});
|
|
219
306
|
}
|
|
220
307
|
|
|
221
|
-
// [IGNORE] this is the first signed bid seen with a valid signature from the given builder for this slot.
|
|
222
|
-
if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex)) {
|
|
223
|
-
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
224
|
-
code: ExecutionPayloadBidErrorCode.BID_ALREADY_KNOWN,
|
|
225
|
-
builderIndex: bid.builderIndex,
|
|
226
|
-
slot: bid.slot,
|
|
227
|
-
parentBlockRoot: parentBlockRootHex,
|
|
228
|
-
parentBlockHash: parentBlockHashHex,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
308
|
// [IGNORE] this bid is the highest value bid seen for the tuple
|
|
233
309
|
// `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
|
|
234
|
-
|
|
235
|
-
|
|
310
|
+
// As a DoS prevention measure, the bid must also exceed the current highest bid by a minimum
|
|
311
|
+
// increment, see https://github.com/ethereum/consensus-specs/pull/4831. This prevents spam
|
|
312
|
+
// from builders submitting numerous bids with minimal value increments.
|
|
313
|
+
const bestBid = chain.executionPayloadBidPool.getBestBid(bid.slot, bidParentBlockHash, bidParentBlockRoot);
|
|
314
|
+
if (bestBid !== null && bid.value < getMinBidValue(bestBid.message.value)) {
|
|
236
315
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
237
316
|
code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
|
|
238
317
|
bidValue: bid.value,
|
|
@@ -276,8 +355,20 @@ async function validateExecutionPayloadBid(
|
|
|
276
355
|
});
|
|
277
356
|
}
|
|
278
357
|
|
|
358
|
+
// Repeat the seen check after the awaited signature verification to prevent concurrent bids
|
|
359
|
+
// for the same builder and tuple from both passing validation.
|
|
360
|
+
if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex, bidParentBlockHash, bidParentBlockRoot)) {
|
|
361
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
362
|
+
code: ExecutionPayloadBidErrorCode.BID_ALREADY_KNOWN,
|
|
363
|
+
builderIndex: bid.builderIndex,
|
|
364
|
+
slot: bid.slot,
|
|
365
|
+
parentBlockRoot: bidParentBlockRoot,
|
|
366
|
+
parentBlockHash: bidParentBlockHash,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
279
370
|
// Valid
|
|
280
|
-
chain.seenExecutionPayloadBids.add(bid.slot, bid.builderIndex);
|
|
371
|
+
chain.seenExecutionPayloadBids.add(bid.slot, bid.builderIndex, bidParentBlockHash, bidParentBlockRoot);
|
|
281
372
|
|
|
282
373
|
return {proposerIndex: proposerPreferences.message.validatorIndex};
|
|
283
374
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
2
|
+
import {
|
|
3
|
+
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
4
|
+
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
|
|
5
|
+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
6
|
+
MAX_WITHDRAWALS_PER_PAYLOAD,
|
|
7
|
+
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
|
|
8
|
+
} from "@lodestar/params";
|
|
2
9
|
import {
|
|
3
10
|
computeStartSlotAtEpoch,
|
|
4
11
|
getExecutionPayloadEnvelopeSignatureSet,
|
|
@@ -115,6 +122,36 @@ async function validateExecutionPayloadEnvelope(
|
|
|
115
122
|
});
|
|
116
123
|
}
|
|
117
124
|
|
|
125
|
+
// [REJECT] The counts of `execution_requests` are within their respective limits.
|
|
126
|
+
// New in Gloas:EIP7688 — progressive lists are unbounded at the type level, so bounds
|
|
127
|
+
// are enforced here in gossip validation.
|
|
128
|
+
const {executionRequests} = envelope;
|
|
129
|
+
const requestCountLimits: [string, number, number][] = [
|
|
130
|
+
["withdrawals", executionRequests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
|
|
131
|
+
["consolidations", executionRequests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
|
|
132
|
+
["builderDeposits", executionRequests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD],
|
|
133
|
+
["builderExits", executionRequests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
|
|
134
|
+
];
|
|
135
|
+
for (const [name, count, limit] of requestCountLimits) {
|
|
136
|
+
if (count > limit) {
|
|
137
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
138
|
+
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED,
|
|
139
|
+
name,
|
|
140
|
+
count,
|
|
141
|
+
limit,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// [REJECT] The number of withdrawals is within the limit.
|
|
147
|
+
if (payload.withdrawals.length > MAX_WITHDRAWALS_PER_PAYLOAD) {
|
|
148
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
149
|
+
code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED,
|
|
150
|
+
count: payload.withdrawals.length,
|
|
151
|
+
limit: MAX_WITHDRAWALS_PER_PAYLOAD,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
118
155
|
// Get the block state to verify the builder's signature.
|
|
119
156
|
const blockState = await chain.regen
|
|
120
157
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
+
import {GENESIS_EPOCH, GENESIS_SLOT, MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
3
|
import {
|
|
3
4
|
computeEpochAtSlot,
|
|
5
|
+
computeStartSlotAtEpoch,
|
|
4
6
|
createSingleSignatureSetFromComponents,
|
|
5
7
|
getProposerPreferencesSigningRoot,
|
|
6
8
|
} from "@lodestar/state-transition";
|
|
7
|
-
import {ValidatorIndex, gloas} from "@lodestar/types";
|
|
9
|
+
import {Epoch, Slot, ValidatorIndex, gloas} from "@lodestar/types";
|
|
8
10
|
import {toRootHex} from "@lodestar/utils";
|
|
9
11
|
import {GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode} from "../errors/index.js";
|
|
10
12
|
import {IBeaconChain} from "../index.js";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Validates a gossiped `SignedProposerPreferences` per
|
|
14
|
-
* https://github.com/ethereum/consensus-specs/blob/
|
|
16
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.13/specs/gloas/p2p-interface.md#new-proposer_preferences
|
|
15
17
|
*/
|
|
16
18
|
export async function validateGossipProposerPreferences(
|
|
17
19
|
chain: IBeaconChain,
|
|
@@ -22,9 +24,10 @@ export async function validateGossipProposerPreferences(
|
|
|
22
24
|
const dependentRootHex = toRootHex(dependentRoot);
|
|
23
25
|
const proposalEpoch = computeEpochAtSlot(proposalSlot);
|
|
24
26
|
|
|
25
|
-
// [IGNORE] `preferences.proposal_slot` is
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
// [IGNORE] `preferences.proposal_slot` is within the proposer lookahead,
|
|
28
|
+
// allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
29
|
+
const currentEpoch = computeEpochAtSlot(chain.clock.currentSlotWithGossipDisparity);
|
|
30
|
+
if (proposalEpoch > currentEpoch + MIN_SEED_LOOKAHEAD) {
|
|
28
31
|
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
29
32
|
code: ProposerPreferencesErrorCode.INVALID_EPOCH,
|
|
30
33
|
proposalSlot,
|
|
@@ -34,31 +37,65 @@ export async function validateGossipProposerPreferences(
|
|
|
34
37
|
|
|
35
38
|
// [IGNORE] `preferences.proposal_slot` has not already passed, i.e. `proposal_slot > current_slot`,
|
|
36
39
|
// allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
37
|
-
|
|
38
|
-
if (proposalSlot <= currentSlot) {
|
|
40
|
+
if (chain.clock.msFromSlot(proposalSlot) > chain.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
|
|
39
41
|
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
40
42
|
code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED,
|
|
41
43
|
proposalSlot,
|
|
42
|
-
currentSlot,
|
|
44
|
+
currentSlot: chain.clock.currentSlot,
|
|
43
45
|
});
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// [IGNORE] The block with root `dependent_root` has been seen by the node.
|
|
49
|
+
const dependentBlock = chain.forkChoice.getBlockHexDefaultStatus(dependentRootHex);
|
|
50
|
+
if (dependentBlock === null) {
|
|
51
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
52
|
+
code: ProposerPreferencesErrorCode.UNKNOWN_DEPENDENT_ROOT,
|
|
53
|
+
proposalSlot,
|
|
54
|
+
dependentRoot: dependentRootHex,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const dependentEpoch = Math.max(GENESIS_EPOCH, proposalEpoch - MIN_SEED_LOOKAHEAD);
|
|
59
|
+
const dependentRootSlot = getDependentRootSlot(dependentEpoch);
|
|
60
|
+
|
|
61
|
+
// [REJECT] The slot of the block with root `preferences.dependent_root` is strictly less than
|
|
62
|
+
// `compute_start_slot_at_epoch(compute_epoch_at_slot(preferences.proposal_slot) - MIN_SEED_LOOKAHEAD)`.
|
|
63
|
+
if (dependentBlock.slot > dependentRootSlot) {
|
|
64
|
+
throw new ProposerPreferencesError(GossipAction.REJECT, {
|
|
65
|
+
code: ProposerPreferencesErrorCode.INVALID_DEPENDENT_ROOT_SLOT,
|
|
66
|
+
dependentRoot: dependentRootHex,
|
|
67
|
+
dependentBlockSlot: dependentBlock.slot,
|
|
68
|
+
dependentRootSlot,
|
|
69
|
+
dependentEpoch,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// [IGNORE] `is_valid_dependent_root(store, preferences.dependent_root, epoch)` returns `True`,
|
|
74
|
+
// where `epoch` is `compute_epoch_at_slot(preferences.proposal_slot) - MIN_SEED_LOOKAHEAD`.
|
|
75
|
+
if (!isValidDependentRoot(chain.forkChoice, dependentBlock, dependentEpoch)) {
|
|
76
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
77
|
+
code: ProposerPreferencesErrorCode.INVALID_DEPENDENT_ROOT,
|
|
78
|
+
dependentRoot: dependentRootHex,
|
|
79
|
+
dependentBlockSlot: dependentBlock.slot,
|
|
80
|
+
dependentEpoch,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
47
84
|
// Resolve the proposer lookahead for the message's branch via head state (fast path) or
|
|
48
85
|
// the previous-root checkpoint state (populated by `processSlotsToNearestCheckpoint` for
|
|
49
|
-
// any imported branch crossing into `
|
|
86
|
+
// any imported branch crossing into `dependentEpoch`). The head-state path also handles
|
|
50
87
|
// narrow timing windows where the checkpoint state isn't yet populated.
|
|
51
88
|
const headState = chain.getHeadState();
|
|
52
89
|
let proposers: ValidatorIndex[] | null = null;
|
|
53
90
|
if (headState.epoch === proposalEpoch && headState.currentDecisionRoot === dependentRootHex) {
|
|
54
91
|
proposers = headState.currentProposers;
|
|
55
|
-
} else if (headState.epoch ===
|
|
92
|
+
} else if (headState.epoch === dependentEpoch && headState.nextDecisionRoot === dependentRootHex) {
|
|
56
93
|
proposers = headState.nextProposers;
|
|
57
94
|
} else {
|
|
58
95
|
// Sync lookup only to not trigger disk reload from gossip input.
|
|
59
|
-
const checkpointState = chain.regen.getCheckpointStateSync({epoch:
|
|
96
|
+
const checkpointState = chain.regen.getCheckpointStateSync({epoch: dependentEpoch, rootHex: dependentRootHex});
|
|
60
97
|
if (checkpointState !== null) {
|
|
61
|
-
// State is at `
|
|
98
|
+
// State is at `dependentEpoch`, so proposers for `proposalSlot` (next epoch from
|
|
62
99
|
// the state's perspective) live in `nextProposers`.
|
|
63
100
|
proposers = checkpointState.nextProposers;
|
|
64
101
|
}
|
|
@@ -118,3 +155,28 @@ export async function validateGossipProposerPreferences(
|
|
|
118
155
|
|
|
119
156
|
chain.proposerPreferencesPool.add(signedProposerPreferences);
|
|
120
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check whether `dependentBlock` is a viable shuffling-dependent root for `epoch`:
|
|
161
|
+
* it is before the epoch boundary and either has a direct child at or after
|
|
162
|
+
* the boundary, or is the current fork-choice head.
|
|
163
|
+
*/
|
|
164
|
+
export function isValidDependentRoot(forkChoice: IForkChoice, dependentBlock: ProtoBlock, epoch: Epoch): boolean {
|
|
165
|
+
const epochStartSlot = computeStartSlotAtEpoch(epoch);
|
|
166
|
+
if (dependentBlock.slot > getDependentRootSlot(epoch)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const block of forkChoice.getBlockSummariesByParentRoot(dependentBlock.blockRoot)) {
|
|
171
|
+
if (block.slot >= epochStartSlot) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return dependentBlock.blockRoot === forkChoice.getHeadRoot();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Return the slot used to select a dependent root for `epoch`, clamped to genesis. */
|
|
180
|
+
function getDependentRootSlot(epoch: Epoch): Slot {
|
|
181
|
+
return Math.max(GENESIS_SLOT, computeStartSlotAtEpoch(epoch) - 1);
|
|
182
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
-
import {DOMAIN_AGGREGATE_AND_PROOF
|
|
2
|
+
import {DOMAIN_AGGREGATE_AND_PROOF} from "@lodestar/params";
|
|
3
3
|
import {ISignatureSet, SignatureSetType, computeSigningRoot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
|
-
import {Epoch, SignedAggregateAndProof, ValidatorIndex
|
|
4
|
+
import {Epoch, SignedAggregateAndProof, ValidatorIndex} from "@lodestar/types";
|
|
5
5
|
|
|
6
6
|
export function getAggregateAndProofSigningRoot(
|
|
7
7
|
config: BeaconConfig,
|
|
@@ -14,7 +14,7 @@ export function getAggregateAndProofSigningRoot(
|
|
|
14
14
|
const slot = computeStartSlotAtEpoch(epoch);
|
|
15
15
|
const fork = config.getForkName(slot);
|
|
16
16
|
const aggregatorDomain = config.getDomainAtFork(fork, DOMAIN_AGGREGATE_AND_PROOF);
|
|
17
|
-
const sszType =
|
|
17
|
+
const sszType = config.getForkTypes(slot).AggregateAndProof;
|
|
18
18
|
return computeSigningRoot(sszType, aggregateAndProof.message, aggregatorDomain);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -469,7 +469,7 @@ export function createValidatorMonitor(
|
|
|
469
469
|
proposerIndex,
|
|
470
470
|
src,
|
|
471
471
|
builderIndex: bid.builderIndex,
|
|
472
|
-
gasLimit: bid.gasLimit,
|
|
472
|
+
gasLimit: Number(bid.gasLimit),
|
|
473
473
|
value: bid.value.toString(),
|
|
474
474
|
parentBlockRoot: toRootHex(bid.parentBlockRoot),
|
|
475
475
|
parentBlockHash: toRootHex(bid.parentBlockHash),
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {ContainerType, VectorCompositeType} from "@chainsafe/ssz";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
3
|
import {DatabaseController, Repository} from "@lodestar/db";
|
|
4
|
+
import {CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS, NEXT_SYNC_COMMITTEE_DEPTH_GLOAS} from "@lodestar/params";
|
|
4
5
|
import {ssz} from "@lodestar/types";
|
|
5
6
|
import {SyncCommitteeWitness} from "../../chain/lightClient/types.js";
|
|
6
7
|
import {Bucket, getBucketNameByValue} from "../buckets.js";
|
|
7
8
|
|
|
8
|
-
// We add a 1-byte prefix where 0 means pre-
|
|
9
|
+
// We add a 1-byte prefix where 0 means pre-Electra, 1 means post-Electra, and 2 means post-Gloas.
|
|
9
10
|
enum PrefixByte {
|
|
10
11
|
PRE_ELECTRA = 0,
|
|
11
12
|
POST_ELECTRA = 1,
|
|
13
|
+
POST_GLOAS = 2,
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const NUM_WITNESS = 4;
|
|
@@ -35,6 +37,35 @@ export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncC
|
|
|
35
37
|
// Overrides for multi-fork
|
|
36
38
|
encodeValue(value: SyncCommitteeWitness): Uint8Array {
|
|
37
39
|
const numWitness = value.witness.length;
|
|
40
|
+
const hasGloasBranches =
|
|
41
|
+
value.currentSyncCommitteeBranch !== undefined || value.nextSyncCommitteeBranch !== undefined;
|
|
42
|
+
|
|
43
|
+
if (hasGloasBranches) {
|
|
44
|
+
if (
|
|
45
|
+
value.currentSyncCommitteeBranch?.length !== CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS ||
|
|
46
|
+
value.nextSyncCommitteeBranch?.length !== NEXT_SYNC_COMMITTEE_DEPTH_GLOAS
|
|
47
|
+
) {
|
|
48
|
+
throw Error(
|
|
49
|
+
`Invalid post-Gloas sync committee branch lengths current=${value.currentSyncCommitteeBranch?.length} next=${value.nextSyncCommitteeBranch?.length}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const type = new ContainerType({
|
|
54
|
+
currentSyncCommitteeBranch: new VectorCompositeType(ssz.Root, CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
55
|
+
nextSyncCommitteeBranch: new VectorCompositeType(ssz.Root, NEXT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
56
|
+
currentSyncCommitteeRoot: ssz.Root,
|
|
57
|
+
nextSyncCommitteeRoot: ssz.Root,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const valueBytes = type.serialize({
|
|
61
|
+
currentSyncCommitteeBranch: value.currentSyncCommitteeBranch,
|
|
62
|
+
nextSyncCommitteeBranch: value.nextSyncCommitteeBranch,
|
|
63
|
+
currentSyncCommitteeRoot: value.currentSyncCommitteeRoot,
|
|
64
|
+
nextSyncCommitteeRoot: value.nextSyncCommitteeRoot,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return prefixData(PrefixByte.POST_GLOAS, valueBytes);
|
|
68
|
+
}
|
|
38
69
|
|
|
39
70
|
if (numWitness !== NUM_WITNESS && numWitness !== NUM_WITNESS_ELECTRA) {
|
|
40
71
|
throw Error(`Number of witness can only be 4 pre-electra or 5 post-electra numWitness=${numWitness}`);
|
|
@@ -51,19 +82,25 @@ export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncC
|
|
|
51
82
|
// We need to differentiate between post-electra and pre-electra witness
|
|
52
83
|
// such that we can deserialize correctly
|
|
53
84
|
const isPostElectra = numWitness === NUM_WITNESS_ELECTRA;
|
|
54
|
-
|
|
55
|
-
prefixByte[0] = isPostElectra ? PrefixByte.POST_ELECTRA : PrefixByte.PRE_ELECTRA;
|
|
56
|
-
|
|
57
|
-
const prefixedData = new Uint8Array(1 + valueBytes.length);
|
|
58
|
-
prefixedData.set(prefixByte, 0);
|
|
59
|
-
prefixedData.set(valueBytes, 1);
|
|
60
|
-
|
|
61
|
-
return prefixedData;
|
|
85
|
+
return prefixData(isPostElectra ? PrefixByte.POST_ELECTRA : PrefixByte.PRE_ELECTRA, valueBytes);
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
decodeValue(data: Uint8Array): SyncCommitteeWitness {
|
|
65
89
|
// First byte is written
|
|
66
90
|
const prefix = data.subarray(0, 1);
|
|
91
|
+
const isPostGloas = prefix[0] === PrefixByte.POST_GLOAS;
|
|
92
|
+
|
|
93
|
+
if (isPostGloas) {
|
|
94
|
+
const type = new ContainerType({
|
|
95
|
+
currentSyncCommitteeBranch: new VectorCompositeType(ssz.Root, CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
96
|
+
nextSyncCommitteeBranch: new VectorCompositeType(ssz.Root, NEXT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
97
|
+
currentSyncCommitteeRoot: ssz.Root,
|
|
98
|
+
nextSyncCommitteeRoot: ssz.Root,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return {witness: [], ...type.deserialize(data.subarray(1))};
|
|
102
|
+
}
|
|
103
|
+
|
|
67
104
|
const isPostElectra = prefix[0] === PrefixByte.POST_ELECTRA;
|
|
68
105
|
|
|
69
106
|
const type = new ContainerType({
|
|
@@ -75,3 +112,14 @@ export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncC
|
|
|
75
112
|
return type.deserialize(data.subarray(1));
|
|
76
113
|
}
|
|
77
114
|
}
|
|
115
|
+
|
|
116
|
+
function prefixData(prefix: PrefixByte, valueBytes: Uint8Array): Uint8Array {
|
|
117
|
+
const prefixByte = new Uint8Array(1);
|
|
118
|
+
prefixByte[0] = prefix;
|
|
119
|
+
|
|
120
|
+
const prefixedData = new Uint8Array(1 + valueBytes.length);
|
|
121
|
+
prefixedData.set(prefixByte, 0);
|
|
122
|
+
prefixedData.set(valueBytes, 1);
|
|
123
|
+
|
|
124
|
+
return prefixedData;
|
|
125
|
+
}
|