@lodestar/beacon-node 1.49.0-dev.ae6cc426f1 → 1.49.0-dev.b27f498c2c
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.js +2 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/validator/index.js +6 -6
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +9 -2
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/index.js +2 -2
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/errors/attesterSlashingError.d.ts +4 -1
- package/lib/chain/errors/attesterSlashingError.d.ts.map +1 -1
- package/lib/chain/errors/attesterSlashingError.js +1 -0
- package/lib/chain/errors/attesterSlashingError.js.map +1 -1
- package/lib/chain/errors/proposerSlashingError.d.ts +4 -1
- package/lib/chain/errors/proposerSlashingError.d.ts.map +1 -1
- package/lib/chain/errors/proposerSlashingError.js +1 -0
- package/lib/chain/errors/proposerSlashingError.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -6
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +3 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -2
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +1 -2
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +2 -2
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +15 -0
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +1 -1
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +1 -1
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +16 -5
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +2 -0
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +88 -7
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/blocks/index.ts +2 -2
- package/src/api/impl/validator/index.ts +6 -6
- package/src/chain/archiveStore/utils/pruneHistory.ts +9 -2
- package/src/chain/blocks/index.ts +2 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -1
- package/src/chain/errors/attesterSlashingError.ts +3 -1
- package/src/chain/errors/proposerSlashingError.ts +3 -1
- package/src/chain/produceBlock/produceBlockBody.ts +17 -6
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +3 -2
- package/src/chain/validation/attesterSlashing.ts +1 -2
- package/src/chain/validation/proposerSlashing.ts +1 -2
- package/src/chain/validatorMonitor.ts +12 -2
- package/src/network/gossip/gossipsub.ts +18 -1
- package/src/network/gossip/topic.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +18 -5
- package/src/network/processor/gossipValidatorFn.ts +111 -7
|
@@ -59,7 +59,18 @@ import {
|
|
|
59
59
|
heze,
|
|
60
60
|
ssz,
|
|
61
61
|
} from "@lodestar/types";
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
GWEI_TO_WEI,
|
|
64
|
+
Logger,
|
|
65
|
+
byteArrayEquals,
|
|
66
|
+
fromHex,
|
|
67
|
+
prettyGweiToEth,
|
|
68
|
+
prettyWeiToEth,
|
|
69
|
+
sleep,
|
|
70
|
+
toHex,
|
|
71
|
+
toPubkeyHex,
|
|
72
|
+
toRootHex,
|
|
73
|
+
} from "@lodestar/utils";
|
|
63
74
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
64
75
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
65
76
|
import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from "../../execution/index.js";
|
|
@@ -259,7 +270,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
259
270
|
this.logger.verbose("Produced block with builder bid", {
|
|
260
271
|
slot: blockSlot,
|
|
261
272
|
builderIndex: builderBid.message.builderIndex,
|
|
262
|
-
bidValue: builderBid.message.value,
|
|
273
|
+
bidValue: prettyGweiToEth(builderBid.message.value),
|
|
263
274
|
parentBlockHash: toRootHex(builderBid.message.parentBlockHash),
|
|
264
275
|
parentBlockRoot: toRootHex(builderBid.message.parentBlockRoot),
|
|
265
276
|
blockHash: toRootHex(builderBid.message.blockHash),
|
|
@@ -410,7 +421,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
410
421
|
this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
|
|
411
422
|
this.logger.verbose("Produced block with self-build bid", {
|
|
412
423
|
slot: blockSlot,
|
|
413
|
-
executionPayloadValue,
|
|
424
|
+
executionPayloadValue: prettyWeiToEth(executionPayloadValue),
|
|
414
425
|
prepType,
|
|
415
426
|
payloadId,
|
|
416
427
|
fetchedTime,
|
|
@@ -490,7 +501,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
490
501
|
this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
|
|
491
502
|
this.logger.verbose("Fetched execution payload header from builder", {
|
|
492
503
|
slot: blockSlot,
|
|
493
|
-
executionPayloadValue,
|
|
504
|
+
executionPayloadValue: prettyWeiToEth(executionPayloadValue),
|
|
494
505
|
prepType,
|
|
495
506
|
fetchedTime,
|
|
496
507
|
});
|
|
@@ -607,7 +618,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
607
618
|
this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
|
|
608
619
|
this.logger.verbose("Fetched execution payload from engine", {
|
|
609
620
|
slot: blockSlot,
|
|
610
|
-
executionPayloadValue,
|
|
621
|
+
executionPayloadValue: prettyWeiToEth(executionPayloadValue),
|
|
611
622
|
prepType,
|
|
612
623
|
payloadId,
|
|
613
624
|
fetchedTime,
|
|
@@ -708,7 +719,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
708
719
|
}
|
|
709
720
|
}
|
|
710
721
|
|
|
711
|
-
Object.assign(logMeta, {executionPayloadValue});
|
|
722
|
+
Object.assign(logMeta, {executionPayloadValue: prettyWeiToEth(executionPayloadValue)});
|
|
712
723
|
this.logger.verbose("Produced beacon block body", logMeta);
|
|
713
724
|
|
|
714
725
|
return {body: blockBody as AssembledBodyType<T>, produceResult, executionPayloadValue, shouldOverrideBuilder};
|
|
@@ -256,10 +256,11 @@ export class SeenPayloadEnvelopeInput {
|
|
|
256
256
|
return this.payloadInputs.size;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
/** Removes the single PayloadEnvelopeInput from the cache */
|
|
260
|
+
remove(blockRootHex: RootHex): void {
|
|
260
261
|
const input = this.payloadInputs.get(blockRootHex);
|
|
261
262
|
if (input) {
|
|
262
|
-
this.evictPayloadInput(input, "
|
|
263
|
+
this.evictPayloadInput(input, "remove");
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
|
|
@@ -63,8 +63,7 @@ export async function validateAttesterSlashing(
|
|
|
63
63
|
const signatureSets = getAttesterSlashingSignatureSets(chain.config, state.slot, attesterSlashing);
|
|
64
64
|
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
|
65
65
|
throw new AttesterSlashingError(GossipAction.REJECT, {
|
|
66
|
-
code: AttesterSlashingErrorCode.
|
|
67
|
-
error: Error("Invalid signature"),
|
|
66
|
+
code: AttesterSlashingErrorCode.INVALID_SIGNATURE,
|
|
68
67
|
});
|
|
69
68
|
}
|
|
70
69
|
}
|
|
@@ -48,8 +48,7 @@ async function validateProposerSlashing(
|
|
|
48
48
|
const signatureSets = getProposerSlashingSignatureSets(chain.config, state.slot, proposerSlashing);
|
|
49
49
|
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
|
50
50
|
throw new ProposerSlashingError(GossipAction.REJECT, {
|
|
51
|
-
code: ProposerSlashingErrorCode.
|
|
52
|
-
error: Error("Invalid signature"),
|
|
51
|
+
code: ProposerSlashingErrorCode.INVALID_SIGNATURE,
|
|
53
52
|
});
|
|
54
53
|
}
|
|
55
54
|
}
|
|
@@ -24,7 +24,17 @@ import {
|
|
|
24
24
|
deneb,
|
|
25
25
|
gloas,
|
|
26
26
|
} from "@lodestar/types";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
LogData,
|
|
29
|
+
LogHandler,
|
|
30
|
+
LogLevel,
|
|
31
|
+
Logger,
|
|
32
|
+
MapDef,
|
|
33
|
+
MapDefMax,
|
|
34
|
+
prettyGweiToEth,
|
|
35
|
+
prettyPrintIndices,
|
|
36
|
+
toRootHex,
|
|
37
|
+
} from "@lodestar/utils";
|
|
28
38
|
import {GENESIS_SLOT} from "../constants/constants.js";
|
|
29
39
|
import {RegistryMetricCreator} from "../metrics/index.js";
|
|
30
40
|
|
|
@@ -470,7 +480,7 @@ export function createValidatorMonitor(
|
|
|
470
480
|
src,
|
|
471
481
|
builderIndex: bid.builderIndex,
|
|
472
482
|
gasLimit: Number(bid.gasLimit),
|
|
473
|
-
value: bid.value
|
|
483
|
+
value: prettyGweiToEth(bid.value),
|
|
474
484
|
parentBlockRoot: toRootHex(bid.parentBlockRoot),
|
|
475
485
|
parentBlockHash: toRootHex(bid.parentBlockHash),
|
|
476
486
|
blockHash: toRootHex(bid.blockHash),
|
|
@@ -14,7 +14,7 @@ import {peerIdFromString} from "@libp2p/peer-id";
|
|
|
14
14
|
import {type Multiaddr, multiaddr} from "@multiformats/multiaddr";
|
|
15
15
|
import {ENR} from "@chainsafe/enr";
|
|
16
16
|
import {routes} from "@lodestar/api";
|
|
17
|
-
import {BeaconConfig, ForkBoundary} from "@lodestar/config";
|
|
17
|
+
import {BeaconConfig, ChainConfig, ForkBoundary} from "@lodestar/config";
|
|
18
18
|
import {
|
|
19
19
|
ATTESTATION_SUBNET_COUNT,
|
|
20
20
|
MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE,
|
|
@@ -47,6 +47,22 @@ const GOSSIPSUB_HEARTBEAT_INTERVAL = 0.7 * 1000;
|
|
|
47
47
|
|
|
48
48
|
const MAX_OUTBOUND_BUFFER_SIZE = 2 ** 24; // 16MB
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Snappy worst-case compressed length for a payload of `n` bytes
|
|
52
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-beta.0/specs/phase0/p2p-interface.md#max_compressed_len
|
|
53
|
+
*/
|
|
54
|
+
function maxCompressedLen(n: number): number {
|
|
55
|
+
return 32 + n + Math.floor(n / 6);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Max size of an inbound gossipsub RPC frame, including all bundled messages and control
|
|
60
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-beta.0/specs/phase0/p2p-interface.md#max_message_size
|
|
61
|
+
*/
|
|
62
|
+
function getMaxInboundDataLength(config: Pick<ChainConfig, "MAX_PAYLOAD_SIZE">): number {
|
|
63
|
+
return Math.max(maxCompressedLen(config.MAX_PAYLOAD_SIZE) + 1024, 1024 * 1024);
|
|
64
|
+
}
|
|
65
|
+
|
|
50
66
|
export type Eth2Context = {
|
|
51
67
|
activeValidatorCount: number;
|
|
52
68
|
currentSlot: number;
|
|
@@ -176,6 +192,7 @@ export class Eth2Gossipsub {
|
|
|
176
192
|
asyncValidation: true,
|
|
177
193
|
|
|
178
194
|
maxOutboundBufferSize: MAX_OUTBOUND_BUFFER_SIZE,
|
|
195
|
+
maxInboundDataLength: getMaxInboundDataLength(config),
|
|
179
196
|
// serialize message once and send to all peers when publishing
|
|
180
197
|
batchPublish: true,
|
|
181
198
|
// if this is false, only publish to mesh peers. If there is not enough GOSSIP_D mesh peers,
|
|
@@ -150,8 +150,8 @@ export function getGossipSSZType(topic: GossipTopic) {
|
|
|
150
150
|
/**
|
|
151
151
|
* Return the maximum uncompressed SSZ byte length allowed by the type and configured network bounds.
|
|
152
152
|
*/
|
|
153
|
-
export function getGossipSSZMaxSize(topic: GossipTopic, config: ChainForkConfig, sszType
|
|
154
|
-
const maxSize = Math.min(
|
|
153
|
+
export function getGossipSSZMaxSize(topic: GossipTopic, config: ChainForkConfig, sszType: CompositeTypeAny): number {
|
|
154
|
+
const maxSize = Math.min(sszType.maxSize, config.MAX_PAYLOAD_SIZE);
|
|
155
155
|
if (isForkPostGloas(topic.boundary.fork) && topic.type === GossipType.data_column_sidecar) {
|
|
156
156
|
return Math.min(maxSize, computeMaxGloasDataColumnSidecarSize(config));
|
|
157
157
|
}
|
|
@@ -244,8 +244,20 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
244
244
|
|
|
245
245
|
// IGNORE means the block is acceptable (e.g. FUTURE_SLOT, ALREADY_KNOWN), just not propagated.
|
|
246
246
|
// Keep the optimistically-added cache entries; they are pruned on finalization. Only REJECT
|
|
247
|
-
// (provably invalid)
|
|
247
|
+
// (provably invalid), unexpected errors and repeat proposals that are not imported prune.
|
|
248
248
|
if (e.action === GossipAction.IGNORE) {
|
|
249
|
+
// Only a signature-verified sibling is imported by the beacon_block handler, any other repeat proposal
|
|
250
|
+
// is dropped from the caches and re-downloaded by sync if it ever becomes relevant. Its signature was not
|
|
251
|
+
// verified, so only its own entry is removed, never its claimed ancestors
|
|
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
|
+
}
|
|
249
261
|
throw e;
|
|
250
262
|
}
|
|
251
263
|
|
|
@@ -258,10 +270,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
258
270
|
}
|
|
259
271
|
|
|
260
272
|
// REJECT or unexpected (non-BlockGossipError) error: drop the optimistically-added entries from
|
|
261
|
-
// both caches, keeping them consistent.
|
|
262
|
-
chain.seenBlockInputCache.
|
|
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);
|
|
263
275
|
if (isForkPostGloas(fork)) {
|
|
264
|
-
chain.seenPayloadEnvelopeInputCache.
|
|
276
|
+
chain.seenPayloadEnvelopeInputCache.remove(blockRootHex);
|
|
265
277
|
}
|
|
266
278
|
throw e;
|
|
267
279
|
} finally {
|
|
@@ -738,7 +750,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
738
750
|
if (
|
|
739
751
|
e instanceof BlockGossipError &&
|
|
740
752
|
e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
|
|
741
|
-
//
|
|
753
|
+
// Only a signature-verified sibling recorded in the seen cache is imported. The cache holds at most two
|
|
754
|
+
// roots per proposer and slot, which bounds full imports over gossip to one alternate
|
|
742
755
|
chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root)
|
|
743
756
|
) {
|
|
744
757
|
// blockInput was optimistically seeded in validateBeaconBlock and retained on IGNORE
|
|
@@ -1,24 +1,122 @@
|
|
|
1
1
|
import {TopicValidatorResult} from "@libp2p/gossipsub";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
3
|
import {Logger} from "@lodestar/utils";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AttestationError,
|
|
6
|
+
AttestationErrorCode,
|
|
7
|
+
AttesterSlashingErrorCode,
|
|
8
|
+
BlobSidecarErrorCode,
|
|
9
|
+
BlockErrorCode,
|
|
10
|
+
BlsToExecutionChangeErrorCode,
|
|
11
|
+
DataColumnSidecarErrorCode,
|
|
12
|
+
ExecutionPayloadBidErrorCode,
|
|
13
|
+
ExecutionPayloadEnvelopeErrorCode,
|
|
14
|
+
GossipAction,
|
|
15
|
+
GossipActionError,
|
|
16
|
+
PayloadAttestationErrorCode,
|
|
17
|
+
ProposerPreferencesErrorCode,
|
|
18
|
+
ProposerSlashingErrorCode,
|
|
19
|
+
SyncCommitteeErrorCode,
|
|
20
|
+
VoluntaryExitErrorCode,
|
|
21
|
+
} from "../../chain/errors/index.js";
|
|
5
22
|
import {Metrics} from "../../metrics/index.js";
|
|
23
|
+
import {INetworkCore} from "../core/index.js";
|
|
6
24
|
import {
|
|
7
25
|
BatchGossipHandlerFn,
|
|
8
26
|
GossipHandlerFn,
|
|
9
27
|
GossipHandlers,
|
|
10
28
|
GossipMessageInfo,
|
|
29
|
+
GossipType,
|
|
11
30
|
GossipValidatorBatchFn,
|
|
12
31
|
GossipValidatorFn,
|
|
13
32
|
} from "../gossip/interface.js";
|
|
33
|
+
import {PeerAction} from "../peers/index.js";
|
|
14
34
|
import {prettyPrintPeerIdStr} from "../util.js";
|
|
15
35
|
|
|
16
36
|
export type ValidatorFnModules = {
|
|
17
37
|
config: ChainForkConfig;
|
|
18
38
|
logger: Logger;
|
|
19
39
|
metrics: Metrics | null;
|
|
40
|
+
core: INetworkCore;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type RejectPeerActionRule = {default: PeerAction; byCode?: Record<string, PeerAction>};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* PeerAction mapping based on the topic and specific codes.
|
|
47
|
+
* Invalid signatures are Fatal on every topic.
|
|
48
|
+
*/
|
|
49
|
+
const gossipRejectPeerAction: Record<GossipType, RejectPeerActionRule> = {
|
|
50
|
+
[GossipType.beacon_block]: {
|
|
51
|
+
default: PeerAction.LowToleranceError,
|
|
52
|
+
byCode: {[BlockErrorCode.PROPOSAL_SIGNATURE_INVALID]: PeerAction.Fatal},
|
|
53
|
+
},
|
|
54
|
+
[GossipType.blob_sidecar]: {
|
|
55
|
+
default: PeerAction.LowToleranceError,
|
|
56
|
+
byCode: {[BlobSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID]: PeerAction.Fatal},
|
|
57
|
+
},
|
|
58
|
+
[GossipType.data_column_sidecar]: {
|
|
59
|
+
default: PeerAction.LowToleranceError,
|
|
60
|
+
byCode: {[DataColumnSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID]: PeerAction.Fatal},
|
|
61
|
+
},
|
|
62
|
+
[GossipType.execution_payload]: {
|
|
63
|
+
default: PeerAction.LowToleranceError,
|
|
64
|
+
byCode: {[ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
65
|
+
},
|
|
66
|
+
[GossipType.beacon_aggregate_and_proof]: {
|
|
67
|
+
default: PeerAction.MidToleranceError,
|
|
68
|
+
byCode: {[AttestationErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
69
|
+
},
|
|
70
|
+
[GossipType.beacon_attestation]: {
|
|
71
|
+
default: PeerAction.MidToleranceError,
|
|
72
|
+
byCode: {[AttestationErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
73
|
+
},
|
|
74
|
+
[GossipType.sync_committee_contribution_and_proof]: {
|
|
75
|
+
default: PeerAction.MidToleranceError,
|
|
76
|
+
byCode: {[SyncCommitteeErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
77
|
+
},
|
|
78
|
+
[GossipType.sync_committee]: {
|
|
79
|
+
default: PeerAction.MidToleranceError,
|
|
80
|
+
byCode: {[SyncCommitteeErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
81
|
+
},
|
|
82
|
+
[GossipType.payload_attestation_message]: {
|
|
83
|
+
default: PeerAction.MidToleranceError,
|
|
84
|
+
byCode: {[PayloadAttestationErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
85
|
+
},
|
|
86
|
+
[GossipType.execution_payload_bid]: {
|
|
87
|
+
default: PeerAction.HighToleranceError,
|
|
88
|
+
byCode: {[ExecutionPayloadBidErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
89
|
+
},
|
|
90
|
+
[GossipType.proposer_preferences]: {
|
|
91
|
+
default: PeerAction.HighToleranceError,
|
|
92
|
+
byCode: {[ProposerPreferencesErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
93
|
+
},
|
|
94
|
+
[GossipType.voluntary_exit]: {
|
|
95
|
+
default: PeerAction.HighToleranceError,
|
|
96
|
+
byCode: {[VoluntaryExitErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
97
|
+
},
|
|
98
|
+
[GossipType.bls_to_execution_change]: {
|
|
99
|
+
default: PeerAction.HighToleranceError,
|
|
100
|
+
byCode: {[BlsToExecutionChangeErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
101
|
+
},
|
|
102
|
+
[GossipType.proposer_slashing]: {
|
|
103
|
+
default: PeerAction.HighToleranceError,
|
|
104
|
+
byCode: {[ProposerSlashingErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
105
|
+
},
|
|
106
|
+
[GossipType.attester_slashing]: {
|
|
107
|
+
default: PeerAction.HighToleranceError,
|
|
108
|
+
byCode: {[AttesterSlashingErrorCode.INVALID_SIGNATURE]: PeerAction.Fatal},
|
|
109
|
+
},
|
|
110
|
+
// light client validators only throw IGNORE
|
|
111
|
+
[GossipType.light_client_finality_update]: {default: PeerAction.HighToleranceError},
|
|
112
|
+
[GossipType.light_client_optimistic_update]: {default: PeerAction.HighToleranceError},
|
|
20
113
|
};
|
|
21
114
|
|
|
115
|
+
function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
116
|
+
const rule = gossipRejectPeerAction[type];
|
|
117
|
+
return rule.byCode?.[code] ?? rule.default;
|
|
118
|
+
}
|
|
119
|
+
|
|
22
120
|
/**
|
|
23
121
|
* Similar to getGossipValidatorFn but return a function to accept a batch of beacon_attestation messages
|
|
24
122
|
* with the same attestation data
|
|
@@ -27,7 +125,7 @@ export function getGossipValidatorBatchFn(
|
|
|
27
125
|
gossipHandlers: GossipHandlers,
|
|
28
126
|
modules: ValidatorFnModules
|
|
29
127
|
): GossipValidatorBatchFn {
|
|
30
|
-
const {logger, metrics} = modules;
|
|
128
|
+
const {logger, metrics, core} = modules;
|
|
31
129
|
|
|
32
130
|
return async function gossipValidatorBatchFn(messageInfos: GossipMessageInfo[]) {
|
|
33
131
|
// all messageInfos have same topic type
|
|
@@ -69,16 +167,19 @@ export function getGossipValidatorBatchFn(
|
|
|
69
167
|
// only beacon_attestation topic is validated in batch
|
|
70
168
|
metrics?.networkProcessor.gossipAttestationIgnoreByReason.inc({reason: e.type.code});
|
|
71
169
|
return TopicValidatorResult.Ignore;
|
|
72
|
-
case GossipAction.REJECT:
|
|
170
|
+
case GossipAction.REJECT: {
|
|
73
171
|
metrics?.networkProcessor.gossipValidationReject.inc({topic: type});
|
|
74
172
|
// only beacon_attestation topic is validated in batch
|
|
75
173
|
metrics?.networkProcessor.gossipAttestationRejectByReason.inc({reason: e.type.code});
|
|
174
|
+
const peerAction = rejectPeerAction(type, e.type.code);
|
|
175
|
+
core.reportPeer(propagationSource, peerAction, e.type.code);
|
|
76
176
|
logger.debug(
|
|
77
177
|
`Gossip validation ${type} rejected`,
|
|
78
|
-
{
|
|
178
|
+
{peer: propagationSource, clientAgent, clientVersion, peerAction},
|
|
79
179
|
e
|
|
80
180
|
);
|
|
81
181
|
return TopicValidatorResult.Reject;
|
|
182
|
+
}
|
|
82
183
|
}
|
|
83
184
|
});
|
|
84
185
|
} catch (e) {
|
|
@@ -108,7 +209,7 @@ export function getGossipValidatorBatchFn(
|
|
|
108
209
|
* @see getGossipHandlers for reasoning on why GossipHandlerFn are used for gossip validation.
|
|
109
210
|
*/
|
|
110
211
|
export function getGossipValidatorFn(gossipHandlers: GossipHandlers, modules: ValidatorFnModules): GossipValidatorFn {
|
|
111
|
-
const {logger, metrics} = modules;
|
|
212
|
+
const {logger, metrics, core} = modules;
|
|
112
213
|
|
|
113
214
|
return async function gossipValidatorFn({
|
|
114
215
|
topic,
|
|
@@ -155,14 +256,17 @@ export function getGossipValidatorFn(gossipHandlers: GossipHandlers, modules: Va
|
|
|
155
256
|
metrics?.networkProcessor.gossipValidationIgnore.inc({topic: type});
|
|
156
257
|
return TopicValidatorResult.Ignore;
|
|
157
258
|
|
|
158
|
-
case GossipAction.REJECT:
|
|
259
|
+
case GossipAction.REJECT: {
|
|
159
260
|
metrics?.networkProcessor.gossipValidationReject.inc({topic: type});
|
|
261
|
+
const peerAction = rejectPeerAction(type, e.type.code);
|
|
262
|
+
core.reportPeer(propagationSource, peerAction, e.type.code);
|
|
160
263
|
logger.debug(
|
|
161
264
|
`Gossip validation ${type} rejected`,
|
|
162
|
-
{
|
|
265
|
+
{peer: propagationSource, clientAgent, clientVersion, peerAction},
|
|
163
266
|
e
|
|
164
267
|
);
|
|
165
268
|
return TopicValidatorResult.Reject;
|
|
269
|
+
}
|
|
166
270
|
}
|
|
167
271
|
}
|
|
168
272
|
};
|