@lodestar/beacon-node 1.48.0-dev.f22c5ce63e → 1.48.0-dev.fd70e5fc15
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 +0 -7
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +14 -0
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/config/index.d.ts.map +1 -1
- package/lib/api/impl/config/index.js +4 -1
- package/lib/api/impl/config/index.js.map +1 -1
- 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/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +4 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +9 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +37 -4
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts +5 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +12 -18
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +8 -4
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +80 -15
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +8 -0
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.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 +26 -11
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/chain.d.ts +7 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +69 -40
- 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/executionPayloadBid.d.ts +4 -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 +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/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +2 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/lightClient/index.d.ts +3 -3
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +21 -23
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts +2 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +5 -1
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.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/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +56 -40
- 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 +0 -29
- 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 +5 -6
- package/lib/chain/validation/payloadAttestationMessage.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/builder/validateBid.d.ts.map +1 -1
- package/lib/execution/builder/validateBid.js +8 -5
- package/lib/execution/builder/validateBid.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/metrics/metrics/lodestar.d.ts +4 -3
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +5 -5
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/network.js +2 -2
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +382 -278
- 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 +22 -16
- 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 +24 -1
- package/lib/sync/range/range.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +0 -10
- package/src/api/impl/beacon/state/utils.ts +15 -0
- package/src/api/impl/config/index.ts +4 -1
- package/src/api/impl/validator/index.ts +44 -43
- package/src/chain/archiveStore/archiveStore.ts +6 -0
- package/src/chain/archiveStore/utils/archiveBlocks.ts +39 -3
- package/src/chain/blocks/importBlock.ts +25 -20
- package/src/chain/blocks/index.ts +107 -21
- package/src/chain/blocks/types.ts +9 -0
- package/src/chain/blocks/utils/chainSegment.ts +26 -12
- package/src/chain/chain.ts +78 -50
- package/src/chain/emitter.ts +1 -1
- package/src/chain/errors/blockError.ts +3 -3
- package/src/chain/errors/executionPayloadBid.ts +2 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +0 -9
- package/src/chain/errors/gossipValidation.ts +1 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/interface.ts +2 -2
- package/src/chain/lightClient/index.ts +32 -36
- package/src/chain/lightClient/proofs.ts +10 -0
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +50 -71
- package/src/chain/validation/block.ts +9 -66
- package/src/chain/validation/executionPayloadBid.ts +62 -44
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -37
- package/src/chain/validation/payloadAttestationMessage.ts +8 -9
- package/src/chain/validatorMonitor.ts +24 -7
- package/src/execution/builder/validateBid.ts +10 -6
- package/src/execution/engine/mock.ts +4 -1
- package/src/metrics/metrics/lodestar.ts +6 -5
- package/src/network/network.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +489 -344
- package/src/network/processor/index.ts +24 -17
- package/src/network/reqresp/rateLimit.ts +2 -1
- package/src/sync/range/range.ts +23 -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
|
|
@@ -104,6 +104,37 @@ export async function validateApiExecutionPayloadBid(
|
|
|
104
104
|
const bid = signedExecutionPayloadBid.message;
|
|
105
105
|
const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
|
|
106
106
|
|
|
107
|
+
// [REJECT] `bid.execution_payment` is zero.
|
|
108
|
+
if (bid.executionPayment !== 0n) {
|
|
109
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
110
|
+
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
|
|
111
|
+
builderIndex: bid.builderIndex,
|
|
112
|
+
executionPayment: bid.executionPayment,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// [REJECT] The bid's block hash is not equal to its parent block hash -- i.e.
|
|
117
|
+
// `bid.block_hash != bid.parent_block_hash`.
|
|
118
|
+
if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
|
|
119
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
120
|
+
code: ExecutionPayloadBidErrorCode.BLOCK_HASH_EQUALS_PARENT_BLOCK_HASH,
|
|
121
|
+
blockHash: toRootHex(bid.blockHash),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
126
|
+
// consensus layer -- i.e. validate that
|
|
127
|
+
// `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
|
|
128
|
+
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
129
|
+
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
130
|
+
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
131
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
132
|
+
code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
|
|
133
|
+
blobKzgCommitmentsLen,
|
|
134
|
+
commitmentLimit: maxBlobsPerBlock,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
107
138
|
// [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
108
139
|
if (
|
|
109
140
|
!chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot) &&
|
|
@@ -138,28 +169,6 @@ export async function validateApiExecutionPayloadBid(
|
|
|
138
169
|
});
|
|
139
170
|
}
|
|
140
171
|
|
|
141
|
-
// [REJECT] `bid.execution_payment` is zero.
|
|
142
|
-
if (bid.executionPayment !== 0n) {
|
|
143
|
-
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
144
|
-
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
|
|
145
|
-
builderIndex: bid.builderIndex,
|
|
146
|
-
executionPayment: bid.executionPayment,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
151
|
-
// consensus layer -- i.e. validate that
|
|
152
|
-
// `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
|
|
153
|
-
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
154
|
-
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
155
|
-
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
156
|
-
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
157
|
-
code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
|
|
158
|
-
blobKzgCommitmentsLen,
|
|
159
|
-
commitmentLimit: maxBlobsPerBlock,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
172
|
const state = await chain.regen
|
|
164
173
|
.getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateApiExecutionPayloadBid)
|
|
165
174
|
.catch(() => {
|
|
@@ -257,6 +266,37 @@ async function validateExecutionPayloadBid(
|
|
|
257
266
|
const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
|
|
258
267
|
const bidParentBlockHash = toRootHex(bid.parentBlockHash);
|
|
259
268
|
|
|
269
|
+
// [REJECT] `bid.execution_payment` is zero.
|
|
270
|
+
if (bid.executionPayment !== 0n) {
|
|
271
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
272
|
+
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
|
|
273
|
+
builderIndex: bid.builderIndex,
|
|
274
|
+
executionPayment: bid.executionPayment,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// [REJECT] The bid's block hash is not equal to its parent block hash -- i.e.
|
|
279
|
+
// `bid.block_hash != bid.parent_block_hash`.
|
|
280
|
+
if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
|
|
281
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
282
|
+
code: ExecutionPayloadBidErrorCode.BLOCK_HASH_EQUALS_PARENT_BLOCK_HASH,
|
|
283
|
+
blockHash: toRootHex(bid.blockHash),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
288
|
+
// consensus layer -- i.e. validate that
|
|
289
|
+
// `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
|
|
290
|
+
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
291
|
+
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
292
|
+
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
293
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
294
|
+
code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
|
|
295
|
+
blobKzgCommitmentsLen,
|
|
296
|
+
commitmentLimit: maxBlobsPerBlock,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
260
300
|
// [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
261
301
|
if (
|
|
262
302
|
!chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot) &&
|
|
@@ -402,15 +442,6 @@ async function validateExecutionPayloadBid(
|
|
|
402
442
|
});
|
|
403
443
|
}
|
|
404
444
|
|
|
405
|
-
// [REJECT] `bid.execution_payment` is zero.
|
|
406
|
-
if (bid.executionPayment !== 0n) {
|
|
407
|
-
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
408
|
-
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
|
|
409
|
-
builderIndex: bid.builderIndex,
|
|
410
|
-
executionPayment: bid.executionPayment,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
445
|
// [IGNORE] `bid.fee_recipient == proposer_preferences.fee_recipient`.
|
|
415
446
|
if (!byteArrayEquals(bid.feeRecipient, proposerPreferences.message.feeRecipient)) {
|
|
416
447
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
@@ -451,19 +482,6 @@ async function validateExecutionPayloadBid(
|
|
|
451
482
|
});
|
|
452
483
|
}
|
|
453
484
|
|
|
454
|
-
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
455
|
-
// consensus layer -- i.e. validate that
|
|
456
|
-
// `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
|
|
457
|
-
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
458
|
-
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
459
|
-
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
460
|
-
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
461
|
-
code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
|
|
462
|
-
blobKzgCommitmentsLen,
|
|
463
|
-
commitmentLimit: maxBlobsPerBlock,
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
|
|
467
485
|
// [IGNORE] this bid is the highest value bid seen for the tuple
|
|
468
486
|
// `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
|
|
469
487
|
// As a DoS prevention measure, the bid must also exceed the current highest bid by a minimum
|
|
@@ -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)
|
|
@@ -7,7 +7,6 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
|
|
|
7
7
|
import {toRootHex} from "@lodestar/utils";
|
|
8
8
|
import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
|
|
9
9
|
import {IBeaconChain} from "../index.js";
|
|
10
|
-
import {RegenCaller} from "../regen/index.js";
|
|
11
10
|
|
|
12
11
|
export type PayloadAttestationValidationResult = {
|
|
13
12
|
attDataRootHex: RootHex;
|
|
@@ -82,15 +81,15 @@ async function validatePayloadAttestationMessage(
|
|
|
82
81
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
83
82
|
// it is possible that the block didn't pass the validation
|
|
84
83
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
84
|
+
// block.slot === data.slot is enforced above, so use the block's post-state directly to avoid
|
|
85
|
+
// getting through regen queue
|
|
86
|
+
const state = chain.regen.getStateSync(block.stateRoot);
|
|
87
|
+
if (state == null) {
|
|
88
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
89
|
+
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
90
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
93
91
|
});
|
|
92
|
+
}
|
|
94
93
|
|
|
95
94
|
if (!isStatePostGloas(state)) {
|
|
96
95
|
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
@@ -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;
|
|
@@ -56,6 +56,16 @@ export async function validateBuilderApiExecutionPayloadBid(
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
|
|
60
|
+
throw Error(`Bid block hash ${toRootHex(bid.blockHash)} must not equal its parent block hash`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
64
|
+
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
65
|
+
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
66
|
+
throw Error(`Bid has too many KZG commitments len=${blobKzgCommitmentsLen} limit=${maxBlobsPerBlock}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
59
69
|
const totalPayment = getBuilderBidTotalGwei(bid, entry.maxExecutionPayment);
|
|
60
70
|
if (totalPayment < entry.minBid) {
|
|
61
71
|
throw Error(
|
|
@@ -100,12 +110,6 @@ export async function validateBuilderApiExecutionPayloadBid(
|
|
|
100
110
|
);
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
104
|
-
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
105
|
-
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
106
|
-
throw Error(`Bid has too many KZG commitments len=${blobKzgCommitmentsLen} limit=${maxBlobsPerBlock}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
113
|
// The coverage check only applies to the staked collateral payment, a pure execution
|
|
110
114
|
// layer payment bid has nothing to cover on-chain
|
|
111
115
|
if (bid.value > 0 && !state.canBuilderCoverBid(bid.builderIndex, bid.value)) {
|
|
@@ -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;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {NotReorgedReason} from "@lodestar/fork-choice";
|
|
3
3
|
import {ArchiveStoreTask} from "../../chain/archiveStore/archiveStore.js";
|
|
4
4
|
import {FrequencyStateArchiveStep} from "../../chain/archiveStore/strategies/frequencyStateArchiveStrategy.js";
|
|
5
|
+
import type {LateCanonicalBlockReason} from "../../chain/archiveStore/utils/archiveBlocks.js";
|
|
5
6
|
import {BlockInputSource} from "../../chain/blocks/blockInput/index.js";
|
|
6
7
|
import {PayloadErrorCode} from "../../chain/blocks/importExecutionPayload.js";
|
|
7
8
|
import {
|
|
@@ -1047,6 +1048,11 @@ export function createLodestarMetrics(
|
|
|
1047
1048
|
name: "lodestar_import_block_set_head_after_cutoff_total",
|
|
1048
1049
|
help: "Total times an imported block is set as head after ATTESTATION_DUE_BPS of the slot",
|
|
1049
1050
|
}),
|
|
1051
|
+
lateCanonicalBlock: register.counter<{reason: LateCanonicalBlockReason}>({
|
|
1052
|
+
name: "lodestar_import_block_late_canonical_total",
|
|
1053
|
+
help: "Total finalized-canonical blocks this node imported after the attestation cutoff; reason distinguishes this node's processing lag (slow_import) from late reception (late_receive)",
|
|
1054
|
+
labelNames: ["reason"],
|
|
1055
|
+
}),
|
|
1050
1056
|
bySource: register.gauge<{source: BlockInputSource}>({
|
|
1051
1057
|
name: "lodestar_import_block_by_source_total",
|
|
1052
1058
|
help: "Total number of imported blocks by source",
|
|
@@ -1352,11 +1358,6 @@ export function createLodestarMetrics(
|
|
|
1352
1358
|
help: "Total number of InsertOutcome as a result of adding an execution payload bid from gossip to the pool",
|
|
1353
1359
|
labelNames: ["insertOutcome"],
|
|
1354
1360
|
}),
|
|
1355
|
-
apiInsertOutcome: register.counter<{insertOutcome: InsertOutcome}>({
|
|
1356
|
-
name: "lodestar_oppool_execution_payload_bid_pool_api_insert_outcome_total",
|
|
1357
|
-
help: "Total number of InsertOutcome as a result of adding an execution payload bid from api to the pool",
|
|
1358
|
-
labelNames: ["insertOutcome"],
|
|
1359
|
-
}),
|
|
1360
1361
|
apiValidationTime: register.histogram({
|
|
1361
1362
|
name: "lodestar_api_execution_payload_bid_validation_time_seconds",
|
|
1362
1363
|
help: "Time elapsed for signed execution payload bid validation - api path",
|
package/src/network/network.ts
CHANGED
|
@@ -481,7 +481,7 @@ export class Network implements INetwork {
|
|
|
481
481
|
}
|
|
482
482
|
|
|
483
483
|
async publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number> {
|
|
484
|
-
const epoch = computeEpochAtSlot(update.
|
|
484
|
+
const epoch = computeEpochAtSlot(update.attestedHeader.beacon.slot);
|
|
485
485
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
486
486
|
|
|
487
487
|
return this.publishGossip<GossipType.light_client_finality_update>(
|
|
@@ -491,7 +491,7 @@ export class Network implements INetwork {
|
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
async publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number> {
|
|
494
|
-
const epoch = computeEpochAtSlot(update.
|
|
494
|
+
const epoch = computeEpochAtSlot(update.attestedHeader.beacon.slot);
|
|
495
495
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
496
496
|
|
|
497
497
|
return this.publishGossip<GossipType.light_client_optimistic_update>(
|