@lodestar/beacon-node 1.48.0-dev.e1303d42b0 → 1.48.0-dev.e22a22a11d
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/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +39 -39
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +0 -13
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +10 -1
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/chain.d.ts +5 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +67 -38
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +1 -1
- package/lib/chain/emitter.js +1 -1
- package/lib/chain/errors/blockError.d.ts +3 -5
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -2
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +0 -11
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +2 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +1 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +13 -13
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +46 -64
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +9 -50
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +0 -29
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +7 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +22 -5
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +4 -1
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +11 -0
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/rateLimit.js +2 -1
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +3 -1
- package/lib/sync/range/range.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/validator/index.ts +44 -43
- package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
- package/src/chain/blocks/importBlock.ts +0 -13
- package/src/chain/blocks/utils/chainSegment.ts +10 -1
- package/src/chain/chain.ts +74 -46
- package/src/chain/emitter.ts +1 -1
- package/src/chain/errors/blockError.ts +3 -3
- package/src/chain/errors/executionPayloadEnvelope.ts +0 -9
- package/src/chain/errors/gossipValidation.ts +1 -0
- package/src/chain/prepareNextSlot.ts +16 -16
- package/src/chain/produceBlock/produceBlockBody.ts +50 -71
- package/src/chain/validation/block.ts +9 -66
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -37
- package/src/chain/validatorMonitor.ts +24 -7
- package/src/execution/engine/mock.ts +4 -1
- package/src/network/processor/index.ts +13 -0
- package/src/network/reqresp/rateLimit.ts +2 -1
- package/src/sync/range/range.ts +3 -1
|
@@ -528,23 +528,18 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
528
528
|
}
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
throw Error(`Invalid builder getHeader response for fork=${fork}, missing blobKzgCommitments`);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
(blockBody as deneb.BlindedBeaconBlockBody).blobKzgCommitments = blobKzgCommitments;
|
|
538
|
-
Object.assign(logMeta, {blobs: blobKzgCommitments.length});
|
|
531
|
+
const {blobKzgCommitments, executionRequests} = builderRes;
|
|
532
|
+
if (blobKzgCommitments === undefined) {
|
|
533
|
+
throw Error(`Invalid builder getHeader response for fork=${fork}, missing blobKzgCommitments`);
|
|
539
534
|
}
|
|
540
535
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
(blockBody as electra.BlindedBeaconBlockBody).executionRequests = executionRequests;
|
|
536
|
+
(blockBody as BlindedBeaconBlockBody<ForkName.fulu>).blobKzgCommitments = blobKzgCommitments;
|
|
537
|
+
Object.assign(logMeta, {blobs: blobKzgCommitments.length});
|
|
538
|
+
|
|
539
|
+
if (executionRequests === undefined) {
|
|
540
|
+
throw Error(`Invalid builder getHeader response for fork=${fork}, missing executionRequests`);
|
|
547
541
|
}
|
|
542
|
+
(blockBody as BlindedBeaconBlockBody<ForkName.fulu>).executionRequests = executionRequests;
|
|
548
543
|
}
|
|
549
544
|
|
|
550
545
|
// blockType === BlockType.Full
|
|
@@ -899,45 +894,38 @@ function preparePayloadAttributes(
|
|
|
899
894
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
900
895
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
901
896
|
const prevRandao = prepareState.getRandaoMix(prepareState.epoch);
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
suggestedFeeRecipient: feeRecipient,
|
|
906
|
-
};
|
|
907
|
-
|
|
908
|
-
if (ForkSeq[fork] >= ForkSeq.capella) {
|
|
909
|
-
if (!isStatePostCapella(prepareState)) {
|
|
910
|
-
throw new Error("Expected Capella state for withdrawals");
|
|
911
|
-
}
|
|
897
|
+
if (!isStatePostCapella(prepareState)) {
|
|
898
|
+
throw new Error("Expected Capella state for withdrawals");
|
|
899
|
+
}
|
|
912
900
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
922
|
-
prepareState.getExpectedWithdrawals().expectedWithdrawals;
|
|
923
|
-
} else {
|
|
924
|
-
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
925
|
-
// already deducted from CL balances but never credited on the EL (the envelope
|
|
926
|
-
// was not delivered). The next payload must carry those same withdrawals to
|
|
927
|
-
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
928
|
-
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
929
|
-
prepareState.payloadExpectedWithdrawals;
|
|
901
|
+
let withdrawals: capella.Withdrawal[];
|
|
902
|
+
if (isStatePostGloas(prepareState)) {
|
|
903
|
+
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
904
|
+
if (isExtendingPayload) {
|
|
905
|
+
// applyParentExecutionPayload sets latestBlockHash = parentBid.blockHash, so a mismatch
|
|
906
|
+
// here means the caller did not apply parent payload to prepareState
|
|
907
|
+
if (!byteArrayEquals(prepareState.latestBlockHash, prepareState.latestExecutionPayloadBid.blockHash)) {
|
|
908
|
+
throw new Error("Expected state with parent execution payload applied for withdrawals");
|
|
930
909
|
}
|
|
910
|
+
withdrawals = prepareState.getExpectedWithdrawals().expectedWithdrawals;
|
|
931
911
|
} else {
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
|
|
912
|
+
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
913
|
+
// already deducted from CL balances but never credited on the EL (the envelope
|
|
914
|
+
// was not delivered). The next payload must carry those same withdrawals to
|
|
915
|
+
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
916
|
+
withdrawals = prepareState.payloadExpectedWithdrawals;
|
|
935
917
|
}
|
|
918
|
+
} else {
|
|
919
|
+
withdrawals = prepareState.getExpectedWithdrawals().expectedWithdrawals;
|
|
936
920
|
}
|
|
937
921
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
922
|
+
const payloadAttributes: deneb.SSEPayloadAttributes["payloadAttributes"] = {
|
|
923
|
+
timestamp,
|
|
924
|
+
prevRandao,
|
|
925
|
+
suggestedFeeRecipient: feeRecipient,
|
|
926
|
+
withdrawals,
|
|
927
|
+
parentBeaconBlockRoot: parentBlockRoot,
|
|
928
|
+
};
|
|
941
929
|
|
|
942
930
|
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
943
931
|
if (!isStatePostGloas(prepareState)) {
|
|
@@ -1050,37 +1038,28 @@ export async function produceCommonBlockBody<T extends BlockType>(
|
|
|
1050
1038
|
step: BlockProductionStep.attestations,
|
|
1051
1039
|
});
|
|
1052
1040
|
|
|
1053
|
-
const
|
|
1041
|
+
const endSyncAggregate = stepsMetrics?.startTimer();
|
|
1042
|
+
const parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
1043
|
+
const previousSlot = slot - 1;
|
|
1044
|
+
const syncAggregate = this.syncContributionAndProofPool.getAggregate(previousSlot, parentBlockRoot);
|
|
1045
|
+
this.metrics?.production.producedSyncAggregateParticipants.observe(
|
|
1046
|
+
syncAggregate.syncCommitteeBits.getTrueBitIndexes().length
|
|
1047
|
+
);
|
|
1048
|
+
endSyncAggregate?.({
|
|
1049
|
+
step: BlockProductionStep.syncAggregate,
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
// Live proposal production is supported from Fulu onward.
|
|
1053
|
+
return {
|
|
1054
1054
|
randaoReveal,
|
|
1055
1055
|
graffiti,
|
|
1056
|
-
// Eth1 data voting is no longer required since electra
|
|
1057
1056
|
eth1Data: currentState.eth1Data,
|
|
1058
1057
|
proposerSlashings: this.opts.disableProposerSlashings === true ? [] : proposerSlashings,
|
|
1059
1058
|
attesterSlashings,
|
|
1060
1059
|
attestations,
|
|
1061
|
-
// Since electra, deposits are processed by the execution layer,
|
|
1062
|
-
// we no longer support handling deposits from earlier forks.
|
|
1063
1060
|
deposits: [],
|
|
1064
1061
|
voluntaryExits,
|
|
1062
|
+
blsToExecutionChanges,
|
|
1063
|
+
syncAggregate,
|
|
1065
1064
|
};
|
|
1066
|
-
|
|
1067
|
-
if (ForkSeq[fork] >= ForkSeq.capella) {
|
|
1068
|
-
(blockBody as CommonBlockBody).blsToExecutionChanges = blsToExecutionChanges;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
const endSyncAggregate = stepsMetrics?.startTimer();
|
|
1072
|
-
if (ForkSeq[fork] >= ForkSeq.altair) {
|
|
1073
|
-
const parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
1074
|
-
const previousSlot = slot - 1;
|
|
1075
|
-
const syncAggregate = this.syncContributionAndProofPool.getAggregate(previousSlot, parentBlockRoot);
|
|
1076
|
-
this.metrics?.production.producedSyncAggregateParticipants.observe(
|
|
1077
|
-
syncAggregate.syncCommitteeBits.getTrueBitIndexes().length
|
|
1078
|
-
);
|
|
1079
|
-
(blockBody as CommonBlockBody).syncAggregate = syncAggregate;
|
|
1080
|
-
}
|
|
1081
|
-
endSyncAggregate?.({
|
|
1082
|
-
step: BlockProductionStep.syncAggregate,
|
|
1083
|
-
});
|
|
1084
|
-
|
|
1085
|
-
return blockBody as CommonBlockBody;
|
|
1086
1065
|
}
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {ExecutionStatus} from "@lodestar/fork-choice";
|
|
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
|
-
MIN_SEED_LOOKAHEAD,
|
|
16
|
-
isForkPostBellatrix,
|
|
17
|
-
isForkPostDeneb,
|
|
18
|
-
isForkPostGloas,
|
|
19
|
-
} from "@lodestar/params";
|
|
3
|
+
import {ForkName, MIN_SEED_LOOKAHEAD, isForkPostBellatrix, isForkPostDeneb, isForkPostGloas} from "@lodestar/params";
|
|
20
4
|
import {
|
|
21
5
|
computeEpochAtSlot,
|
|
22
6
|
computeStartSlotAtEpoch,
|
|
@@ -173,7 +157,8 @@ export async function validateGossipBlock(
|
|
|
173
157
|
}
|
|
174
158
|
|
|
175
159
|
if (isForkPostGloas(fork)) {
|
|
176
|
-
const
|
|
160
|
+
const body = (block as gloas.BeaconBlock).body;
|
|
161
|
+
const bid = body.signedExecutionPayloadBid.message;
|
|
177
162
|
|
|
178
163
|
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in Consensus Layer
|
|
179
164
|
// -- i.e. validate that len(bid.blob_kzg_commitments) <= max_blobs_per_block
|
|
@@ -196,54 +181,12 @@ export async function validateGossipBlock(
|
|
|
196
181
|
});
|
|
197
182
|
}
|
|
198
183
|
|
|
199
|
-
// [REJECT] The
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// `len(block.body.parent_execution_requests.builder_exits) <= MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD`.
|
|
206
|
-
// [REJECT] The counts of the block body operations are within their respective
|
|
207
|
-
// limits -- i.e. validate that
|
|
208
|
-
// `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`,
|
|
209
|
-
// `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS_ELECTRA`,
|
|
210
|
-
// `len(block.body.attestations) <= MAX_ATTESTATIONS_ELECTRA`,
|
|
211
|
-
// `len(block.body.deposits) == 0`,
|
|
212
|
-
// `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`,
|
|
213
|
-
// `len(block.body.bls_to_execution_changes) <= MAX_BLS_TO_EXECUTION_CHANGES`,
|
|
214
|
-
// and `len(block.body.payload_attestations) <= MAX_PAYLOAD_ATTESTATIONS`.
|
|
215
|
-
const body = (block as gloas.BeaconBlock).body;
|
|
216
|
-
const requests = body.parentExecutionRequests;
|
|
217
|
-
const countLimits: [string, number, number][] = [
|
|
218
|
-
["parentExecutionRequests.withdrawals", requests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
|
|
219
|
-
[
|
|
220
|
-
"parentExecutionRequests.consolidations",
|
|
221
|
-
requests.consolidations.length,
|
|
222
|
-
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
223
|
-
],
|
|
224
|
-
[
|
|
225
|
-
"parentExecutionRequests.builderDeposits",
|
|
226
|
-
requests.builderDeposits.length,
|
|
227
|
-
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
228
|
-
],
|
|
229
|
-
["parentExecutionRequests.builderExits", requests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
|
|
230
|
-
["proposerSlashings", body.proposerSlashings.length, MAX_PROPOSER_SLASHINGS],
|
|
231
|
-
["attesterSlashings", body.attesterSlashings.length, MAX_ATTESTER_SLASHINGS_ELECTRA],
|
|
232
|
-
["attestations", body.attestations.length, MAX_ATTESTATIONS_ELECTRA],
|
|
233
|
-
["deposits", body.deposits.length, 0],
|
|
234
|
-
["voluntaryExits", body.voluntaryExits.length, MAX_VOLUNTARY_EXITS],
|
|
235
|
-
["blsToExecutionChanges", body.blsToExecutionChanges.length, MAX_BLS_TO_EXECUTION_CHANGES],
|
|
236
|
-
["payloadAttestations", body.payloadAttestations.length, MAX_PAYLOAD_ATTESTATIONS],
|
|
237
|
-
];
|
|
238
|
-
for (const [name, count, limit] of countLimits) {
|
|
239
|
-
if (count > limit) {
|
|
240
|
-
throw new BlockGossipError(GossipAction.REJECT, {
|
|
241
|
-
code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS,
|
|
242
|
-
name,
|
|
243
|
-
count,
|
|
244
|
-
limit,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
184
|
+
// [REJECT] The block contains no deposits.
|
|
185
|
+
if (body.deposits.length !== 0) {
|
|
186
|
+
throw new BlockGossipError(GossipAction.REJECT, {
|
|
187
|
+
code: BlockErrorCode.NON_ZERO_DEPOSITS,
|
|
188
|
+
count: body.deposits.length,
|
|
189
|
+
});
|
|
247
190
|
}
|
|
248
191
|
|
|
249
192
|
// TODO GLOAS: [REJECT] The block's execution payload parent (defined by bid.parent_block_hash) passes all validation
|
|
@@ -1,11 +1,4 @@
|
|
|
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";
|
|
9
2
|
import {
|
|
10
3
|
computeStartSlotAtEpoch,
|
|
11
4
|
getExecutionPayloadEnvelopeSignatureSet,
|
|
@@ -122,36 +115,6 @@ async function validateExecutionPayloadEnvelope(
|
|
|
122
115
|
});
|
|
123
116
|
}
|
|
124
117
|
|
|
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
|
-
|
|
155
118
|
// Get the block state to verify the builder's signature.
|
|
156
119
|
const blockState = await chain.regen
|
|
157
120
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
|
+
import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
3
3
|
import {
|
|
4
4
|
IBeaconStateView,
|
|
5
5
|
ParticipationFlags,
|
|
@@ -1117,7 +1117,11 @@ function isCanonical(rootCache: RootHexCache, block: AttestationBlockInclusion):
|
|
|
1117
1117
|
|
|
1118
1118
|
/** Returns true if root at slot is the same at slot - 1 == there was no new block at slot */
|
|
1119
1119
|
function isMissedSlot(rootCache: RootHexCache, slot: Slot): boolean {
|
|
1120
|
-
|
|
1120
|
+
if (slot <= 0) {
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
const root = rootCache.getBlockRootAtSlot(slot);
|
|
1124
|
+
return root !== null && root === rootCache.getBlockRootAtSlot(slot - 1);
|
|
1121
1125
|
}
|
|
1122
1126
|
|
|
1123
1127
|
function renderBlockProposalSummary(
|
|
@@ -1131,7 +1135,11 @@ function renderBlockProposalSummary(
|
|
|
1131
1135
|
return "not_submitted";
|
|
1132
1136
|
}
|
|
1133
1137
|
|
|
1134
|
-
|
|
1138
|
+
const canonicalRoot = rootCache.getBlockRootAtSlot(proposalSlot);
|
|
1139
|
+
if (canonicalRoot === null) {
|
|
1140
|
+
return "unknown";
|
|
1141
|
+
}
|
|
1142
|
+
if (canonicalRoot === proposal.blockRoot) {
|
|
1135
1143
|
// Canonical state includes our block
|
|
1136
1144
|
return "canonical";
|
|
1137
1145
|
}
|
|
@@ -1157,14 +1165,23 @@ function renderBlockProposalSummary(
|
|
|
1157
1165
|
* In normal network conditions the same root is read multiple times, specially the target.
|
|
1158
1166
|
*/
|
|
1159
1167
|
export class RootHexCache {
|
|
1160
|
-
private readonly blockRootSlotCache = new Map<Slot, RootHex>();
|
|
1168
|
+
private readonly blockRootSlotCache = new Map<Slot, RootHex | null>();
|
|
1161
1169
|
|
|
1162
1170
|
constructor(private readonly state: IBeaconStateView) {}
|
|
1163
1171
|
|
|
1164
|
-
|
|
1172
|
+
/**
|
|
1173
|
+
* Block root at `slot`, null if the state does not cover it: slots at or after the state's own slot (an inclusion
|
|
1174
|
+
* in a block the head state does not know yet, e.g. an orphaned block, or the slot after the last attestation of
|
|
1175
|
+
* the epoch) or slots older than `SLOTS_PER_HISTORICAL_ROOT`. Callers treat null as unknown instead of throwing
|
|
1176
|
+
* and aborting the summaries of all remaining validators.
|
|
1177
|
+
*/
|
|
1178
|
+
getBlockRootAtSlot(slot: Slot): RootHex | null {
|
|
1165
1179
|
let root = this.blockRootSlotCache.get(slot);
|
|
1166
|
-
if (
|
|
1167
|
-
root =
|
|
1180
|
+
if (root === undefined) {
|
|
1181
|
+
root =
|
|
1182
|
+
slot < this.state.slot && slot >= this.state.slot - SLOTS_PER_HISTORICAL_ROOT
|
|
1183
|
+
? toRootHex(this.state.getBlockRootAtSlot(slot))
|
|
1184
|
+
: null;
|
|
1168
1185
|
this.blockRootSlotCache.set(slot, root);
|
|
1169
1186
|
}
|
|
1170
1187
|
return root;
|
|
@@ -296,7 +296,10 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
|
|
|
296
296
|
|
|
297
297
|
// 5. Client software MUST update its forkchoice state if payloads referenced by forkchoiceState.headBlockHash and
|
|
298
298
|
// forkchoiceState.finalizedBlockHash are VALID.
|
|
299
|
-
|
|
299
|
+
//
|
|
300
|
+
// A zero finalizedBlockHash means "no finalized execution block" (e.g. the finalized checkpoint has no execution
|
|
301
|
+
// payload, pre-merge/pre-TTD). Real EL clients accept this, so the mock must not treat it as an unknown block.
|
|
302
|
+
if (finalizedBlockHash !== ZERO_HASH_HEX && !this.validBlocks.has(finalizedBlockHash)) {
|
|
300
303
|
throw Error(`Unknown finalizedBlockHash ${finalizedBlockHash}`);
|
|
301
304
|
}
|
|
302
305
|
this.headBlockHash = headBlockHash;
|
|
@@ -360,6 +360,19 @@ export class NetworkProcessor {
|
|
|
360
360
|
return;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
// DOS protection: drop far-future messages before allocating any unknown-root tracking entry.
|
|
364
|
+
// Bound each topic to what its gossip validator accepts; only execution_payload_bid accepts the next slot.
|
|
365
|
+
const latestPermissableSlot =
|
|
366
|
+
this.chain.clock.currentSlotWithGossipDisparity + (topicType === GossipType.execution_payload_bid ? 1 : 0);
|
|
367
|
+
if (slot > latestPermissableSlot) {
|
|
368
|
+
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
369
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({
|
|
370
|
+
topic: topicType,
|
|
371
|
+
error: GossipErrorCode.FUTURE_SLOT,
|
|
372
|
+
});
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
363
376
|
message.msgSlot = slot;
|
|
364
377
|
|
|
365
378
|
// this determines whether this message needs to wait for a Block or Envelope
|
|
@@ -126,7 +126,8 @@ function getRequestCountFn<T extends ReqRespMethod>(
|
|
|
126
126
|
const type = requestSszTypeByMethod(fork, config)[method];
|
|
127
127
|
return (reqData: Uint8Array) => {
|
|
128
128
|
try {
|
|
129
|
-
|
|
129
|
+
// A schema-valid request can cost 0 tokens; floor to 1 so it stays charged and bannable, not free.
|
|
130
|
+
return Math.max(1, (type && fn(type.deserialize(reqData))) ?? 1);
|
|
130
131
|
} catch (_e) {
|
|
131
132
|
return 1;
|
|
132
133
|
}
|
package/src/sync/range/range.ts
CHANGED
|
@@ -220,8 +220,9 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
220
220
|
const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
|
|
221
221
|
// We log orphaned payloads to work with different clients
|
|
222
222
|
// in the future, consider applying penalties in certain conditions
|
|
223
|
+
// make sure the payload source was from range sync in that case
|
|
223
224
|
for (const {slot, payloadEnvelopeInput} of orphaned) {
|
|
224
|
-
const peerIdStr = payloadEnvelopeInput.getPayloadEnvelopeSource()
|
|
225
|
+
const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
|
|
225
226
|
let client = "unknown";
|
|
226
227
|
if (peerIdStr !== undefined) {
|
|
227
228
|
try {
|
|
@@ -234,6 +235,7 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
234
235
|
slot,
|
|
235
236
|
root: payloadEnvelopeInput.blockRootHex,
|
|
236
237
|
peer: peerIdStr ?? "unknown",
|
|
238
|
+
source,
|
|
237
239
|
client,
|
|
238
240
|
skipped,
|
|
239
241
|
});
|