@lodestar/beacon-node 1.50.0-dev.ebecebb9ba → 1.50.0-dev.f0a5c36612
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 +6 -1
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js +13 -0
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +4 -4
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +23 -3
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +26 -18
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +1 -0
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +23 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +6 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +18 -24
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +1 -1
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +5 -3
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +11 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +1 -1
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/chain.d.ts +9 -5
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +56 -21
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +7 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
- package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.js +2 -0
- package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +3 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +2 -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/initState.d.ts +2 -9
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +5 -38
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/interface.d.ts +3 -2
- 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 +5 -4
- package/lib/chain/opPools/opPool.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.js +62 -18
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +4 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +11 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/options.d.ts +2 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +5 -2
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +2 -0
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +10 -4
- package/lib/chain/regen/regen.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/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +1 -0
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +4 -0
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts +4 -4
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +3 -0
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.d.ts +3 -3
- package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +3 -0
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +5 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +64 -54
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/beacon.d.ts +2 -0
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +5 -0
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +2 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +1 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +2 -0
- 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/proposerSlashing.d.ts +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
- package/lib/db/repositories/proposerSlashing.js +5 -1
- package/lib/db/repositories/proposerSlashing.js.map +1 -1
- package/lib/execution/engine/disabled.d.ts +0 -2
- package/lib/execution/engine/disabled.d.ts.map +1 -1
- package/lib/execution/engine/disabled.js +0 -6
- package/lib/execution/engine/disabled.js.map +1 -1
- package/lib/execution/engine/http.d.ts +1 -3
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +2 -17
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -4
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts +0 -2
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +0 -8
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +0 -8
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/metrics.d.ts +7 -3
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +4 -4
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/network/metadata.d.ts +1 -1
- package/lib/network/metadata.d.ts.map +1 -1
- package/lib/network/metadata.js +17 -17
- package/lib/network/metadata.js.map +1 -1
- package/lib/network/peers/datastore.d.ts +9 -9
- package/lib/network/peers/datastore.d.ts.map +1 -1
- package/lib/network/peers/datastore.js +36 -36
- package/lib/network/peers/datastore.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +66 -67
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +7 -3
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +9 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +77 -10
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.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 +27 -4
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts +11 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +21 -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 +12 -6
- 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 +75 -29
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +14 -7
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +132 -66
- 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/execution.d.ts +11 -11
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +6 -2
- package/lib/util/execution.js.map +1 -1
- package/package.json +17 -17
- package/src/api/impl/beacon/blocks/index.ts +7 -4
- package/src/api/impl/beacon/index.ts +15 -0
- package/src/api/impl/beacon/pool/index.ts +4 -4
- package/src/api/impl/beacon/state/index.ts +31 -3
- package/src/api/impl/debug/index.ts +28 -19
- package/src/api/impl/validator/index.ts +24 -2
- package/src/chain/archiveStore/archiveStore.ts +26 -26
- package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -2
- package/src/chain/archiveStore/historicalState/worker.ts +1 -2
- package/src/chain/archiveStore/interface.ts +1 -1
- package/src/chain/archiveStore/utils/pruneHistory.ts +9 -4
- package/src/chain/blocks/importBlock.ts +13 -3
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/blocks/verifyBlock.ts +1 -0
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
- package/src/chain/chain.ts +76 -23
- package/src/chain/emitter.ts +7 -1
- package/src/chain/errors/envelopeReconstructionError.ts +3 -0
- package/src/chain/errors/gossipValidation.ts +2 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/initState.ts +8 -60
- package/src/chain/interface.ts +3 -2
- package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
- package/src/chain/opPools/opPool.ts +91 -18
- package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
- package/src/chain/opPools/utils.ts +31 -3
- package/src/chain/options.ts +3 -3
- package/src/chain/prepareNextSlot.ts +6 -3
- package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +8 -1
- package/src/chain/regen/regen.ts +12 -4
- package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
- package/src/chain/shufflingCache.ts +5 -0
- package/src/chain/validation/attesterSlashing.ts +7 -4
- package/src/chain/validation/proposerSlashing.ts +7 -4
- package/src/chain/validatorMonitor.ts +75 -57
- package/src/db/beacon.ts +5 -0
- package/src/db/buckets.ts +1 -0
- package/src/db/interface.ts +2 -0
- package/src/db/repositories/earliestAvailableSlot.ts +19 -0
- package/src/db/repositories/proposerSlashing.ts +6 -1
- package/src/execution/engine/disabled.ts +0 -8
- package/src/execution/engine/http.ts +1 -30
- package/src/execution/engine/interface.ts +2 -6
- package/src/execution/engine/mock.ts +1 -19
- package/src/execution/engine/types.ts +0 -10
- package/src/index.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/metrics/metrics.ts +21 -6
- package/src/network/metadata.ts +17 -17
- package/src/network/peers/datastore.ts +36 -36
- package/src/network/processor/gossipHandlers.ts +97 -80
- package/src/network/processor/gossipValidatorFn.ts +13 -3
- package/src/network/processor/index.ts +89 -10
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
- package/src/node/nodejs.ts +35 -3
- package/src/sync/range/batch.ts +25 -19
- package/src/sync/range/chain.ts +15 -8
- package/src/sync/range/range.ts +76 -30
- package/src/sync/unknownBlock.ts +156 -71
- package/src/sync/utils/downloadByRange.ts +123 -58
- package/src/util/execution.ts +17 -9
package/src/metrics/metrics.ts
CHANGED
|
@@ -9,27 +9,42 @@ import {RegistryMetricCreator} from "./utils/registryMetricCreator.js";
|
|
|
9
9
|
|
|
10
10
|
export type Metrics = BeaconMetrics &
|
|
11
11
|
ForkChoiceMetrics &
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
LodestarMetrics & {
|
|
13
|
+
/** Null when we use native state transition metrics instead */
|
|
14
|
+
stateTransition: BeaconStateTransitionMetrics | null;
|
|
15
|
+
register: RegistryMetricCreator;
|
|
16
|
+
close: () => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type CreateMetricsOptions = {
|
|
20
|
+
/*
|
|
21
|
+
* Toggle inclusion of state transition metrics.
|
|
22
|
+
*
|
|
23
|
+
* False when native state transition is used, so we
|
|
24
|
+
* can grab metrics from the native implementation instead.
|
|
25
|
+
* */
|
|
26
|
+
includeStateTransitionMetrics?: boolean;
|
|
27
|
+
collectNodeMetrics?: boolean;
|
|
28
|
+
};
|
|
14
29
|
|
|
15
30
|
export function createMetrics(
|
|
16
31
|
opts: MetricsOptions,
|
|
17
32
|
genesisTime: number,
|
|
18
33
|
externalRegistries: Registry[] = [],
|
|
19
|
-
|
|
34
|
+
createOpts: CreateMetricsOptions = {}
|
|
20
35
|
): Metrics {
|
|
21
36
|
const register = new RegistryMetricCreator();
|
|
22
37
|
const beacon = createBeaconMetrics(register);
|
|
23
38
|
const forkChoice = getForkChoiceMetrics(register);
|
|
24
39
|
const lodestar = createLodestarMetrics(register, opts.metadata, genesisTime);
|
|
25
|
-
const stateTransition = getMetrics(register);
|
|
40
|
+
const stateTransition = createOpts.includeStateTransitionMetrics === false ? null : getMetrics(register);
|
|
26
41
|
|
|
27
42
|
const onUnhandledRejection = (_error: unknown): void => {
|
|
28
43
|
lodestar.unhandledPromiseRejections.inc();
|
|
29
44
|
};
|
|
30
45
|
process.on("unhandledRejection", onUnhandledRejection);
|
|
31
46
|
|
|
32
|
-
const nodeJsMetricsClose = collectNodeMetrics
|
|
47
|
+
const nodeJsMetricsClose = createOpts.collectNodeMetrics === false ? (): void => {} : collectNodeJSMetrics(register);
|
|
33
48
|
const close = (): void => {
|
|
34
49
|
process.removeListener("unhandledRejection", onUnhandledRejection);
|
|
35
50
|
nodeJsMetricsClose();
|
|
@@ -46,7 +61,7 @@ export function createMetrics(
|
|
|
46
61
|
...beacon,
|
|
47
62
|
...forkChoice,
|
|
48
63
|
...lodestar,
|
|
49
|
-
|
|
64
|
+
stateTransition,
|
|
50
65
|
register,
|
|
51
66
|
close,
|
|
52
67
|
};
|
package/src/network/metadata.ts
CHANGED
|
@@ -42,13 +42,13 @@ export class MetadataController {
|
|
|
42
42
|
private onSetValue: (key: string, value: Uint8Array) => void;
|
|
43
43
|
private networkConfig: NetworkConfig;
|
|
44
44
|
private logger: Logger;
|
|
45
|
-
private
|
|
45
|
+
private metadata: fulu.Metadata;
|
|
46
46
|
|
|
47
47
|
constructor(opts: MetadataOpts, modules: MetadataModules) {
|
|
48
48
|
this.networkConfig = modules.networkConfig;
|
|
49
49
|
this.logger = modules.logger;
|
|
50
50
|
this.onSetValue = modules.onSetValue;
|
|
51
|
-
this.
|
|
51
|
+
this.metadata = opts.metadata ?? {
|
|
52
52
|
...ssz.fulu.Metadata.defaultValue(),
|
|
53
53
|
custodyGroupCount: modules.networkConfig.custodyConfig.targetCustodyGroupCount,
|
|
54
54
|
};
|
|
@@ -58,61 +58,61 @@ export class MetadataController {
|
|
|
58
58
|
// updateEth2Field() MUST be called with clock epoch
|
|
59
59
|
this.updateEth2Field(currentEpoch);
|
|
60
60
|
|
|
61
|
-
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.
|
|
61
|
+
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.metadata.attnets));
|
|
62
62
|
|
|
63
63
|
const config = this.networkConfig.config;
|
|
64
64
|
|
|
65
65
|
if (config.getForkSeq(computeStartSlotAtEpoch(currentEpoch)) >= ForkSeq.altair) {
|
|
66
66
|
// Only persist syncnets if altair fork is already activated. If currentFork is altair but head is phase0
|
|
67
67
|
// adding syncnets to the ENR is not a problem, we will just have a useless field for a few hours.
|
|
68
|
-
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.
|
|
68
|
+
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.metadata.syncnets));
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Set CGC regardless of fork. It may be useful to clients before Fulu, and will be ignored otherwise.
|
|
72
|
-
this.onSetValue(ENRKey.cgc, serializeCgc(this.
|
|
72
|
+
this.onSetValue(ENRKey.cgc, serializeCgc(this.metadata.custodyGroupCount));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
get seqNumber(): bigint {
|
|
76
|
-
return this.
|
|
76
|
+
return this.metadata.seqNumber;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
get syncnets(): BitArray {
|
|
80
|
-
return this.
|
|
80
|
+
return this.metadata.syncnets;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
set syncnets(syncnets: BitArray) {
|
|
84
84
|
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(syncnets));
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
85
|
+
this.metadata.seqNumber++;
|
|
86
|
+
this.metadata.syncnets = syncnets;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
get attnets(): BitArray {
|
|
90
|
-
return this.
|
|
90
|
+
return this.metadata.attnets;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
set attnets(attnets: BitArray) {
|
|
94
94
|
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(attnets));
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
95
|
+
this.metadata.seqNumber++;
|
|
96
|
+
this.metadata.attnets = attnets;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
get custodyGroupCount(): number {
|
|
100
|
-
return this.
|
|
100
|
+
return this.metadata.custodyGroupCount;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
set custodyGroupCount(custodyGroupCount: number) {
|
|
104
|
-
if (custodyGroupCount === this.
|
|
104
|
+
if (custodyGroupCount === this.metadata.custodyGroupCount) {
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
this.onSetValue(ENRKey.cgc, serializeCgc(custodyGroupCount));
|
|
108
108
|
this.logger.debug("Updated cgc field in ENR", {custodyGroupCount});
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
109
|
+
this.metadata.seqNumber++;
|
|
110
|
+
this.metadata.custodyGroupCount = custodyGroupCount;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** Consumers that need the phase0.Metadata type can just ignore the .syncnets property */
|
|
114
114
|
get json(): fulu.Metadata {
|
|
115
|
-
return this.
|
|
115
|
+
return this.metadata;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
@@ -16,23 +16,23 @@ type AwaitGenerator<T, TReturn = any, TNext = any> = Generator<T, TReturn, TNext
|
|
|
16
16
|
* This has a memory issue because all peer data stays in memory and loaded at startup time
|
|
17
17
|
* This is written for libp2p >=0.35, we maintain the same mechanism but with bounded data structure
|
|
18
18
|
* This datastore includes a memory datastore and fallback to db datastore
|
|
19
|
-
* Use an in-memory datastore with last accessed time and
|
|
19
|
+
* Use an in-memory datastore with last accessed time and maxMemoryItems, on start it's empty (lazy load)
|
|
20
20
|
* - get: Search in-memory datastore first, if not found search from db.
|
|
21
21
|
* - If found from db, add back to the in-memory datastore
|
|
22
22
|
* - Update lastAccessedMs
|
|
23
|
-
* - put: move oldest items from memory to db if there are more than
|
|
24
|
-
* - update memory datastore, only update db datastore if there are at least
|
|
23
|
+
* - put: move oldest items from memory to db if there are more than maxMemoryItems items in memory
|
|
24
|
+
* - update memory datastore, only update db datastore if there are at least threshold dirty items
|
|
25
25
|
* - Update lastAccessedMs
|
|
26
26
|
*/
|
|
27
27
|
export class Eth2PeerDataStore extends BaseDatastore {
|
|
28
|
-
private
|
|
29
|
-
private
|
|
28
|
+
private dbDatastore: LevelDatastore;
|
|
29
|
+
private memoryDatastore: Map<string, MemoryItem>;
|
|
30
30
|
/** Same to PersistentPeerStore of the old libp2p implementation */
|
|
31
|
-
private
|
|
31
|
+
private dirtyItems = new Set<string>();
|
|
32
32
|
/** If there are more dirty items than threshold, commit data to db */
|
|
33
|
-
private
|
|
33
|
+
private threshold: number;
|
|
34
34
|
/** If there are more memory items than this, prune oldest ones from memory and move to db */
|
|
35
|
-
private
|
|
35
|
+
private maxMemoryItems: number;
|
|
36
36
|
|
|
37
37
|
constructor(
|
|
38
38
|
dbDatastore: LevelDatastore | string,
|
|
@@ -47,18 +47,18 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
47
47
|
throw Error(`Threshold ${threshold} should be at most maxMemoryItems ${maxMemoryItems}`);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
50
|
+
this.dbDatastore = typeof dbDatastore === "string" ? new LevelDatastore(dbDatastore) : dbDatastore;
|
|
51
|
+
this.memoryDatastore = new Map();
|
|
52
|
+
this.threshold = threshold;
|
|
53
|
+
this.maxMemoryItems = maxMemoryItems;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async open(): Promise<void> {
|
|
57
|
-
return this.
|
|
57
|
+
return this.dbDatastore.open();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
async close(): Promise<void> {
|
|
61
|
-
return this.
|
|
61
|
+
return this.dbDatastore.close();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
async put(key: Key, val: Uint8Array, _options?: AbortOptions): Promise<Key> {
|
|
@@ -67,23 +67,23 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Same interface to put with "fromDb" option, if this item is updated back from db
|
|
70
|
-
* Move oldest items from memory data store to db if it's over this.
|
|
70
|
+
* Move oldest items from memory data store to db if it's over this.maxMemoryItems
|
|
71
71
|
*/
|
|
72
72
|
async _put(key: Key, val: Uint8Array, fromDb = false): Promise<Key> {
|
|
73
|
-
while (this.
|
|
73
|
+
while (this.memoryDatastore.size >= this.maxMemoryItems) {
|
|
74
74
|
// it's likely this is called only 1 time
|
|
75
75
|
await this.pruneMemoryDatastore();
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const keyStr = key.toString();
|
|
79
|
-
const memoryItem = this.
|
|
79
|
+
const memoryItem = this.memoryDatastore.get(keyStr);
|
|
80
80
|
if (memoryItem) {
|
|
81
81
|
// update existing
|
|
82
82
|
memoryItem.lastAccessedMs = Date.now();
|
|
83
83
|
memoryItem.data = val;
|
|
84
84
|
} else {
|
|
85
85
|
// new
|
|
86
|
-
this.
|
|
86
|
+
this.memoryDatastore.set(keyStr, {data: val, lastAccessedMs: Date.now()});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if (!fromDb) await this._addDirtyItem(keyStr);
|
|
@@ -98,15 +98,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
98
98
|
*/
|
|
99
99
|
async get(key: Key, options?: AbortOptions): Promise<Uint8Array> {
|
|
100
100
|
const keyStr = key.toString();
|
|
101
|
-
const memoryItem = this.
|
|
101
|
+
const memoryItem = this.memoryDatastore.get(keyStr);
|
|
102
102
|
if (memoryItem) {
|
|
103
103
|
memoryItem.lastAccessedMs = Date.now();
|
|
104
104
|
return memoryItem.data;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// this throws error if not found
|
|
108
|
-
const dbValue = await this.
|
|
109
|
-
// don't call this.
|
|
108
|
+
const dbValue = await this.dbDatastore.get(key, options);
|
|
109
|
+
// don't call this.memoryDatastore.set directly
|
|
110
110
|
// we want to get through prune() logic with fromDb as true
|
|
111
111
|
await this._put(key, dbValue, true);
|
|
112
112
|
return dbValue;
|
|
@@ -125,30 +125,30 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
async delete(key: Key, options?: AbortOptions): Promise<void> {
|
|
128
|
-
this.
|
|
129
|
-
await this.
|
|
128
|
+
this.memoryDatastore.delete(key.toString());
|
|
129
|
+
await this.dbDatastore.delete(key, options);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
async *_all(q: Query, options?: AbortOptions): AwaitGenerator<Pair> {
|
|
133
|
-
for (const [key, value] of this.
|
|
133
|
+
for (const [key, value] of this.memoryDatastore.entries()) {
|
|
134
134
|
yield {
|
|
135
135
|
key: new Key(key),
|
|
136
136
|
value: value.data,
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
yield* this.
|
|
139
|
+
yield* this.dbDatastore.query(q, options);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
async *_allKeys(q: KeyQuery, options?: AbortOptions): AwaitGenerator<Key> {
|
|
143
|
-
for (const key of this.
|
|
143
|
+
for (const key of this.memoryDatastore.keys()) {
|
|
144
144
|
yield new Key(key);
|
|
145
145
|
}
|
|
146
|
-
yield* this.
|
|
146
|
+
yield* this.dbDatastore.queryKeys(q, options);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
private async _addDirtyItem(keyStr: string): Promise<void> {
|
|
150
|
-
this.
|
|
151
|
-
if (this.
|
|
150
|
+
this.dirtyItems.add(keyStr);
|
|
151
|
+
if (this.dirtyItems.size >= this.threshold) {
|
|
152
152
|
try {
|
|
153
153
|
await this._commitData();
|
|
154
154
|
} catch (_e) {}
|
|
@@ -156,15 +156,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
private async _commitData(): Promise<void> {
|
|
159
|
-
const batch = this.
|
|
160
|
-
for (const keyStr of this.
|
|
161
|
-
const memoryItem = this.
|
|
159
|
+
const batch = this.dbDatastore.batch();
|
|
160
|
+
for (const keyStr of this.dirtyItems) {
|
|
161
|
+
const memoryItem = this.memoryDatastore.get(keyStr);
|
|
162
162
|
if (memoryItem) {
|
|
163
163
|
batch.put(new Key(keyStr), memoryItem.data);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
await batch.commit();
|
|
167
|
-
this.
|
|
167
|
+
this.dirtyItems.clear();
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
@@ -175,7 +175,7 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
175
175
|
let oldestKey: string | undefined = undefined;
|
|
176
176
|
let oldestValue: Uint8Array | undefined = undefined;
|
|
177
177
|
|
|
178
|
-
for (const [key, value] of this.
|
|
178
|
+
for (const [key, value] of this.memoryDatastore) {
|
|
179
179
|
if (value.lastAccessedMs < oldestAccessedMs) {
|
|
180
180
|
oldestAccessedMs = value.lastAccessedMs;
|
|
181
181
|
oldestKey = key;
|
|
@@ -184,8 +184,8 @@ export class Eth2PeerDataStore extends BaseDatastore {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
if (oldestKey && oldestValue) {
|
|
187
|
-
await this.
|
|
188
|
-
this.
|
|
187
|
+
await this.dbDatastore.put(new Key(oldestKey), oldestValue);
|
|
188
|
+
this.memoryDatastore.delete(oldestKey);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -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;
|
|
@@ -1065,12 +1079,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1065
1079
|
const {serializedData} = gossipData;
|
|
1066
1080
|
const {fork} = topic.boundary;
|
|
1067
1081
|
const attesterSlashing = sszDeserialize(topic, serializedData);
|
|
1068
|
-
await validateGossipAttesterSlashing(chain, attesterSlashing);
|
|
1082
|
+
const verifiedDomains = await validateGossipAttesterSlashing(chain, attesterSlashing);
|
|
1069
1083
|
|
|
1070
1084
|
// Handler - deferred to next event loop so the validation result propagates first
|
|
1071
1085
|
callInNextEventLoop(() => {
|
|
1072
1086
|
try {
|
|
1073
|
-
chain.opPool.insertAttesterSlashing(fork, attesterSlashing);
|
|
1087
|
+
chain.opPool.insertAttesterSlashing(fork, attesterSlashing, verifiedDomains);
|
|
1074
1088
|
chain.forkChoice.onAttesterSlashing(attesterSlashing);
|
|
1075
1089
|
chain.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing);
|
|
1076
1090
|
} catch (e) {
|
|
@@ -1089,12 +1103,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1089
1103
|
}: GossipHandlerParamGeneric<GossipType.proposer_slashing>) => {
|
|
1090
1104
|
const {serializedData} = gossipData;
|
|
1091
1105
|
const proposerSlashing = sszDeserialize(topic, serializedData);
|
|
1092
|
-
await validateGossipProposerSlashing(chain, proposerSlashing);
|
|
1106
|
+
const verifiedDomain = await validateGossipProposerSlashing(chain, proposerSlashing);
|
|
1093
1107
|
|
|
1094
1108
|
// Handler - deferred to next event loop so the validation result propagates first
|
|
1095
1109
|
callInNextEventLoop(() => {
|
|
1096
1110
|
try {
|
|
1097
|
-
chain.opPool.insertProposerSlashing(proposerSlashing);
|
|
1111
|
+
chain.opPool.insertProposerSlashing(proposerSlashing, verifiedDomain);
|
|
1098
1112
|
chain.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing);
|
|
1099
1113
|
} catch (e) {
|
|
1100
1114
|
logger.debug(
|
|
@@ -1341,6 +1355,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1341
1355
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1342
1356
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1343
1357
|
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
|
|
1358
|
+
// The builder may have signed another envelope that is valid, keeping this one would make
|
|
1359
|
+
// by-root and range sync reuse it and never import the payload
|
|
1360
|
+
chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
|
1344
1361
|
// Misbehaving peer, but could highlight an issue in another client
|
|
1345
1362
|
logLevel = LogLevel.warn;
|
|
1346
1363
|
break;
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
VoluntaryExitErrorCode,
|
|
21
21
|
} from "../../chain/errors/index.js";
|
|
22
22
|
import {Metrics} from "../../metrics/index.js";
|
|
23
|
+
import {PeerIdStr} from "../../util/peerId.js";
|
|
23
24
|
import {INetworkCore} from "../core/index.js";
|
|
24
25
|
import {
|
|
25
26
|
BatchGossipHandlerFn,
|
|
@@ -40,6 +41,8 @@ export type ValidatorFnModules = {
|
|
|
40
41
|
core: INetworkCore;
|
|
41
42
|
};
|
|
42
43
|
|
|
44
|
+
export type OnFatalPeerFn = (peer: PeerIdStr) => void;
|
|
45
|
+
|
|
43
46
|
type RejectPeerActionRule = {default: PeerAction; byCode?: Record<string, PeerAction>};
|
|
44
47
|
|
|
45
48
|
/**
|
|
@@ -112,7 +115,7 @@ const gossipRejectPeerAction: Record<GossipType, RejectPeerActionRule> = {
|
|
|
112
115
|
[GossipType.light_client_optimistic_update]: {default: PeerAction.HighToleranceError},
|
|
113
116
|
};
|
|
114
117
|
|
|
115
|
-
function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
118
|
+
export function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
116
119
|
const rule = gossipRejectPeerAction[type];
|
|
117
120
|
return rule.byCode?.[code] ?? rule.default;
|
|
118
121
|
}
|
|
@@ -123,7 +126,8 @@ function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
|
123
126
|
*/
|
|
124
127
|
export function getGossipValidatorBatchFn(
|
|
125
128
|
gossipHandlers: GossipHandlers,
|
|
126
|
-
modules: ValidatorFnModules
|
|
129
|
+
modules: ValidatorFnModules,
|
|
130
|
+
onFatalPeer: OnFatalPeerFn
|
|
127
131
|
): GossipValidatorBatchFn {
|
|
128
132
|
const {logger, metrics, core} = modules;
|
|
129
133
|
|
|
@@ -173,6 +177,7 @@ export function getGossipValidatorBatchFn(
|
|
|
173
177
|
metrics?.networkProcessor.gossipAttestationRejectByReason.inc({reason: e.type.code});
|
|
174
178
|
const peerAction = rejectPeerAction(type, e.type.code);
|
|
175
179
|
core.reportPeer(propagationSource, peerAction, e.type.code);
|
|
180
|
+
if (peerAction === PeerAction.Fatal) onFatalPeer(propagationSource);
|
|
176
181
|
logger.debug(
|
|
177
182
|
`Gossip validation ${type} rejected`,
|
|
178
183
|
{peer: propagationSource, clientAgent, clientVersion, peerAction},
|
|
@@ -208,7 +213,11 @@ export function getGossipValidatorBatchFn(
|
|
|
208
213
|
*
|
|
209
214
|
* @see getGossipHandlers for reasoning on why GossipHandlerFn are used for gossip validation.
|
|
210
215
|
*/
|
|
211
|
-
export function getGossipValidatorFn(
|
|
216
|
+
export function getGossipValidatorFn(
|
|
217
|
+
gossipHandlers: GossipHandlers,
|
|
218
|
+
modules: ValidatorFnModules,
|
|
219
|
+
onFatalPeer: OnFatalPeerFn
|
|
220
|
+
): GossipValidatorFn {
|
|
212
221
|
const {logger, metrics, core} = modules;
|
|
213
222
|
|
|
214
223
|
return async function gossipValidatorFn({
|
|
@@ -260,6 +269,7 @@ export function getGossipValidatorFn(gossipHandlers: GossipHandlers, modules: Va
|
|
|
260
269
|
metrics?.networkProcessor.gossipValidationReject.inc({topic: type});
|
|
261
270
|
const peerAction = rejectPeerAction(type, e.type.code);
|
|
262
271
|
core.reportPeer(propagationSource, peerAction, e.type.code);
|
|
272
|
+
if (peerAction === PeerAction.Fatal) onFatalPeer(propagationSource);
|
|
263
273
|
logger.debug(
|
|
264
274
|
`Gossip validation ${type} rejected`,
|
|
265
275
|
{peer: propagationSource, clientAgent, clientVersion, peerAction},
|