@lodestar/beacon-node 1.50.0-dev.fda8006fc3 → 1.50.0-rc.0
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/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +26 -5
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +26 -0
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +13 -23
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/beaconProposerCache.d.ts +6 -0
- package/lib/chain/beaconProposerCache.d.ts.map +1 -1
- package/lib/chain/beaconProposerCache.js +16 -0
- package/lib/chain/beaconProposerCache.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +14 -8
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +6 -6
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/chain.d.ts +3 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +55 -19
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -0
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/opPool.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.js +1 -23
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -0
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +13 -1
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +11 -0
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +22 -0
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +11 -3
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +8 -0
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +40 -28
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +18 -8
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +72 -18
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/db/beacon.d.ts +4 -1
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +6 -1
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +3 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +2 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +4 -1
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
- package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
- package/lib/db/repositories/earliestAvailableSlot.js +19 -0
- package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
- package/lib/db/repositories/index.d.ts +1 -0
- package/lib/db/repositories/index.d.ts.map +1 -1
- package/lib/db/repositories/index.js +1 -0
- package/lib/db/repositories/index.js.map +1 -1
- package/lib/db/repositories/proposerPreferences.d.ts +14 -0
- package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
- package/lib/db/repositories/proposerPreferences.js +21 -0
- package/lib/db/repositories/proposerPreferences.js.map +1 -0
- package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
- package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
- package/lib/execution/engine/blobVersionedHashes.js +74 -0
- package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
- package/lib/execution/engine/http.d.ts +45 -13
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +236 -198
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/index.d.ts +3 -2
- package/lib/execution/engine/index.d.ts.map +1 -1
- package/lib/execution/engine/index.js +33 -8
- package/lib/execution/engine/index.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -1
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
- package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
- package/lib/execution/engine/jsonRpcTransport.js +158 -0
- package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
- package/lib/execution/engine/restHttpClient.d.ts +71 -0
- package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
- package/lib/execution/engine/restHttpClient.js +201 -0
- package/lib/execution/engine/restHttpClient.js.map +1 -0
- package/lib/execution/engine/restTransport.d.ts +46 -0
- package/lib/execution/engine/restTransport.d.ts.map +1 -0
- package/lib/execution/engine/restTransport.js +399 -0
- package/lib/execution/engine/restTransport.js.map +1 -0
- package/lib/execution/engine/sszTypes.d.ts +1261 -0
- package/lib/execution/engine/sszTypes.d.ts.map +1 -0
- package/lib/execution/engine/sszTypes.js +216 -0
- package/lib/execution/engine/sszTypes.js.map +1 -0
- package/lib/execution/engine/transport.d.ts +39 -0
- package/lib/execution/engine/transport.d.ts.map +1 -0
- package/lib/execution/engine/transport.js +2 -0
- package/lib/execution/engine/transport.js.map +1 -0
- 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 +24 -57
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/execution/engine/utils.d.ts.map +1 -1
- package/lib/execution/engine/utils.js +9 -2
- package/lib/execution/engine/utils.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +10 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +22 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/core/events.d.ts +5 -1
- package/lib/network/core/events.d.ts.map +1 -1
- package/lib/network/core/events.js +8 -0
- package/lib/network/core/events.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +60 -64
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/node/nodejs.d.ts +3 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -1
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/options.d.ts +3 -2
- package/lib/node/options.d.ts.map +1 -1
- package/lib/node/options.js +2 -2
- package/lib/node/options.js.map +1 -1
- package/lib/sync/range/batch.d.ts +5 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +11 -17
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +4 -4
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +7 -5
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +25 -11
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +5 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +44 -20
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +38 -14
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +84 -39
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/util/asyncIterableToEvents.d.ts +10 -2
- package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
- package/lib/util/asyncIterableToEvents.js +29 -3
- package/lib/util/asyncIterableToEvents.js.map +1 -1
- package/lib/util/metadata.d.ts.map +1 -1
- package/lib/util/metadata.js +2 -1
- package/lib/util/metadata.js.map +1 -1
- package/package.json +17 -16
- package/src/api/impl/beacon/state/index.ts +41 -5
- package/src/api/impl/validator/index.ts +37 -0
- package/src/chain/archiveStore/archiveStore.ts +18 -24
- package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
- package/src/chain/beaconProposerCache.ts +19 -1
- package/src/chain/blocks/importBlock.ts +17 -7
- package/src/chain/blocks/verifyBlock.ts +7 -6
- package/src/chain/chain.ts +68 -18
- package/src/chain/interface.ts +2 -0
- package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
- package/src/chain/opPools/opPool.ts +1 -31
- package/src/chain/opPools/proposerPreferencesPool.ts +20 -1
- package/src/chain/opPools/utils.ts +31 -0
- package/src/chain/prepareNextSlot.ts +15 -3
- package/src/chain/produceBlock/produceBlockBody.ts +68 -34
- package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
- package/src/chain/shufflingCache.ts +84 -22
- package/src/db/beacon.ts +6 -0
- package/src/db/buckets.ts +2 -0
- package/src/db/interface.ts +4 -0
- package/src/db/repositories/earliestAvailableSlot.ts +19 -0
- package/src/db/repositories/index.ts +1 -0
- package/src/db/repositories/proposerPreferences.ts +23 -0
- package/src/execution/engine/blobVersionedHashes.ts +72 -0
- package/src/execution/engine/http.ts +304 -271
- package/src/execution/engine/index.ts +41 -7
- package/src/execution/engine/interface.ts +1 -1
- package/src/execution/engine/jsonRpcTransport.ts +247 -0
- package/src/execution/engine/restHttpClient.ts +269 -0
- package/src/execution/engine/restTransport.ts +544 -0
- package/src/execution/engine/sszTypes.ts +354 -0
- package/src/execution/engine/transport.ts +61 -0
- package/src/execution/engine/types.ts +34 -74
- package/src/execution/engine/utils.ts +12 -2
- package/src/metrics/metrics/lodestar.ts +23 -0
- package/src/network/core/events.ts +16 -1
- package/src/network/processor/gossipHandlers.ts +91 -77
- package/src/node/nodejs.ts +4 -0
- package/src/node/options.ts +10 -1
- package/src/sync/range/batch.ts +14 -19
- package/src/sync/range/chain.ts +9 -7
- package/src/sync/range/range.ts +26 -12
- package/src/sync/unknownBlock.ts +57 -21
- package/src/sync/utils/downloadByRange.ts +123 -58
- package/src/util/asyncIterableToEvents.ts +44 -5
- package/src/util/metadata.ts +2 -1
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import EventEmitter from "node:events";
|
|
2
2
|
import {ResponseIncoming, ResponseOutgoing} from "@lodestar/reqresp";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AsyncIterableEventBus,
|
|
5
|
+
IteratorEvent,
|
|
6
|
+
RequestCancelEvent,
|
|
7
|
+
RequestEvent,
|
|
8
|
+
} from "../../util/asyncIterableToEvents.js";
|
|
4
9
|
import {StrictEventEmitterSingleArg} from "../../util/strictEvents.js";
|
|
5
10
|
import {EventDirection} from "../events.js";
|
|
6
11
|
import {IncomingRequestArgs, OutgoingRequestArgs} from "../reqresp/types.js";
|
|
7
12
|
|
|
8
13
|
export enum ReqRespBridgeEvent {
|
|
9
14
|
outgoingRequest = "reqresp.outgoingRequest",
|
|
15
|
+
outgoingRequestCancel = "reqresp.outgoingRequestCancel",
|
|
10
16
|
outgoingResponse = "reqresp.outgoingResponse",
|
|
11
17
|
incomingRequest = "reqresp.incomingRequest",
|
|
18
|
+
incomingRequestCancel = "reqresp.incomingRequestCancel",
|
|
12
19
|
incomingResponse = "reqresp.incomingResponse",
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
export type ReqRespBridgeEventData = {
|
|
16
23
|
[ReqRespBridgeEvent.outgoingRequest]: RequestEvent<OutgoingRequestArgs>;
|
|
24
|
+
[ReqRespBridgeEvent.outgoingRequestCancel]: RequestCancelEvent;
|
|
17
25
|
[ReqRespBridgeEvent.outgoingResponse]: IteratorEvent<ResponseOutgoing>;
|
|
18
26
|
[ReqRespBridgeEvent.incomingRequest]: RequestEvent<IncomingRequestArgs>;
|
|
27
|
+
[ReqRespBridgeEvent.incomingRequestCancel]: RequestCancelEvent;
|
|
19
28
|
[ReqRespBridgeEvent.incomingResponse]: IteratorEvent<ResponseIncoming>;
|
|
20
29
|
};
|
|
21
30
|
|
|
@@ -26,8 +35,10 @@ export class ReqRespBridgeEventBus extends (EventEmitter as {new (): IReqRespBri
|
|
|
26
35
|
// NOTE: If the same event is on this two arrays it can create an infinite cycle
|
|
27
36
|
export const reqRespBridgeEventDirection: Record<ReqRespBridgeEvent, EventDirection> = {
|
|
28
37
|
[ReqRespBridgeEvent.outgoingRequest]: EventDirection.mainToWorker,
|
|
38
|
+
[ReqRespBridgeEvent.outgoingRequestCancel]: EventDirection.mainToWorker,
|
|
29
39
|
[ReqRespBridgeEvent.outgoingResponse]: EventDirection.mainToWorker,
|
|
30
40
|
[ReqRespBridgeEvent.incomingRequest]: EventDirection.workerToMain,
|
|
41
|
+
[ReqRespBridgeEvent.incomingRequestCancel]: EventDirection.workerToMain,
|
|
31
42
|
[ReqRespBridgeEvent.incomingResponse]: EventDirection.workerToMain,
|
|
32
43
|
};
|
|
33
44
|
|
|
@@ -36,8 +47,10 @@ export function getReqRespBridgeReqEvents(
|
|
|
36
47
|
): AsyncIterableEventBus<OutgoingRequestArgs, ResponseIncoming> {
|
|
37
48
|
return {
|
|
38
49
|
emitRequest: (data) => events.emit(ReqRespBridgeEvent.outgoingRequest, data),
|
|
50
|
+
emitRequestCancel: (data) => events.emit(ReqRespBridgeEvent.outgoingRequestCancel, data),
|
|
39
51
|
emitResponse: (data) => events.emit(ReqRespBridgeEvent.incomingResponse, data),
|
|
40
52
|
onRequest: (cb) => events.on(ReqRespBridgeEvent.outgoingRequest, cb),
|
|
53
|
+
onRequestCancel: (cb) => events.on(ReqRespBridgeEvent.outgoingRequestCancel, cb),
|
|
41
54
|
onResponse: (cb) => events.on(ReqRespBridgeEvent.incomingResponse, cb),
|
|
42
55
|
};
|
|
43
56
|
}
|
|
@@ -47,8 +60,10 @@ export function getReqRespBridgeRespEvents(
|
|
|
47
60
|
): AsyncIterableEventBus<IncomingRequestArgs, ResponseOutgoing> {
|
|
48
61
|
return {
|
|
49
62
|
emitRequest: (data) => events.emit(ReqRespBridgeEvent.incomingRequest, data),
|
|
63
|
+
emitRequestCancel: (data) => events.emit(ReqRespBridgeEvent.incomingRequestCancel, data),
|
|
50
64
|
emitResponse: (data) => events.emit(ReqRespBridgeEvent.outgoingResponse, data),
|
|
51
65
|
onRequest: (cb) => events.on(ReqRespBridgeEvent.incomingRequest, cb),
|
|
66
|
+
onRequestCancel: (cb) => events.on(ReqRespBridgeEvent.incomingRequestCancel, cb),
|
|
52
67
|
onResponse: (cb) => events.on(ReqRespBridgeEvent.outgoingResponse, cb),
|
|
53
68
|
};
|
|
54
69
|
}
|
|
@@ -12,9 +12,10 @@ import {
|
|
|
12
12
|
isForkPostElectra,
|
|
13
13
|
isForkPostGloas,
|
|
14
14
|
} from "@lodestar/params";
|
|
15
|
-
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
15
|
+
import {computeTimeAtSlot, signedBlockToSignedHeader} from "@lodestar/state-transition";
|
|
16
16
|
import {
|
|
17
17
|
Root,
|
|
18
|
+
RootHex,
|
|
18
19
|
SignedBeaconBlock,
|
|
19
20
|
SingleAttestation,
|
|
20
21
|
Slot,
|
|
@@ -78,6 +79,7 @@ import {
|
|
|
78
79
|
validateGossipSyncCommittee,
|
|
79
80
|
validateGossipVoluntaryExit,
|
|
80
81
|
validateSyncCommitteeGossipContributionAndProof,
|
|
82
|
+
verifyBlockProposerSignature,
|
|
81
83
|
} from "../../chain/validation/index.js";
|
|
82
84
|
import {validateLightClientFinalityUpdate} from "../../chain/validation/lightClientFinalityUpdate.js";
|
|
83
85
|
import {validateLightClientOptimisticUpdate} from "../../chain/validation/lightClientOptimisticUpdate.js";
|
|
@@ -148,37 +150,16 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
|
|
|
148
150
|
function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHandlerOpts): SequentialGossipHandlers {
|
|
149
151
|
const {chain, config, metrics, logger, core} = modules;
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Add a gossip block to the seen caches. The proposer signature of the block MUST be verified.
|
|
155
|
+
*/
|
|
156
|
+
function cacheSignatureVerifiedBlock(
|
|
152
157
|
signedBlock: SignedBeaconBlock,
|
|
158
|
+
blockRootHex: RootHex,
|
|
153
159
|
fork: ForkName,
|
|
154
160
|
peerIdStr: string,
|
|
155
161
|
seenTimestampSec: number
|
|
156
|
-
):
|
|
157
|
-
const slot = signedBlock.message.slot;
|
|
158
|
-
const forkTypes = config.getForkTypes(slot);
|
|
159
|
-
const blockRootHex = toRootHex(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message));
|
|
160
|
-
const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
|
|
161
|
-
const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
|
|
162
|
-
|
|
163
|
-
// always set block to seen cache for all forks so that we don't need to download it
|
|
164
|
-
// TODO: validate block before adding to cache
|
|
165
|
-
// tracked in https://github.com/ChainSafe/lodestar/issues/7957
|
|
166
|
-
|
|
167
|
-
const logCtx = {
|
|
168
|
-
slot,
|
|
169
|
-
root: blockRootHex,
|
|
170
|
-
currentSlot: chain.clock.currentSlot,
|
|
171
|
-
peerId: peerIdStr,
|
|
172
|
-
delaySec,
|
|
173
|
-
recvToValLatency,
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
logger.debug("Received gossip block", {...logCtx});
|
|
177
|
-
|
|
178
|
-
// optimistically add gossip block to the seen cache
|
|
179
|
-
// if validation fails, we will NOT forward this gossip block to peers
|
|
180
|
-
// - if PARENT_BLOCK_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
|
|
181
|
-
// - if other validator errors, blockInput will stay in the seen cache and will be pruned on finalization
|
|
162
|
+
): BlockInput {
|
|
182
163
|
const blockInput = chain.seenBlockInputCache.getByBlock({
|
|
183
164
|
block: signedBlock,
|
|
184
165
|
blockRootHex,
|
|
@@ -187,8 +168,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
187
168
|
peerIdStr,
|
|
188
169
|
});
|
|
189
170
|
|
|
190
|
-
//
|
|
191
|
-
// This ensures we have PayloadEnvelopeInput, even through "PARENT_BLOCK_UNKNOWN" error
|
|
171
|
+
// Also seed the payload-envelope cache, so we have PayloadEnvelopeInput even through "PARENT_BLOCK_UNKNOWN" error
|
|
192
172
|
// see https://github.com/ChainSafe/lodestar/issues/9475
|
|
193
173
|
if (isForkPostGloas(fork)) {
|
|
194
174
|
chain.seenPayloadEnvelopeInputCache.add({
|
|
@@ -202,8 +182,37 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
202
182
|
});
|
|
203
183
|
}
|
|
204
184
|
|
|
185
|
+
return blockInput;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function validateBeaconBlock(
|
|
189
|
+
signedBlock: SignedBeaconBlock,
|
|
190
|
+
fork: ForkName,
|
|
191
|
+
peerIdStr: string,
|
|
192
|
+
seenTimestampSec: number
|
|
193
|
+
): Promise<IBlockInput> {
|
|
194
|
+
const slot = signedBlock.message.slot;
|
|
195
|
+
const forkTypes = config.getForkTypes(slot);
|
|
196
|
+
const blockRootHex = toRootHex(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message));
|
|
197
|
+
const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
|
|
198
|
+
const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
|
|
199
|
+
|
|
200
|
+
const logCtx = {
|
|
201
|
+
slot,
|
|
202
|
+
root: blockRootHex,
|
|
203
|
+
currentSlot: chain.clock.currentSlot,
|
|
204
|
+
peerId: peerIdStr,
|
|
205
|
+
delaySec,
|
|
206
|
+
recvToValLatency,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
logger.debug("Received gossip block", {...logCtx});
|
|
210
|
+
|
|
211
|
+
chain.seenBlockInputCache.markValidatingBlock(blockRootHex);
|
|
212
|
+
|
|
205
213
|
try {
|
|
206
214
|
const {skippedSlots} = await validateGossipBlock(config, chain, signedBlock, fork);
|
|
215
|
+
const blockInput = cacheSignatureVerifiedBlock(signedBlock, blockRootHex, fork, peerIdStr, seenTimestampSec);
|
|
207
216
|
|
|
208
217
|
const blockInputMeta = blockInput.getLogMeta();
|
|
209
218
|
|
|
@@ -229,54 +238,54 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
229
238
|
if (e instanceof BlockGossipError) {
|
|
230
239
|
logger.debug("Gossip block has error", {slot, root: blockRootHex, code: e.type.code});
|
|
231
240
|
if (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
blockInput
|
|
241
|
+
e.type.code === BlockErrorCode.PARENT_BLOCK_UNKNOWN ||
|
|
242
|
+
e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN
|
|
235
243
|
) {
|
|
244
|
+
// The block is retained for unknown parent sync without full validation. Authenticate it first, this
|
|
245
|
+
// does not need the parent state
|
|
246
|
+
const proposerIndex = signedBlock.message.proposerIndex;
|
|
247
|
+
await verifyBlockProposerSignature(chain, signedBlock, blockRootHex);
|
|
248
|
+
chain.seenBlockProposers.observeBlockRoot(
|
|
249
|
+
slot,
|
|
250
|
+
proposerIndex,
|
|
251
|
+
blockRootHex,
|
|
252
|
+
signedBlockToSignedHeader(config, signedBlock)
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Check again after the signature verification so concurrent proposals cannot both be retained
|
|
256
|
+
if (chain.seenBlockProposers.isKnown(slot, proposerIndex)) {
|
|
257
|
+
if (chain.seenBlockProposers.isRepeatProposal(slot, proposerIndex, blockRootHex)) {
|
|
258
|
+
throw new BlockGossipError(GossipAction.IGNORE, {
|
|
259
|
+
code: BlockErrorCode.REPEAT_PROPOSAL,
|
|
260
|
+
proposerIndex,
|
|
261
|
+
root: blockRootHex,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRootHex});
|
|
265
|
+
}
|
|
266
|
+
chain.seenBlockProposers.add(slot, proposerIndex, blockRootHex);
|
|
267
|
+
|
|
268
|
+
const blockInput = cacheSignatureVerifiedBlock(signedBlock, blockRootHex, fork, peerIdStr, seenTimestampSec);
|
|
236
269
|
chain.emitter.emit(ChainEvent.blockUnknownParent, {
|
|
237
270
|
blockInput,
|
|
238
271
|
peer: peerIdStr,
|
|
239
272
|
source: BlockInputSource.gossip,
|
|
240
273
|
});
|
|
241
|
-
// throw error (don't prune the blockInput)
|
|
242
274
|
throw e;
|
|
243
275
|
}
|
|
244
276
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
|
|
254
|
-
!chain.seenBlockProposers.hasBlockRoot(slot, signedBlock.message.proposerIndex, blockRootHex)
|
|
255
|
-
) {
|
|
256
|
-
chain.seenBlockInputCache.remove(blockRootHex);
|
|
257
|
-
if (isForkPostGloas(fork)) {
|
|
258
|
-
chain.seenPayloadEnvelopeInputCache.remove(blockRootHex);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
throw e;
|
|
277
|
+
if (e.action === GossipAction.REJECT) {
|
|
278
|
+
chain.persistInvalidSszValue(
|
|
279
|
+
forkTypes.SignedBeaconBlock,
|
|
280
|
+
signedBlock,
|
|
281
|
+
`gossip_reject_slot_${slot}`,
|
|
282
|
+
blockRootHex
|
|
283
|
+
);
|
|
262
284
|
}
|
|
263
|
-
|
|
264
|
-
chain.persistInvalidSszValue(
|
|
265
|
-
forkTypes.SignedBeaconBlock,
|
|
266
|
-
signedBlock,
|
|
267
|
-
`gossip_reject_slot_${slot}`,
|
|
268
|
-
blockRootHex
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// REJECT or unexpected (non-BlockGossipError) error: drop the optimistically-added entries from
|
|
273
|
-
// both caches, keeping them consistent. The block may carry any parent root, so only its own entry is removed
|
|
274
|
-
chain.seenBlockInputCache.remove(blockRootHex);
|
|
275
|
-
if (isForkPostGloas(fork)) {
|
|
276
|
-
chain.seenPayloadEnvelopeInputCache.remove(blockRootHex);
|
|
277
285
|
}
|
|
278
286
|
throw e;
|
|
279
287
|
} finally {
|
|
288
|
+
chain.seenBlockInputCache.unmarkValidatingBlock(blockRootHex);
|
|
280
289
|
// The block received from the network may have established an equivocation, either by conflicting
|
|
281
290
|
// with a previously observed block root (REPEAT_PROPOSAL) or with a root observed during validation
|
|
282
291
|
const proposerIndex = signedBlock.message.proposerIndex;
|
|
@@ -752,18 +761,23 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
752
761
|
e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
|
|
753
762
|
// Only a signature-verified sibling recorded in the seen cache is imported. The cache holds at most two
|
|
754
763
|
// roots per proposer and slot, which bounds full imports over gossip to one alternate
|
|
755
|
-
chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root)
|
|
764
|
+
chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root) &&
|
|
765
|
+
// A sibling with an unknown parent cannot be imported, sync fetches it if its branch becomes relevant
|
|
766
|
+
chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null
|
|
756
767
|
) {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
768
|
+
const blockInput = cacheSignatureVerifiedBlock(
|
|
769
|
+
signedBlock,
|
|
770
|
+
e.type.root,
|
|
771
|
+
topic.boundary.fork,
|
|
772
|
+
peerIdStr,
|
|
773
|
+
seenTimestampSec
|
|
774
|
+
);
|
|
775
|
+
// We're returning IGNORE (thrown below), so this block is not forwarded to peers. Unlike the
|
|
776
|
+
// happy path there is no rush to forward, so we don't need to wrap in callInNextEventLoop.
|
|
777
|
+
chain.serializedCache.set(signedBlock, serializedData);
|
|
778
|
+
// this is technically not a valid gossip block but gossip validation is a cheap subset of checks
|
|
779
|
+
// this runs the full state transition, so importing an equivocating-but-valid block here is safe.
|
|
780
|
+
handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
|
|
767
781
|
}
|
|
768
782
|
// rethrow so gossipValidatorFn maps IGNORE -> TopicValidatorResult.Ignore (message not forwarded)
|
|
769
783
|
throw e;
|
|
@@ -1381,7 +1395,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1381
1395
|
const delaySec = chain.clock.secFromSlot(payloadAttestationMessage.data.slot, seenTimestampSec);
|
|
1382
1396
|
metrics?.gossipPayloadAttestationMessage.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
1383
1397
|
const nextSlotProposer = chain.getHeadState().getBeaconProposer(payloadAttestationMessage.data.slot + 1);
|
|
1384
|
-
const isNextSlotProposer = chain.beaconProposerCache.
|
|
1398
|
+
const isNextSlotProposer = chain.beaconProposerCache.has(nextSlotProposer);
|
|
1385
1399
|
|
|
1386
1400
|
if (isNextSlotProposer) {
|
|
1387
1401
|
try {
|
package/src/node/nodejs.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {BeaconConfig} from "@lodestar/config";
|
|
|
14
14
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
15
15
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
16
16
|
import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
|
|
17
|
+
import {Slot} from "@lodestar/types";
|
|
17
18
|
import {sleep, toRootHex} from "@lodestar/utils";
|
|
18
19
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
19
20
|
import {BeaconRestApiServer, getApi} from "../api/index.js";
|
|
@@ -61,6 +62,7 @@ export type BeaconNodeInitModules = {
|
|
|
61
62
|
peerStoreDir?: string;
|
|
62
63
|
anchorState: IBeaconStateView;
|
|
63
64
|
isAnchorStateFinalized: boolean;
|
|
65
|
+
earliestAvailableSlot: Slot;
|
|
64
66
|
metricsRegistries?: Registry[];
|
|
65
67
|
};
|
|
66
68
|
|
|
@@ -159,6 +161,7 @@ export class BeaconNode {
|
|
|
159
161
|
peerStoreDir,
|
|
160
162
|
anchorState,
|
|
161
163
|
isAnchorStateFinalized,
|
|
164
|
+
earliestAvailableSlot,
|
|
162
165
|
metricsRegistries = [],
|
|
163
166
|
}: BeaconNodeInitModules): Promise<T> {
|
|
164
167
|
if (hasher.name !== "hashtree") {
|
|
@@ -266,6 +269,7 @@ export class BeaconNode {
|
|
|
266
269
|
validatorMonitor,
|
|
267
270
|
anchorState,
|
|
268
271
|
isAnchorStateFinalized,
|
|
272
|
+
earliestAvailableSlot,
|
|
269
273
|
executionEngine: initializeExecutionEngine(executionEngineOpts, {
|
|
270
274
|
metrics,
|
|
271
275
|
signal,
|
package/src/node/options.ts
CHANGED
|
@@ -3,12 +3,14 @@ import {ArchiveMode, DEFAULT_ARCHIVE_MODE, IChainOptions, defaultChainOptions} f
|
|
|
3
3
|
import {ValidatorMonitorOpts, defaultValidatorMonitorOpts} from "../chain/validatorMonitor.js";
|
|
4
4
|
import {DatabaseOptions, defaultDbOptions} from "../db/options.js";
|
|
5
5
|
import {
|
|
6
|
+
EngineApiMode,
|
|
6
7
|
ExecutionBuilderOpts,
|
|
7
8
|
ExecutionEngineOpts,
|
|
8
9
|
defaultExecutionBuilderHttpOpts,
|
|
9
10
|
defaultExecutionBuilderOpts,
|
|
10
11
|
defaultExecutionEngineHttpOpts,
|
|
11
12
|
defaultExecutionEngineOpts,
|
|
13
|
+
engineApiModes,
|
|
12
14
|
} from "../execution/index.js";
|
|
13
15
|
import {MetricsOptions, defaultMetricsOptions} from "../metrics/options.js";
|
|
14
16
|
import {MonitoringOptions, defaultMonitoringOptions} from "../monitoring/options.js";
|
|
@@ -19,7 +21,14 @@ import {SyncOptions, defaultSyncOptions} from "../sync/options.js";
|
|
|
19
21
|
export {allNamespaces} from "../api/rest/index.js";
|
|
20
22
|
|
|
21
23
|
// Re-export to use as default values in CLI args
|
|
22
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
defaultExecutionEngineHttpOpts,
|
|
26
|
+
defaultExecutionBuilderHttpOpts,
|
|
27
|
+
engineApiModes,
|
|
28
|
+
ArchiveMode,
|
|
29
|
+
DEFAULT_ARCHIVE_MODE,
|
|
30
|
+
};
|
|
31
|
+
export type {EngineApiMode};
|
|
23
32
|
|
|
24
33
|
export interface IBeaconNodeOptions {
|
|
25
34
|
api: ApiOptions;
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {IClock} from "../../util/clock.js";
|
|
|
14
14
|
import {CustodyConfig} from "../../util/dataColumns.js";
|
|
15
15
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
16
16
|
import {MAX_BATCH_DOWNLOAD_ATTEMPTS, MAX_BATCH_PROCESSING_ATTEMPTS} from "../constants.js";
|
|
17
|
-
import {DownloadByRangeRequests,
|
|
17
|
+
import {DownloadByRangeRequests, ParentPayload} from "../utils/downloadByRange.js";
|
|
18
18
|
import {getBatchSlotRange, hashBlocks} from "./utils/index.js";
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -175,7 +175,7 @@ export class Batch {
|
|
|
175
175
|
private readonly clock: IClock;
|
|
176
176
|
private readonly custodyConfig: CustodyConfig;
|
|
177
177
|
private readonly isFirstBatchInChain: boolean;
|
|
178
|
-
private readonly
|
|
178
|
+
private readonly parentPayload: ParentPayload | undefined;
|
|
179
179
|
|
|
180
180
|
constructor(
|
|
181
181
|
startEpoch: Epoch,
|
|
@@ -183,7 +183,7 @@ export class Batch {
|
|
|
183
183
|
clock: IClock,
|
|
184
184
|
custodyConfig: CustodyConfig,
|
|
185
185
|
isFirstBatchInChain: boolean,
|
|
186
|
-
|
|
186
|
+
parentPayload: ParentPayload | undefined,
|
|
187
187
|
targetSlot: Slot
|
|
188
188
|
) {
|
|
189
189
|
this.config = config;
|
|
@@ -196,7 +196,7 @@ export class Batch {
|
|
|
196
196
|
this.startSlot = startSlot;
|
|
197
197
|
this.count = Math.min(count, targetSlot - startSlot + 1);
|
|
198
198
|
this.isFirstBatchInChain = isFirstBatchInChain;
|
|
199
|
-
this.
|
|
199
|
+
this.parentPayload = parentPayload;
|
|
200
200
|
this.requests = this.getRequests([]);
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -208,24 +208,19 @@ export class Batch {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// we only know if we should download parent envelope if firstBlock is downloaded
|
|
211
|
-
if (firstBlock === undefined) return false;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
.
|
|
215
|
-
|
|
211
|
+
if (firstBlock === undefined || this.parentPayload === undefined) return false;
|
|
212
|
+
const firstBlockBid = (firstBlock.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message;
|
|
213
|
+
return (
|
|
214
|
+
byteArrayEquals(firstBlock.message.parentRoot, this.parentPayload.blockRoot) &&
|
|
215
|
+
byteArrayEquals(firstBlockBid.parentBlockHash, this.parentPayload.bid.blockHash)
|
|
216
|
+
);
|
|
216
217
|
}
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
if (this.
|
|
220
|
-
throw new Error(
|
|
221
|
-
`Coding error: getParentPayloadCommitments called without latestBid for parentBlockRoot=${toRootHex(parentBlockRoot)}`
|
|
222
|
-
);
|
|
219
|
+
getParentPayload(): ParentPayload {
|
|
220
|
+
if (this.parentPayload === undefined) {
|
|
221
|
+
throw new Error("Coding error: getParentPayload called without parentPayload");
|
|
223
222
|
}
|
|
224
|
-
return
|
|
225
|
-
blockRoot: parentBlockRoot,
|
|
226
|
-
blockRootHex: toRootHex(parentBlockRoot),
|
|
227
|
-
kzgCommitments: this.latestBid.blobKzgCommitments,
|
|
228
|
-
};
|
|
223
|
+
return this.parentPayload;
|
|
229
224
|
}
|
|
230
225
|
|
|
231
226
|
/**
|
package/src/sync/range/chain.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {Epoch, Root, Slot
|
|
2
|
+
import {Epoch, Root, Slot} from "@lodestar/types";
|
|
3
3
|
import {ErrorAborted, LodestarError, Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
5
5
|
import {BlockInputErrorCode} from "../../chain/blocks/blockInput/errors.js";
|
|
@@ -16,7 +16,7 @@ import {ItTrigger} from "../../util/itTrigger.js";
|
|
|
16
16
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
17
17
|
import {WarnResult, wrapError} from "../../util/wrapError.js";
|
|
18
18
|
import {BATCH_BUFFER_SIZE, EPOCHS_PER_BATCH, MAX_LOOK_AHEAD_EPOCHS} from "../constants.js";
|
|
19
|
-
import {DownloadByRangeError, DownloadByRangeErrorCode} from "../utils/downloadByRange.js";
|
|
19
|
+
import {DownloadByRangeError, DownloadByRangeErrorCode, ParentPayload} from "../utils/downloadByRange.js";
|
|
20
20
|
import {getRateLimitedUntilMs} from "../utils/rateLimit.js";
|
|
21
21
|
import {RangeSyncType} from "../utils/remoteSyncType.js";
|
|
22
22
|
import {Batch, BatchError, BatchErrorCode, BatchMetadata, BatchStatus} from "./batch.js";
|
|
@@ -160,7 +160,7 @@ export class SyncChain {
|
|
|
160
160
|
private readonly clock: IClock;
|
|
161
161
|
private readonly metrics: Metrics | null;
|
|
162
162
|
private readonly custodyConfig: CustodyConfig;
|
|
163
|
-
private readonly
|
|
163
|
+
private readonly parentPayload: ParentPayload | undefined;
|
|
164
164
|
|
|
165
165
|
constructor(
|
|
166
166
|
initialBatchEpoch: Epoch,
|
|
@@ -168,7 +168,7 @@ export class SyncChain {
|
|
|
168
168
|
syncType: RangeSyncType,
|
|
169
169
|
fns: SyncChainFns,
|
|
170
170
|
modules: SyncChainModules,
|
|
171
|
-
|
|
171
|
+
parentPayload: ParentPayload | undefined
|
|
172
172
|
) {
|
|
173
173
|
const {config, clock, custodyConfig, logger, metrics} = modules;
|
|
174
174
|
this.firstBatchEpoch = initialBatchEpoch;
|
|
@@ -184,7 +184,7 @@ export class SyncChain {
|
|
|
184
184
|
this.clock = clock;
|
|
185
185
|
this.metrics = metrics;
|
|
186
186
|
this.custodyConfig = custodyConfig;
|
|
187
|
-
this.
|
|
187
|
+
this.parentPayload = parentPayload;
|
|
188
188
|
this.logger = logger;
|
|
189
189
|
this.logId = `${syncType}-${nextChainId++}`;
|
|
190
190
|
|
|
@@ -536,8 +536,8 @@ export class SyncChain {
|
|
|
536
536
|
this.clock,
|
|
537
537
|
this.custodyConfig,
|
|
538
538
|
this.isFirstBatch,
|
|
539
|
-
// `
|
|
540
|
-
this.isFirstBatch ? this.
|
|
539
|
+
// `parentPayload` is only meaningful for the first batch
|
|
540
|
+
this.isFirstBatch ? this.parentPayload : undefined,
|
|
541
541
|
this.target.slot
|
|
542
542
|
);
|
|
543
543
|
this.isFirstBatch = false;
|
|
@@ -586,6 +586,8 @@ export class SyncChain {
|
|
|
586
586
|
case DownloadByRangeErrorCode.OUT_OF_RANGE_BLOCKS:
|
|
587
587
|
case DownloadByRangeErrorCode.PARENT_ROOT_MISMATCH:
|
|
588
588
|
case DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT:
|
|
589
|
+
case DownloadByRangeErrorCode.INVALID_ENVELOPE_BLOCK_HASH:
|
|
590
|
+
case DownloadByRangeErrorCode.INVALID_ENVELOPE_SLOT:
|
|
589
591
|
case DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT:
|
|
590
592
|
case BlobSidecarErrorCode.INCLUSION_PROOF_INVALID:
|
|
591
593
|
case BlobSidecarErrorCode.INVALID_KZG_PROOF_BATCH:
|
package/src/sync/range/range.ts
CHANGED
|
@@ -2,8 +2,8 @@ import {EventEmitter} from "node:events";
|
|
|
2
2
|
import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
3
3
|
import {BeaconConfig} from "@lodestar/config";
|
|
4
4
|
import {IBeaconStateViewGloas, computeStartSlotAtEpoch, isStatePostGloas} from "@lodestar/state-transition";
|
|
5
|
-
import {Epoch, Status, fulu} from "@lodestar/types";
|
|
6
|
-
import {Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
5
|
+
import {Epoch, Status, fulu, ssz} from "@lodestar/types";
|
|
6
|
+
import {Logger, byteArrayEquals, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
8
8
|
import {PayloadError, PayloadErrorCode} from "../../chain/blocks/importExecutionPayload.js";
|
|
9
9
|
import {AttestationImportOpt, ImportBlockOpts} from "../../chain/blocks/index.js";
|
|
@@ -16,6 +16,7 @@ import {PeerIdStr} from "../../util/peerId.js";
|
|
|
16
16
|
import {
|
|
17
17
|
DownloadByRangeError,
|
|
18
18
|
DownloadByRangeErrorCode,
|
|
19
|
+
ParentPayload,
|
|
19
20
|
cacheByRangeResponses,
|
|
20
21
|
downloadByRange,
|
|
21
22
|
} from "../utils/downloadByRange.js";
|
|
@@ -278,15 +279,14 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
278
279
|
private downloadByRange: SyncChainFns["downloadByRange"] = async (peer, batch) => {
|
|
279
280
|
const batchBlocks = batch.getBlocks();
|
|
280
281
|
const requests = batch.getRequestsForPeer(peer);
|
|
281
|
-
const
|
|
282
|
-
const parentPayloadCommitments = parentRoot ? batch.getParentPayloadCommitments(parentRoot) : undefined;
|
|
282
|
+
const parentPayload = requests.parentPayloadRequest ? batch.getParentPayload() : undefined;
|
|
283
283
|
const {result, warnings} = await downloadByRange({
|
|
284
284
|
config: this.config,
|
|
285
285
|
network: this.network,
|
|
286
286
|
logger: this.logger,
|
|
287
287
|
peerIdStr: peer.peerId,
|
|
288
288
|
batchBlocks,
|
|
289
|
-
|
|
289
|
+
parentPayload,
|
|
290
290
|
peerDasMetrics: this.chain.metrics?.peerDas,
|
|
291
291
|
...requests,
|
|
292
292
|
});
|
|
@@ -299,6 +299,7 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
299
299
|
batchBlocks,
|
|
300
300
|
downloadedPayloadEnvelopes,
|
|
301
301
|
existingPayloadEnvelopes: batch.getPayloadEnvelopes(),
|
|
302
|
+
parentPayload,
|
|
302
303
|
custodyConfig: this.chain.custodyConfig,
|
|
303
304
|
seenTimestampSec: Date.now() / 1000,
|
|
304
305
|
});
|
|
@@ -376,13 +377,26 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
376
377
|
private addPeerOrCreateChain(startEpoch: Epoch, target: ChainTarget, peer: PeerIdStr, syncType: RangeSyncType): void {
|
|
377
378
|
let syncChain = this.chains.get(syncType);
|
|
378
379
|
if (!syncChain) {
|
|
379
|
-
// The first
|
|
380
|
-
//
|
|
381
|
-
// downloaded block's `bid.parentBlockHash` against the head state's `latestExecutionPayloadBid.blockHash`.
|
|
380
|
+
// The first block of the first batch may build on the head's FULL variant before its payload is imported,
|
|
381
|
+
// the batch then downloads the head's payload as dangling parent and validates it against the bid
|
|
382
382
|
const headState = this.chain.getHeadState();
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
383
|
+
let parentPayload: ParentPayload | undefined;
|
|
384
|
+
if (isStatePostGloas(headState) && headState.latestBlockHeader.slot > 0) {
|
|
385
|
+
const header = ssz.phase0.BeaconBlockHeader.clone(headState.latestBlockHeader);
|
|
386
|
+
if (byteArrayEquals(header.stateRoot, ssz.Root.defaultValue())) {
|
|
387
|
+
header.stateRoot = headState.hashTreeRoot();
|
|
388
|
+
}
|
|
389
|
+
const headRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(header);
|
|
390
|
+
const headSlot = headState.latestBlockHeader.slot;
|
|
391
|
+
parentPayload = {
|
|
392
|
+
blockRoot: headRoot,
|
|
393
|
+
blockRootHex: toRootHex(headRoot),
|
|
394
|
+
slot: headSlot,
|
|
395
|
+
proposerIndex: headState.latestBlockHeader.proposerIndex,
|
|
396
|
+
forkName: this.config.getForkName(headSlot),
|
|
397
|
+
bid: (headState as IBeaconStateViewGloas).latestExecutionPayloadBid,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
386
400
|
|
|
387
401
|
syncChain = new SyncChain(
|
|
388
402
|
startEpoch,
|
|
@@ -403,7 +417,7 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
403
417
|
custodyConfig: this.chain.custodyConfig,
|
|
404
418
|
metrics: this.metrics,
|
|
405
419
|
},
|
|
406
|
-
|
|
420
|
+
parentPayload
|
|
407
421
|
);
|
|
408
422
|
this.chains.set(syncType, syncChain);
|
|
409
423
|
|