@lodestar/beacon-node 1.44.0-dev.552cdce8d0 → 1.44.0-dev.5a822af737
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 +50 -29
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +1 -26
- package/lib/api/impl/beacon/pool/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/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +69 -12
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +28 -0
- 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 +121 -69
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +0 -4
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +5 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +5 -3
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +10 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +2 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +0 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +0 -3
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +17 -1
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +3 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +6 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts +4 -4
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +24 -11
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +7 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +108 -19
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -1
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +0 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +0 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +0 -2
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/block.d.ts +5 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +4 -14
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts +7 -3
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +70 -16
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +0 -2
- 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 +24 -4
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +1 -0
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +16 -0
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/builder/index.d.ts +1 -2
- package/lib/execution/builder/index.d.ts.map +1 -1
- package/lib/execution/builder/index.js +0 -1
- package/lib/execution/builder/index.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -0
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/types.d.ts +2 -0
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +2 -0
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +5 -1
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +14 -3
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -2
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +5 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +21 -6
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +2 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +25 -23
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +4 -4
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +22 -3
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/sync/types.d.ts +9 -1
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +9 -2
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +64 -30
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +16 -11
- package/lib/util/dataColumns.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/package.json +15 -17
- package/src/api/impl/beacon/blocks/index.ts +60 -38
- package/src/api/impl/beacon/pool/index.ts +4 -42
- package/src/api/impl/config/constants.ts +2 -0
- package/src/api/impl/debug/index.ts +73 -12
- package/src/api/impl/lodestar/index.ts +30 -0
- package/src/api/impl/validator/index.ts +141 -82
- package/src/chain/archiveStore/archiveStore.ts +0 -5
- package/src/chain/blocks/importBlock.ts +10 -2
- package/src/chain/blocks/importExecutionPayload.ts +8 -2
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -4
- package/src/chain/chain.ts +12 -1
- package/src/chain/emitter.ts +3 -2
- package/src/chain/errors/blockError.ts +0 -4
- package/src/chain/errors/executionPayloadBid.ts +13 -1
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/forkChoice/index.ts +27 -4
- package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
- package/src/chain/options.ts +1 -0
- package/src/chain/prepareNextSlot.ts +2 -1
- package/src/chain/produceBlock/produceBlockBody.ts +159 -26
- package/src/chain/regen/interface.ts +2 -1
- package/src/chain/regen/queued.ts +0 -5
- package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
- package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
- package/src/chain/stateCache/types.ts +0 -2
- package/src/chain/validation/block.ts +12 -16
- package/src/chain/validation/executionPayloadBid.ts +78 -19
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -2
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/chain/validatorMonitor.ts +18 -0
- package/src/execution/builder/index.ts +1 -4
- package/src/execution/engine/interface.ts +1 -0
- package/src/execution/engine/types.ts +4 -0
- package/src/metrics/metrics/lodestar.ts +15 -3
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +26 -6
- package/src/network/processor/index.ts +27 -27
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
- package/src/network/reqresp/handlers/index.ts +4 -4
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +21 -3
- package/src/sync/types.ts +11 -2
- package/src/sync/unknownBlock.ts +70 -31
- package/src/util/dataColumns.ts +17 -12
- package/src/util/sszBytes.ts +1 -1
- package/lib/execution/builder/utils.d.ts +0 -5
- package/lib/execution/builder/utils.d.ts.map +0 -1
- package/lib/execution/builder/utils.js +0 -17
- package/lib/execution/builder/utils.js.map +0 -1
- package/src/execution/builder/utils.ts +0 -19
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
createSingleSignatureSetFromComponents,
|
|
5
5
|
getExecutionPayloadBidSigningRoot,
|
|
6
6
|
isActiveBuilder,
|
|
7
|
+
isGasLimitTargetCompatible,
|
|
7
8
|
isStatePostGloas,
|
|
8
9
|
} from "@lodestar/state-transition";
|
|
9
|
-
import {gloas} from "@lodestar/types";
|
|
10
|
+
import {ValidatorIndex, gloas} from "@lodestar/types";
|
|
10
11
|
import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
|
|
11
12
|
import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
|
|
12
13
|
import {ExecutionPayloadBidError, ExecutionPayloadBidErrorCode, GossipAction} from "../errors/index.js";
|
|
@@ -16,28 +17,24 @@ import {RegenCaller} from "../regen/index.js";
|
|
|
16
17
|
export async function validateApiExecutionPayloadBid(
|
|
17
18
|
chain: IBeaconChain,
|
|
18
19
|
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
19
|
-
): Promise<
|
|
20
|
+
): Promise<{proposerIndex: ValidatorIndex}> {
|
|
20
21
|
return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export async function validateGossipExecutionPayloadBid(
|
|
24
25
|
chain: IBeaconChain,
|
|
25
26
|
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
26
|
-
): Promise<
|
|
27
|
+
): Promise<{proposerIndex: ValidatorIndex}> {
|
|
27
28
|
return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
async function validateExecutionPayloadBid(
|
|
31
32
|
chain: IBeaconChain,
|
|
32
33
|
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
33
|
-
): Promise<
|
|
34
|
+
): Promise<{proposerIndex: ValidatorIndex}> {
|
|
34
35
|
const bid = signedExecutionPayloadBid.message;
|
|
35
36
|
const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
|
|
36
37
|
const parentBlockHashHex = toRootHex(bid.parentBlockHash);
|
|
37
|
-
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipExecutionPayloadBid);
|
|
38
|
-
if (!isStatePostGloas(state)) {
|
|
39
|
-
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
40
|
-
}
|
|
41
38
|
|
|
42
39
|
// [IGNORE] `bid.slot` is the current slot or the next slot.
|
|
43
40
|
const currentSlot = chain.clock.currentSlot;
|
|
@@ -60,6 +57,17 @@ async function validateExecutionPayloadBid(
|
|
|
60
57
|
});
|
|
61
58
|
}
|
|
62
59
|
|
|
60
|
+
// [REJECT] The bid is for a higher slot than its parent block -- i.e.
|
|
61
|
+
// validate that `bid.slot` is greater than the slot of the block with root
|
|
62
|
+
// `bid.parent_block_root`.
|
|
63
|
+
if (bid.slot <= parentBlock.slot) {
|
|
64
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
65
|
+
code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT,
|
|
66
|
+
parentSlot: parentBlock.slot,
|
|
67
|
+
slot: bid.slot,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
63
71
|
// [IGNORE] A `SignedProposerPreferences` matching `bid.slot` and the bid's branch has been
|
|
64
72
|
// seen — i.e. `proposal_slot == bid.slot` AND `dependent_root ==
|
|
65
73
|
// get_proposer_dependent_root(parent_state, compute_epoch_at_slot(bid.slot))`.
|
|
@@ -99,9 +107,31 @@ async function validateExecutionPayloadBid(
|
|
|
99
107
|
});
|
|
100
108
|
}
|
|
101
109
|
|
|
110
|
+
// Use the bid's parent branch state for builder checks
|
|
111
|
+
const state = await chain.regen
|
|
112
|
+
.getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateGossipExecutionPayloadBid)
|
|
113
|
+
.catch(() => {
|
|
114
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
115
|
+
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
116
|
+
parentBlockRoot: parentBlockRootHex,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!isStatePostGloas(state)) {
|
|
121
|
+
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
102
124
|
// [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
|
|
103
125
|
// `is_active_builder(state, bid.builder_index)` returns `True`.
|
|
104
|
-
|
|
126
|
+
let builder: gloas.Builder;
|
|
127
|
+
try {
|
|
128
|
+
builder = state.getBuilder(bid.builderIndex);
|
|
129
|
+
} catch {
|
|
130
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
131
|
+
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
132
|
+
builderIndex: bid.builderIndex,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
105
135
|
if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
|
|
106
136
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
107
137
|
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
@@ -128,14 +158,33 @@ async function validateExecutionPayloadBid(
|
|
|
128
158
|
});
|
|
129
159
|
}
|
|
130
160
|
|
|
131
|
-
// [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
161
|
+
// [IGNORE] `bid.parent_block_hash` is the block hash of a known execution payload in fork
|
|
162
|
+
// choice. Looks up the variant of `bid.parent_block_root` whose payload hash matches
|
|
163
|
+
// `bid.parent_block_hash` — works for both FULL parents (FULL variant carries the delivered
|
|
164
|
+
// payload's hash) and EMPTY parents (EMPTY/PENDING variants carry the inherited parent
|
|
165
|
+
// payload's hash, since the new block doesn't have its own payload). Variant carries the
|
|
166
|
+
// executed payload's gas_limit, which we use as `parent_gas_limit` below.
|
|
167
|
+
const parentPayloadVariant = chain.forkChoice.getBlockHexAndBlockHash(parentBlockRootHex, parentBlockHashHex);
|
|
168
|
+
if (parentPayloadVariant === null || parentPayloadVariant.executionPayloadBlockHash === null) {
|
|
169
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
170
|
+
code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH,
|
|
171
|
+
parentBlockHash: parentBlockHashHex,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// [IGNORE] `is_gas_limit_target_compatible(parent_gas_limit, bid.gas_limit, target_gas_limit)`,
|
|
176
|
+
// where `parent_gas_limit` is the `gas_limit` of the parent execution payload and
|
|
177
|
+
// `target_gas_limit` is `proposer_preferences.target_gas_limit`.
|
|
178
|
+
const bidGasLimit = bid.gasLimit;
|
|
179
|
+
const parentGasLimit = parentPayloadVariant.executionPayloadGasLimit;
|
|
180
|
+
const targetGasLimit = proposerPreferences.message.targetGasLimit;
|
|
181
|
+
if (!isGasLimitTargetCompatible(parentGasLimit, bidGasLimit, targetGasLimit)) {
|
|
182
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
135
183
|
code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_GAS_LIMIT_MISMATCH,
|
|
136
184
|
builderIndex: bid.builderIndex,
|
|
137
185
|
bidGasLimit,
|
|
138
|
-
|
|
186
|
+
parentGasLimit,
|
|
187
|
+
targetGasLimit,
|
|
139
188
|
});
|
|
140
189
|
}
|
|
141
190
|
|
|
@@ -166,11 +215,11 @@ async function validateExecutionPayloadBid(
|
|
|
166
215
|
// [IGNORE] this bid is the highest value bid seen for the tuple
|
|
167
216
|
// `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
|
|
168
217
|
const bestBid = chain.executionPayloadBidPool.getBestBid(bid.slot, parentBlockHashHex, parentBlockRootHex);
|
|
169
|
-
if (bestBid !== null && bestBid.value >= bid.value) {
|
|
218
|
+
if (bestBid !== null && bestBid.message.value >= bid.value) {
|
|
170
219
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
171
220
|
code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
|
|
172
221
|
bidValue: bid.value,
|
|
173
|
-
currentHighestBid: bestBid.value,
|
|
222
|
+
currentHighestBid: bestBid.message.value,
|
|
174
223
|
});
|
|
175
224
|
}
|
|
176
225
|
// [IGNORE] `bid.value` is less or equal than the builder's excess balance --
|
|
@@ -183,9 +232,17 @@ async function validateExecutionPayloadBid(
|
|
|
183
232
|
});
|
|
184
233
|
}
|
|
185
234
|
|
|
186
|
-
// [
|
|
187
|
-
//
|
|
188
|
-
|
|
235
|
+
// [REJECT] `bid.prev_randao` is the correct RANDAO mix -- i.e. validate that
|
|
236
|
+
// `bid.prev_randao == get_randao_mix(parent_state, get_current_epoch(parent_state))`.
|
|
237
|
+
const randaoMix = state.getRandaoMix(computeEpochAtSlot(state.slot));
|
|
238
|
+
if (!byteArrayEquals(bid.prevRandao, randaoMix)) {
|
|
239
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
240
|
+
code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO,
|
|
241
|
+
builderIndex: bid.builderIndex,
|
|
242
|
+
bidPrevRandao: toHex(bid.prevRandao),
|
|
243
|
+
expectedPrevRandao: toHex(randaoMix),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
189
246
|
|
|
190
247
|
// [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
|
|
191
248
|
const signatureSet = createSingleSignatureSetFromComponents(
|
|
@@ -204,4 +261,6 @@ async function validateExecutionPayloadBid(
|
|
|
204
261
|
|
|
205
262
|
// Valid
|
|
206
263
|
chain.seenExecutionPayloadBids.add(bid.slot, bid.builderIndex);
|
|
264
|
+
|
|
265
|
+
return {proposerIndex: proposerPreferences.message.validatorIndex};
|
|
207
266
|
}
|
|
@@ -35,8 +35,6 @@ async function validateExecutionPayloadEnvelope(
|
|
|
35
35
|
// [IGNORE] The envelope's block root `envelope.beacon_block_root` has been seen (via
|
|
36
36
|
// gossip or non-gossip sources) (a client MAY queue payload for processing once
|
|
37
37
|
// the block is retrieved).
|
|
38
|
-
// TODO GLOAS: Need to review this, we should queue the envelope for later
|
|
39
|
-
// processing if the block is not yet known, otherwise we would ignore it here
|
|
40
38
|
const block = chain.forkChoice.getBlockDefaultStatus(envelope.beaconBlockRoot);
|
|
41
39
|
if (block === null) {
|
|
42
40
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
@@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
|
|
|
8
8
|
import {toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
|
+
import {RegenCaller} from "../regen/index.js";
|
|
11
12
|
|
|
12
13
|
export type PayloadAttestationValidationResult = {
|
|
13
14
|
attDataRootHex: RootHex;
|
|
@@ -61,22 +62,43 @@ async function validatePayloadAttestationMessage(
|
|
|
61
62
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
62
63
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
63
64
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
64
|
-
|
|
65
|
+
const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot);
|
|
66
|
+
if (!block) {
|
|
65
67
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
66
68
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
67
69
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// [IGNORE] The block referenced by `data.beacon_block_root` is at slot `data.slot`,
|
|
74
|
+
// i.e. the block has `block.slot == data.slot`.
|
|
75
|
+
if (block.slot !== data.slot) {
|
|
76
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
77
|
+
code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT,
|
|
78
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
79
|
+
blockSlot: block.slot,
|
|
80
|
+
slot: data.slot,
|
|
81
|
+
});
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
// [REJECT] The message's block `data.beacon_block_root` passes validation.
|
|
77
85
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
78
86
|
// it is possible that the block didn't pass the validation
|
|
79
87
|
|
|
88
|
+
// Use the referenced block's branch state for the PTC committee check
|
|
89
|
+
const state = await chain.regen
|
|
90
|
+
.getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
|
|
91
|
+
.catch(() => {
|
|
92
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
93
|
+
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
94
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!isStatePostGloas(state)) {
|
|
99
|
+
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
// [REJECT] The message's validator index is within the payload committee in
|
|
81
103
|
// `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
|
|
82
104
|
// processing the block up to the current slot as determined by the fork choice.
|
|
@@ -66,6 +66,7 @@ export type ValidatorMonitor = {
|
|
|
66
66
|
delaySec: Seconds,
|
|
67
67
|
envelope: gloas.SignedExecutionPayloadEnvelope
|
|
68
68
|
): void;
|
|
69
|
+
registerExecutionPayloadBid(src: OpSource, proposerIndex: ValidatorIndex, bid: gloas.ExecutionPayloadBid): void;
|
|
69
70
|
registerImportedBlock(block: BeaconBlock, data: {proposerBalanceDelta: number}): void;
|
|
70
71
|
onPoolSubmitUnaggregatedAttestation(
|
|
71
72
|
seenTimestampSec: number,
|
|
@@ -459,6 +460,23 @@ export function createValidatorMonitor(
|
|
|
459
460
|
// TODO GLOAS: implement execution payload envelope monitoring
|
|
460
461
|
},
|
|
461
462
|
|
|
463
|
+
registerExecutionPayloadBid(src, proposerIndex, bid) {
|
|
464
|
+
if (!validators.has(proposerIndex)) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
log("Received an execution payload bid for monitored proposer", {
|
|
468
|
+
slot: bid.slot,
|
|
469
|
+
proposerIndex,
|
|
470
|
+
src,
|
|
471
|
+
builderIndex: bid.builderIndex,
|
|
472
|
+
gasLimit: bid.gasLimit,
|
|
473
|
+
value: bid.value.toString(),
|
|
474
|
+
parentBlockRoot: toRootHex(bid.parentBlockRoot),
|
|
475
|
+
parentBlockHash: toRootHex(bid.parentBlockHash),
|
|
476
|
+
blockHash: toRootHex(bid.blockHash),
|
|
477
|
+
});
|
|
478
|
+
},
|
|
479
|
+
|
|
462
480
|
registerImportedBlock(block, {proposerBalanceDelta}) {
|
|
463
481
|
const validator = validators.get(block.proposerIndex);
|
|
464
482
|
if (validator) {
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {Logger} from "@lodestar/logger";
|
|
3
3
|
import {Metrics} from "../../metrics/metrics.js";
|
|
4
|
-
import {IExecutionBuilder} from "./interface.js";
|
|
5
|
-
|
|
6
|
-
export {getExpectedGasLimit} from "./utils.js";
|
|
7
|
-
|
|
8
4
|
import {ExecutionBuilderHttp, ExecutionBuilderHttpOpts, defaultExecutionBuilderHttpOpts} from "./http.js";
|
|
5
|
+
import {IExecutionBuilder} from "./interface.js";
|
|
9
6
|
|
|
10
7
|
export {ExecutionBuilderHttp, defaultExecutionBuilderHttpOpts};
|
|
11
8
|
|
|
@@ -88,6 +88,7 @@ export type PayloadAttributes = {
|
|
|
88
88
|
withdrawals?: capella.Withdrawal[];
|
|
89
89
|
parentBeaconBlockRoot?: Uint8Array;
|
|
90
90
|
slotNumber?: number; // EIP-7843
|
|
91
|
+
targetGasLimit?: number; // GLOAS (PayloadAttributesV4, execution-apis#796)
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
export type VersionedHashes = Uint8Array[];
|
|
@@ -245,6 +245,8 @@ export type PayloadAttributesRpc = {
|
|
|
245
245
|
parentBeaconBlockRoot?: DATA;
|
|
246
246
|
/** QUANTITY, 64 Bits - value for the slot number field of the new payload (EIP-7843) */
|
|
247
247
|
slotNumber?: QUANTITY;
|
|
248
|
+
/** QUANTITY, 64 Bits - target value for the gasLimit field of the new payload (GLOAS, execution-apis#796) */
|
|
249
|
+
targetGasLimit?: QUANTITY;
|
|
248
250
|
};
|
|
249
251
|
|
|
250
252
|
export type ClientVersionRpc = {
|
|
@@ -425,6 +427,7 @@ export function serializePayloadAttributes(data: PayloadAttributes): PayloadAttr
|
|
|
425
427
|
withdrawals: data.withdrawals?.map(serializeWithdrawal),
|
|
426
428
|
parentBeaconBlockRoot: data.parentBeaconBlockRoot ? bytesToData(data.parentBeaconBlockRoot) : undefined,
|
|
427
429
|
slotNumber: data.slotNumber !== undefined ? numToQuantity(data.slotNumber) : undefined,
|
|
430
|
+
targetGasLimit: data.targetGasLimit !== undefined ? numToQuantity(data.targetGasLimit) : undefined,
|
|
428
431
|
};
|
|
429
432
|
}
|
|
430
433
|
|
|
@@ -442,6 +445,7 @@ export function deserializePayloadAttributes(data: PayloadAttributesRpc): Payloa
|
|
|
442
445
|
withdrawals: data.withdrawals?.map((withdrawal) => deserializeWithdrawal(withdrawal)),
|
|
443
446
|
parentBeaconBlockRoot: data.parentBeaconBlockRoot ? dataToBytes(data.parentBeaconBlockRoot, 32) : undefined,
|
|
444
447
|
slotNumber: data.slotNumber !== undefined ? quantityToNum(data.slotNumber) : undefined,
|
|
448
|
+
targetGasLimit: data.targetGasLimit !== undefined ? quantityToNum(data.targetGasLimit) : undefined,
|
|
445
449
|
};
|
|
446
450
|
}
|
|
447
451
|
|
|
@@ -610,6 +610,11 @@ export function createLodestarMetrics(
|
|
|
610
610
|
help: "The origination source of one of the BlockInputSync triggers",
|
|
611
611
|
labelNames: ["source"],
|
|
612
612
|
}),
|
|
613
|
+
payloadSource: register.counter<{source: BlockInputSource}>({
|
|
614
|
+
name: "lodestar_payload_input_sync_source_total",
|
|
615
|
+
help: "Count of payload (execution payload envelope) sync triggers, labeled by their source",
|
|
616
|
+
labelNames: ["source"],
|
|
617
|
+
}),
|
|
613
618
|
pendingBlocks: register.gauge({
|
|
614
619
|
name: "lodestar_sync_unknown_block_pending_blocks_size",
|
|
615
620
|
help: "Current size of UnknownBlockSync pending blocks cache",
|
|
@@ -861,6 +866,12 @@ export function createLodestarMetrics(
|
|
|
861
866
|
labelNames: ["numBlobs"],
|
|
862
867
|
}),
|
|
863
868
|
|
|
869
|
+
skippedSlots: register.histogram({
|
|
870
|
+
name: "lodestar_gossip_block_skipped_slots",
|
|
871
|
+
help: "Number of skipped slots between a gossip block and its parent (blockSlot - parentSlot - 1)",
|
|
872
|
+
buckets: [0, 1, 2, 4, 8, 16, 32],
|
|
873
|
+
}),
|
|
874
|
+
|
|
864
875
|
processBlockErrors: register.gauge<{error: BlockErrorCode | "NOT_BLOCK_ERROR"}>({
|
|
865
876
|
name: "lodestar_gossip_block_process_block_errors",
|
|
866
877
|
help: "Count of errors, by error type, while processing blocks",
|
|
@@ -987,10 +998,11 @@ export function createLodestarMetrics(
|
|
|
987
998
|
}),
|
|
988
999
|
},
|
|
989
1000
|
importPayload: {
|
|
990
|
-
|
|
991
|
-
name: "
|
|
992
|
-
help: "
|
|
1001
|
+
elapsedTimeTillImported: register.histogram<{source: PayloadEnvelopeInputSource}>({
|
|
1002
|
+
name: "lodestar_import_payload_elapsed_time_till_imported_seconds",
|
|
1003
|
+
help: "Time elapsed between slot time and the time execution payload envelope is imported (added to fork choice)",
|
|
993
1004
|
labelNames: ["source"],
|
|
1005
|
+
buckets: [1, 2, 3, 6, 9, 12],
|
|
994
1006
|
}),
|
|
995
1007
|
columnsBySource: register.gauge<{source: PayloadEnvelopeInputSource}>({
|
|
996
1008
|
name: "lodestar_import_payload_columns_by_source_total",
|
package/src/network/interface.ts
CHANGED
|
@@ -113,6 +113,7 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
113
113
|
publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
|
|
114
114
|
publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
|
|
115
115
|
publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
|
|
116
|
+
publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number>;
|
|
116
117
|
publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number>;
|
|
117
118
|
publishProposerPreferences(signedProposerPreferences: gloas.SignedProposerPreferences): Promise<number>;
|
|
118
119
|
|
package/src/network/network.ts
CHANGED
|
@@ -515,6 +515,17 @@ export class Network implements INetwork {
|
|
|
515
515
|
);
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
async publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number> {
|
|
519
|
+
const epoch = computeEpochAtSlot(signedBid.message.slot);
|
|
520
|
+
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
521
|
+
|
|
522
|
+
return this.publishGossip<GossipType.execution_payload_bid>(
|
|
523
|
+
{type: GossipType.execution_payload_bid, boundary},
|
|
524
|
+
signedBid,
|
|
525
|
+
{ignoreDuplicatePublishError: true}
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
518
529
|
async publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number> {
|
|
519
530
|
const epoch = computeEpochAtSlot(payloadAttestationMessage.data.slot);
|
|
520
531
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
@@ -185,7 +185,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
185
185
|
peerIdStr,
|
|
186
186
|
});
|
|
187
187
|
try {
|
|
188
|
-
await validateGossipBlock(config, chain, signedBlock, fork);
|
|
188
|
+
const {skippedSlots} = await validateGossipBlock(config, chain, signedBlock, fork);
|
|
189
189
|
|
|
190
190
|
if (isForkPostGloas(fork)) {
|
|
191
191
|
chain.seenPayloadEnvelopeInputCache.add({
|
|
@@ -205,8 +205,15 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
205
205
|
|
|
206
206
|
metrics?.gossipBlock.gossipValidation.recvToValidation.observe(recvToValidation);
|
|
207
207
|
metrics?.gossipBlock.gossipValidation.validationTime.observe(validationTime);
|
|
208
|
+
metrics?.gossipBlock.skippedSlots.observe(skippedSlots);
|
|
208
209
|
|
|
209
|
-
logger.debug("Validated gossip block", {
|
|
210
|
+
logger.debug("Validated gossip block", {
|
|
211
|
+
...blockInputMeta,
|
|
212
|
+
...logCtx,
|
|
213
|
+
recvToValidation,
|
|
214
|
+
validationTime,
|
|
215
|
+
skippedSlots,
|
|
216
|
+
});
|
|
210
217
|
|
|
211
218
|
chain.emitter.emit(routes.events.EventType.blockGossip, {slot, block: blockRootHex});
|
|
212
219
|
|
|
@@ -1117,7 +1124,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1117
1124
|
}
|
|
1118
1125
|
|
|
1119
1126
|
const slot = envelope.payload.slotNumber;
|
|
1120
|
-
const delaySec =
|
|
1127
|
+
const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
|
|
1128
|
+
|
|
1129
|
+
logger.debug("Received gossip payload envelope", {
|
|
1130
|
+
currentSlot: chain.clock.currentSlot,
|
|
1131
|
+
peerId: peerIdStr,
|
|
1132
|
+
slot,
|
|
1133
|
+
blockRoot: toRootHex(envelope.beaconBlockRoot),
|
|
1134
|
+
delaySec,
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1121
1137
|
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
1122
1138
|
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
|
|
1123
1139
|
|
|
@@ -1205,8 +1221,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1205
1221
|
}
|
|
1206
1222
|
chain.forkChoice.notifyPtcMessages(
|
|
1207
1223
|
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1224
|
+
payloadAttestationMessage.data.slot,
|
|
1208
1225
|
validationResult.validatorCommitteeIndices,
|
|
1209
|
-
payloadAttestationMessage.data.payloadPresent
|
|
1226
|
+
payloadAttestationMessage.data.payloadPresent,
|
|
1227
|
+
payloadAttestationMessage.data.blobDataAvailable
|
|
1210
1228
|
);
|
|
1211
1229
|
},
|
|
1212
1230
|
[GossipType.execution_payload_bid]: async ({
|
|
@@ -1215,16 +1233,18 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1215
1233
|
}: GossipHandlerParamGeneric<GossipType.execution_payload_bid>) => {
|
|
1216
1234
|
const {serializedData} = gossipData;
|
|
1217
1235
|
const executionPayloadBid = sszDeserialize(topic, serializedData);
|
|
1218
|
-
await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
|
|
1236
|
+
const {proposerIndex} = await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
|
|
1219
1237
|
|
|
1220
1238
|
// Handle valid payload bid by storing in a bid pool
|
|
1221
1239
|
try {
|
|
1222
|
-
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid
|
|
1240
|
+
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
|
|
1223
1241
|
metrics?.opPool.executionPayloadBidPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1224
1242
|
} catch (e) {
|
|
1225
1243
|
logger.error("Error adding to executionPayloadBid pool", {}, e as Error);
|
|
1226
1244
|
}
|
|
1227
1245
|
|
|
1246
|
+
chain.validatorMonitor?.registerExecutionPayloadBid(OpSource.gossip, proposerIndex, executionPayloadBid.message);
|
|
1247
|
+
|
|
1228
1248
|
chain.emitter.emit(routes.events.EventType.executionPayloadBid, {
|
|
1229
1249
|
version: config.getForkName(executionPayloadBid.message.slot),
|
|
1230
1250
|
data: executionPayloadBid,
|
|
@@ -186,9 +186,11 @@ export class NetworkProcessor {
|
|
|
186
186
|
// we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
|
|
187
187
|
// to be stored in this Map and reprocessed once the block comes
|
|
188
188
|
private readonly awaitingMessagesByBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
|
|
189
|
+
private awaitingBlockMessageCount = 0;
|
|
189
190
|
// we may not receive the payload for messages that require the FULL payload variant to be processed,
|
|
190
191
|
// in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
|
|
191
192
|
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
|
|
193
|
+
private awaitingPayloadMessageCount = 0;
|
|
192
194
|
private unknownBlocksBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
193
195
|
private unknownEnvelopesBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
194
196
|
|
|
@@ -228,8 +230,8 @@ export class NetworkProcessor {
|
|
|
228
230
|
metrics.gossipValidationQueue.keySize.set({topic}, this.gossipQueues[topic].keySize);
|
|
229
231
|
metrics.gossipValidationQueue.concurrency.set({topic}, this.gossipTopicConcurrency[topic]);
|
|
230
232
|
}
|
|
231
|
-
metrics.awaitingBlockGossipMessages.countPerSlot.set(this.
|
|
232
|
-
metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.
|
|
233
|
+
metrics.awaitingBlockGossipMessages.countPerSlot.set(this.awaitingBlockMessageCount);
|
|
234
|
+
metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.awaitingPayloadMessageCount);
|
|
233
235
|
// specific metric for beacon_attestation topic
|
|
234
236
|
metrics.gossipValidationQueue.keyAge.reset();
|
|
235
237
|
for (const ageMs of this.gossipQueues.beacon_attestation.getDataAgeMs()) {
|
|
@@ -297,7 +299,7 @@ export class NetworkProcessor {
|
|
|
297
299
|
return;
|
|
298
300
|
}
|
|
299
301
|
this.unknownEnvelopesBySlot.getOrDefault(slot).add(root);
|
|
300
|
-
this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, peer, source});
|
|
302
|
+
this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, slot, peer, source});
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
|
|
@@ -497,7 +499,7 @@ export class NetworkProcessor {
|
|
|
497
499
|
this.pushPendingGossipsubMessageToQueue(message);
|
|
498
500
|
break;
|
|
499
501
|
case PreprocessAction.AwaitBlock: {
|
|
500
|
-
if (this.
|
|
502
|
+
if (this.awaitingBlockMessageCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
|
|
501
503
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
502
504
|
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
503
505
|
reason: ReprocessRejectReason.reached_limit,
|
|
@@ -509,10 +511,11 @@ export class NetworkProcessor {
|
|
|
509
511
|
this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
|
|
510
512
|
const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
|
|
511
513
|
awaitingGossipsubMessages.add(message);
|
|
514
|
+
this.awaitingBlockMessageCount++;
|
|
512
515
|
break;
|
|
513
516
|
}
|
|
514
517
|
case PreprocessAction.AwaitEnvelope: {
|
|
515
|
-
if (this.
|
|
518
|
+
if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
|
|
516
519
|
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
517
520
|
reason: ReprocessRejectReason.reached_limit,
|
|
518
521
|
topic: topicType,
|
|
@@ -525,6 +528,7 @@ export class NetworkProcessor {
|
|
|
525
528
|
preprocessResult.root
|
|
526
529
|
);
|
|
527
530
|
awaitingPayloadGossipsubMessages.add(message);
|
|
531
|
+
this.awaitingPayloadMessageCount++;
|
|
528
532
|
break;
|
|
529
533
|
}
|
|
530
534
|
}
|
|
@@ -548,6 +552,12 @@ export class NetworkProcessor {
|
|
|
548
552
|
return;
|
|
549
553
|
}
|
|
550
554
|
|
|
555
|
+
// Atomically remove from map and update counter before async iteration to
|
|
556
|
+
// prevent double-decrement race with onClockSlot during yield points below
|
|
557
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
558
|
+
this.awaitingBlockMessageCount -= waitingGossipsubMessages.size;
|
|
559
|
+
}
|
|
560
|
+
|
|
551
561
|
const nowSec = Date.now() / 1000;
|
|
552
562
|
let count = 0;
|
|
553
563
|
// TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
|
|
@@ -567,8 +577,6 @@ export class NetworkProcessor {
|
|
|
567
577
|
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
568
578
|
}
|
|
569
579
|
}
|
|
570
|
-
|
|
571
|
-
this.awaitingMessagesByBlockRoot.delete(rootHex);
|
|
572
580
|
};
|
|
573
581
|
|
|
574
582
|
private onPayloadEnvelopeProcessed = async ({blockRoot: rootHex}: {blockRoot: RootHex}): Promise<void> => {
|
|
@@ -577,6 +585,12 @@ export class NetworkProcessor {
|
|
|
577
585
|
return;
|
|
578
586
|
}
|
|
579
587
|
|
|
588
|
+
// Atomically remove from map and update counter before async iteration to
|
|
589
|
+
// prevent double-decrement race with onClockSlot during yield points below
|
|
590
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
591
|
+
this.awaitingPayloadMessageCount -= waitingGossipsubMessages.size;
|
|
592
|
+
}
|
|
593
|
+
|
|
580
594
|
const nowSec = Date.now() / 1000;
|
|
581
595
|
let count = 0;
|
|
582
596
|
for (const message of waitingGossipsubMessages) {
|
|
@@ -593,8 +607,6 @@ export class NetworkProcessor {
|
|
|
593
607
|
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
594
608
|
}
|
|
595
609
|
}
|
|
596
|
-
|
|
597
|
-
this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
|
|
598
610
|
};
|
|
599
611
|
|
|
600
612
|
private onClockSlot = (clockSlot: Slot): void => {
|
|
@@ -618,7 +630,9 @@ export class NetworkProcessor {
|
|
|
618
630
|
);
|
|
619
631
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
620
632
|
}
|
|
621
|
-
this.awaitingMessagesByBlockRoot.delete(rootHex)
|
|
633
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
634
|
+
this.awaitingBlockMessageCount -= gossipMessages.size;
|
|
635
|
+
}
|
|
622
636
|
}
|
|
623
637
|
}
|
|
624
638
|
this.unknownBlocksBySlot.delete(slot);
|
|
@@ -641,7 +655,9 @@ export class NetworkProcessor {
|
|
|
641
655
|
);
|
|
642
656
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
643
657
|
}
|
|
644
|
-
this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)
|
|
658
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
659
|
+
this.awaitingPayloadMessageCount -= gossipMessages.size;
|
|
660
|
+
}
|
|
645
661
|
}
|
|
646
662
|
}
|
|
647
663
|
this.unknownEnvelopesBySlot.delete(slot);
|
|
@@ -784,20 +800,4 @@ export class NetworkProcessor {
|
|
|
784
800
|
|
|
785
801
|
return null;
|
|
786
802
|
}
|
|
787
|
-
|
|
788
|
-
private get unknownBlockGossipsubMessagesCount(): number {
|
|
789
|
-
let count = 0;
|
|
790
|
-
for (const messages of this.awaitingMessagesByBlockRoot.values()) {
|
|
791
|
-
count += messages.size;
|
|
792
|
-
}
|
|
793
|
-
return count;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
private get unknownPayloadGossipsubMessagesCount(): number {
|
|
797
|
-
let count = 0;
|
|
798
|
-
for (const messages of this.awaitingMessagesByPayloadBlockRoot.values()) {
|
|
799
|
-
count += messages.size;
|
|
800
|
-
}
|
|
801
|
-
return count;
|
|
802
|
-
}
|
|
803
803
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {PeerId} from "@libp2p/interface";
|
|
2
2
|
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
-
import {GENESIS_SLOT, isForkPostDeneb
|
|
3
|
+
import {GENESIS_SLOT, isForkPostDeneb} from "@lodestar/params";
|
|
4
4
|
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
|
|
5
5
|
import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
6
6
|
import {deneb, phase0} from "@lodestar/types";
|
|
@@ -29,13 +29,20 @@ export async function* onBeaconBlocksByRange(
|
|
|
29
29
|
// starts above it to avoid duplicate yields. See archiveBlocks.ts for the migration logic.
|
|
30
30
|
const archiveMaxSlot = finalizedSlot;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// endSlot is exclusive, so highest served slot is endSlot - 1.
|
|
33
|
+
// Throw only when the entire requested range is below earliestAvailableSlot.
|
|
34
|
+
if (endSlot - 1 < chain.earliestAvailableSlot) {
|
|
35
|
+
chain.logger.verbose("Peer requested range before earliestAvailableSlot for BeaconBlocksByRange", {
|
|
35
36
|
peer: prettyPrintPeerId(peerId),
|
|
36
37
|
client: peerClient,
|
|
38
|
+
startSlot,
|
|
39
|
+
count,
|
|
40
|
+
earliestAvailableSlot: chain.earliestAvailableSlot,
|
|
37
41
|
});
|
|
38
|
-
|
|
42
|
+
throw new ResponseError(
|
|
43
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
44
|
+
`Requested range is before earliestAvailableSlot startSlot=${startSlot} count=${count} earliestAvailableSlot=${chain.earliestAvailableSlot}`
|
|
45
|
+
);
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
// Finalized range of blocks
|