@lodestar/beacon-node 1.47.0-dev.be9d5b774d → 1.47.0-dev.beabd3a65c
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 +3 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +8 -1
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/events/index.d.ts.map +1 -1
- package/lib/api/impl/events/index.js +2 -0
- package/lib/api/impl/events/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +7 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +11 -9
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +14 -6
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/index.d.ts +1 -1
- package/lib/chain/bls/index.d.ts.map +1 -1
- package/lib/chain/bls/interface.d.ts +5 -5
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +5 -11
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +116 -101
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +10 -65
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +29 -8
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/types.js +12 -0
- package/lib/chain/bls/multithread/types.js.map +1 -1
- package/lib/chain/bls/multithread/utils.d.ts +4 -0
- package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
- package/lib/chain/bls/multithread/utils.js +10 -0
- package/lib/chain/bls/multithread/utils.js.map +1 -1
- package/lib/chain/bls/multithread/worker.js +49 -23
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -10
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +8 -45
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +4 -4
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +18 -19
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +12 -5
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +1 -0
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +6 -1
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/attestationPool.d.ts +1 -1
- package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/attestationPool.js +1 -1
- package/lib/chain/opPools/attestationPool.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +3 -3
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +1 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
- package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
- package/lib/chain/seenCache/seenAttesters.js +25 -4
- package/lib/chain/seenCache/seenAttesters.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +1 -4
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +1 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -14
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +2 -2
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +1 -1
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +45 -20
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +87 -51
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +13 -0
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +37 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +22 -43
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +5 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/util.d.ts +0 -1
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +0 -4
- package/lib/network/util.js.map +1 -1
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +61 -22
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +2 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +23 -10
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +7 -13
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +16 -11
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +3 -1
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/package.json +35 -35
- package/src/api/impl/beacon/blocks/index.ts +3 -2
- package/src/api/impl/beacon/pool/index.ts +7 -1
- package/src/api/impl/beacon/state/utils.ts +2 -1
- package/src/api/impl/events/index.ts +3 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/blocks/importExecutionPayload.ts +12 -11
- package/src/chain/blocks/index.ts +18 -6
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
- package/src/chain/bls/index.ts +1 -1
- package/src/chain/bls/interface.ts +7 -3
- package/src/chain/bls/multithread/index.ts +148 -120
- package/src/chain/bls/multithread/jobItem.ts +12 -82
- package/src/chain/bls/multithread/types.ts +33 -8
- package/src/chain/bls/multithread/utils.ts +11 -0
- package/src/chain/bls/multithread/worker.ts +85 -27
- package/src/chain/bls/singleThread.ts +11 -55
- package/src/chain/bls/utils.ts +23 -24
- package/src/chain/chain.ts +13 -6
- package/src/chain/errors/blockError.ts +8 -2
- package/src/chain/interface.ts +2 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/opPools/attestationPool.ts +1 -1
- package/src/chain/opPools/payloadAttestationPool.ts +3 -3
- package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
- package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/seenCache/seenAttesters.ts +31 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
- package/src/chain/validation/attestation.ts +1 -7
- package/src/chain/validation/attesterSlashing.ts +1 -8
- package/src/chain/validation/executionPayloadBid.ts +1 -2
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
- package/src/chain/validation/payloadAttestationMessage.ts +6 -17
- package/src/chain/validation/proposerPreferences.ts +3 -3
- package/src/chain/validation/proposerSlashing.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +97 -54
- package/src/network/gossip/topic.ts +38 -0
- package/src/network/network.ts +27 -55
- package/src/network/processor/gossipHandlers.ts +5 -1
- package/src/network/util.ts +0 -5
- package/src/node/nodejs.ts +2 -1
- package/src/sync/range/batch.ts +83 -28
- package/src/sync/range/chain.ts +26 -11
- package/src/sync/unknownBlock.ts +8 -15
- package/src/sync/utils/downloadByRange.ts +21 -12
- package/src/sync/utils/downloadByRoot.ts +3 -1
- package/lib/chain/bls/maybeBatch.d.ts +0 -12
- package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
- package/lib/chain/bls/maybeBatch.js +0 -35
- package/lib/chain/bls/maybeBatch.js.map +0 -1
- package/src/chain/bls/maybeBatch.ts +0 -45
|
@@ -414,3 +414,41 @@ export const gossipTopicIgnoreDuplicatePublishError: Record<GossipType, boolean>
|
|
|
414
414
|
[GossipType.execution_payload_bid]: true,
|
|
415
415
|
[GossipType.proposer_preferences]: true,
|
|
416
416
|
};
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Whether a publish that reached zero subscribed peers is an acceptable outcome for a topic.
|
|
420
|
+
*
|
|
421
|
+
* Publishing to a topic with no subscribed peers throws `PublishError.NoPeersSubscribedToTopic`, which
|
|
422
|
+
* propagates to the caller and, for messages submitted through the beacon API, is surfaced to the
|
|
423
|
+
* validator client. That is the right default: a message that reached nobody is a failed broadcast, and a
|
|
424
|
+
* VC configured with several beacon nodes can fall back to another one.
|
|
425
|
+
*
|
|
426
|
+
* Only topics that have a concrete reason to tolerate it opt out here, ie. are set to `true`, and the
|
|
427
|
+
* reason is named on the entry. Everything else is `false` and keeps raising. Note this is applied per
|
|
428
|
+
* publish, a topic set to `false` still honors the global `--network.allowPublishToZeroPeers` flag.
|
|
429
|
+
*/
|
|
430
|
+
export const gossipTopicAllowPublishToZeroPeers: Record<GossipType, boolean> = {
|
|
431
|
+
[GossipType.beacon_block]: false,
|
|
432
|
+
[GossipType.blob_sidecar]: false,
|
|
433
|
+
// data_column_sidecar: we ensure having all topic peers via prioritizePeers(). Even with 0 peers on the
|
|
434
|
+
// topic the overall publish can still succeed, because a supernode rebuilds and publishes the missing
|
|
435
|
+
// columns for us, so track sent peers as 0 instead of raising
|
|
436
|
+
[GossipType.data_column_sidecar]: true,
|
|
437
|
+
[GossipType.beacon_aggregate_and_proof]: false,
|
|
438
|
+
[GossipType.beacon_attestation]: false,
|
|
439
|
+
[GossipType.voluntary_exit]: false,
|
|
440
|
+
[GossipType.proposer_slashing]: false,
|
|
441
|
+
[GossipType.attester_slashing]: false,
|
|
442
|
+
[GossipType.sync_committee_contribution_and_proof]: false,
|
|
443
|
+
[GossipType.sync_committee]: false,
|
|
444
|
+
// light_client_finality_update and light_client_optimistic_update: non-mandatory route on most of the
|
|
445
|
+
// network as of Oct 2022, there may be no peer subscribed to the topic at all. Reason carried over from
|
|
446
|
+
// the handlers in network.ts, which used to swallow the error instead
|
|
447
|
+
[GossipType.light_client_finality_update]: true,
|
|
448
|
+
[GossipType.light_client_optimistic_update]: true,
|
|
449
|
+
[GossipType.bls_to_execution_change]: false,
|
|
450
|
+
[GossipType.execution_payload]: false,
|
|
451
|
+
[GossipType.payload_attestation_message]: false,
|
|
452
|
+
[GossipType.execution_payload_bid]: false,
|
|
453
|
+
[GossipType.proposer_preferences]: false,
|
|
454
|
+
};
|
package/src/network/network.ts
CHANGED
|
@@ -49,7 +49,12 @@ import {INetworkCore, NetworkCore, WorkerNetworkCore} from "./core/index.js";
|
|
|
49
49
|
import {INetworkEventBus, NetworkEvent, NetworkEventBus, NetworkEventData} from "./events.js";
|
|
50
50
|
import {getActiveForkBoundaries} from "./forks.js";
|
|
51
51
|
import {GossipHandlers, GossipTopicMap, GossipType, GossipTypeMap} from "./gossip/index.js";
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
getGossipSSZType,
|
|
54
|
+
gossipTopicAllowPublishToZeroPeers,
|
|
55
|
+
gossipTopicIgnoreDuplicatePublishError,
|
|
56
|
+
stringifyGossipTopic,
|
|
57
|
+
} from "./gossip/topic.js";
|
|
53
58
|
import {INetwork} from "./interface.js";
|
|
54
59
|
import {NetworkOptions} from "./options.js";
|
|
55
60
|
import {PeerAction, PeerScoreStats} from "./peers/index.js";
|
|
@@ -65,7 +70,7 @@ import {
|
|
|
65
70
|
} from "./reqresp/utils/collect.js";
|
|
66
71
|
import {collectSequentialBlocksInRange} from "./reqresp/utils/collectSequentialBlocksInRange.js";
|
|
67
72
|
import {CommitteeSubscription} from "./subnets/index.js";
|
|
68
|
-
import {isPublishDuplicateError,
|
|
73
|
+
import {isPublishDuplicateError, prettyPrintPeerIdStr} from "./util.js";
|
|
69
74
|
|
|
70
75
|
type NetworkModules = {
|
|
71
76
|
opts: NetworkOptions;
|
|
@@ -352,9 +357,7 @@ export class Network implements INetwork {
|
|
|
352
357
|
const epoch = computeEpochAtSlot(signedBlock.message.slot);
|
|
353
358
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
354
359
|
|
|
355
|
-
return this.publishGossip<GossipType.beacon_block>({type: GossipType.beacon_block, boundary}, signedBlock
|
|
356
|
-
ignoreDuplicatePublishError: true,
|
|
357
|
-
});
|
|
360
|
+
return this.publishGossip<GossipType.beacon_block>({type: GossipType.beacon_block, boundary}, signedBlock);
|
|
358
361
|
}
|
|
359
362
|
|
|
360
363
|
async publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number> {
|
|
@@ -363,9 +366,7 @@ export class Network implements INetwork {
|
|
|
363
366
|
|
|
364
367
|
const subnet = blobSidecar.index;
|
|
365
368
|
|
|
366
|
-
return this.publishGossip<GossipType.blob_sidecar>({type: GossipType.blob_sidecar, boundary, subnet}, blobSidecar
|
|
367
|
-
ignoreDuplicatePublishError: true,
|
|
368
|
-
});
|
|
369
|
+
return this.publishGossip<GossipType.blob_sidecar>({type: GossipType.blob_sidecar, boundary, subnet}, blobSidecar);
|
|
369
370
|
}
|
|
370
371
|
|
|
371
372
|
async publishDataColumnSidecar(
|
|
@@ -381,14 +382,7 @@ export class Network implements INetwork {
|
|
|
381
382
|
try {
|
|
382
383
|
const sentPeers = await this.publishGossip<GossipType.data_column_sidecar>(
|
|
383
384
|
{type: GossipType.data_column_sidecar, boundary, subnet},
|
|
384
|
-
dataColumnSidecar
|
|
385
|
-
{
|
|
386
|
-
// we ensure having all topic peers via prioritizePeers() function
|
|
387
|
-
// in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
|
|
388
|
-
// because supernode will rebuild and publish missing data column sidecars for us
|
|
389
|
-
// hence we want to track sent peers as 0 instead of an error
|
|
390
|
-
allowPublishToZeroTopicPeers: true,
|
|
391
|
-
}
|
|
385
|
+
dataColumnSidecar
|
|
392
386
|
);
|
|
393
387
|
return {sentPeers, alreadyPublished: false};
|
|
394
388
|
} catch (e) {
|
|
@@ -405,8 +399,7 @@ export class Network implements INetwork {
|
|
|
405
399
|
|
|
406
400
|
return this.publishGossip<GossipType.beacon_aggregate_and_proof>(
|
|
407
401
|
{type: GossipType.beacon_aggregate_and_proof, boundary},
|
|
408
|
-
aggregateAndProof
|
|
409
|
-
{ignoreDuplicatePublishError: true}
|
|
402
|
+
aggregateAndProof
|
|
410
403
|
);
|
|
411
404
|
}
|
|
412
405
|
|
|
@@ -416,8 +409,7 @@ export class Network implements INetwork {
|
|
|
416
409
|
|
|
417
410
|
return this.publishGossip<GossipType.beacon_attestation>(
|
|
418
411
|
{type: GossipType.beacon_attestation, boundary, subnet},
|
|
419
|
-
attestation
|
|
420
|
-
{ignoreDuplicatePublishError: true}
|
|
412
|
+
attestation
|
|
421
413
|
);
|
|
422
414
|
}
|
|
423
415
|
|
|
@@ -425,9 +417,7 @@ export class Network implements INetwork {
|
|
|
425
417
|
const epoch = voluntaryExit.message.epoch;
|
|
426
418
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
427
419
|
|
|
428
|
-
return this.publishGossip<GossipType.voluntary_exit>({type: GossipType.voluntary_exit, boundary}, voluntaryExit
|
|
429
|
-
ignoreDuplicatePublishError: true,
|
|
430
|
-
});
|
|
420
|
+
return this.publishGossip<GossipType.voluntary_exit>({type: GossipType.voluntary_exit, boundary}, voluntaryExit);
|
|
431
421
|
}
|
|
432
422
|
|
|
433
423
|
async publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number> {
|
|
@@ -438,8 +428,7 @@ export class Network implements INetwork {
|
|
|
438
428
|
if (fork >= ForkSeq.capella) {
|
|
439
429
|
const publishPromise = this.publishGossip<GossipType.bls_to_execution_change>(
|
|
440
430
|
{type: GossipType.bls_to_execution_change, boundary},
|
|
441
|
-
blsToExecutionChange
|
|
442
|
-
{ignoreDuplicatePublishError: true}
|
|
431
|
+
blsToExecutionChange
|
|
443
432
|
);
|
|
444
433
|
publishChanges.push(publishPromise);
|
|
445
434
|
}
|
|
@@ -457,8 +446,7 @@ export class Network implements INetwork {
|
|
|
457
446
|
|
|
458
447
|
return this.publishGossip<GossipType.proposer_slashing>(
|
|
459
448
|
{type: GossipType.proposer_slashing, boundary},
|
|
460
|
-
proposerSlashing
|
|
461
|
-
{ignoreDuplicatePublishError: true}
|
|
449
|
+
proposerSlashing
|
|
462
450
|
);
|
|
463
451
|
}
|
|
464
452
|
|
|
@@ -478,10 +466,7 @@ export class Network implements INetwork {
|
|
|
478
466
|
|
|
479
467
|
return this.publishGossip<GossipType.sync_committee>(
|
|
480
468
|
{type: GossipType.sync_committee, boundary, subnet},
|
|
481
|
-
signature
|
|
482
|
-
{
|
|
483
|
-
ignoreDuplicatePublishError: true,
|
|
484
|
-
}
|
|
469
|
+
signature
|
|
485
470
|
);
|
|
486
471
|
}
|
|
487
472
|
|
|
@@ -491,8 +476,7 @@ export class Network implements INetwork {
|
|
|
491
476
|
|
|
492
477
|
return this.publishGossip<GossipType.sync_committee_contribution_and_proof>(
|
|
493
478
|
{type: GossipType.sync_committee_contribution_and_proof, boundary},
|
|
494
|
-
contributionAndProof
|
|
495
|
-
{ignoreDuplicatePublishError: true}
|
|
479
|
+
contributionAndProof
|
|
496
480
|
);
|
|
497
481
|
}
|
|
498
482
|
|
|
@@ -522,8 +506,7 @@ export class Network implements INetwork {
|
|
|
522
506
|
|
|
523
507
|
return this.publishGossip<GossipType.execution_payload>(
|
|
524
508
|
{type: GossipType.execution_payload, boundary},
|
|
525
|
-
signedEnvelope
|
|
526
|
-
{ignoreDuplicatePublishError: true}
|
|
509
|
+
signedEnvelope
|
|
527
510
|
);
|
|
528
511
|
}
|
|
529
512
|
|
|
@@ -533,8 +516,7 @@ export class Network implements INetwork {
|
|
|
533
516
|
|
|
534
517
|
return this.publishGossip<GossipType.execution_payload_bid>(
|
|
535
518
|
{type: GossipType.execution_payload_bid, boundary},
|
|
536
|
-
signedBid
|
|
537
|
-
{ignoreDuplicatePublishError: true}
|
|
519
|
+
signedBid
|
|
538
520
|
);
|
|
539
521
|
}
|
|
540
522
|
|
|
@@ -544,8 +526,7 @@ export class Network implements INetwork {
|
|
|
544
526
|
|
|
545
527
|
return this.publishGossip<GossipType.payload_attestation_message>(
|
|
546
528
|
{type: GossipType.payload_attestation_message, boundary},
|
|
547
|
-
payloadAttestationMessage
|
|
548
|
-
{ignoreDuplicatePublishError: true}
|
|
529
|
+
payloadAttestationMessage
|
|
549
530
|
);
|
|
550
531
|
}
|
|
551
532
|
|
|
@@ -555,22 +536,21 @@ export class Network implements INetwork {
|
|
|
555
536
|
|
|
556
537
|
return this.publishGossip<GossipType.proposer_preferences>(
|
|
557
538
|
{type: GossipType.proposer_preferences, boundary},
|
|
558
|
-
signedProposerPreferences
|
|
559
|
-
{ignoreDuplicatePublishError: true}
|
|
539
|
+
signedProposerPreferences
|
|
560
540
|
);
|
|
561
541
|
}
|
|
562
542
|
|
|
563
543
|
private async publishGossip<K extends GossipType>(
|
|
564
544
|
topic: GossipTopicMap[K],
|
|
565
|
-
object: GossipTypeMap[K]
|
|
566
|
-
opts?: PublishOpts | undefined
|
|
545
|
+
object: GossipTypeMap[K]
|
|
567
546
|
): Promise<number> {
|
|
568
547
|
const topicStr = stringifyGossipTopic(this.config, topic);
|
|
569
548
|
const sszType = getGossipSSZType(topic);
|
|
570
549
|
const messageData = (sszType.serialize as (object: GossipTypeMap[GossipType]) => Uint8Array)(object);
|
|
571
|
-
opts = {
|
|
572
|
-
...opts,
|
|
550
|
+
const opts: PublishOpts = {
|
|
573
551
|
ignoreDuplicatePublishError: gossipTopicIgnoreDuplicatePublishError[topic.type],
|
|
552
|
+
// Leave undefined unless the topic opts out, so `--network.allowPublishToZeroPeers` still applies
|
|
553
|
+
allowPublishToZeroTopicPeers: gossipTopicAllowPublishToZeroPeers[topic.type] ? true : undefined,
|
|
574
554
|
};
|
|
575
555
|
const sentPeers = await this.core.publishGossip(topicStr, messageData, opts);
|
|
576
556
|
|
|
@@ -813,11 +793,7 @@ export class Network implements INetwork {
|
|
|
813
793
|
await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
|
|
814
794
|
await this.publishLightClientFinalityUpdate(finalityUpdate);
|
|
815
795
|
} catch (e) {
|
|
816
|
-
|
|
817
|
-
// Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
|
|
818
|
-
if (!isPublishToZeroPeersError(e as Error)) {
|
|
819
|
-
this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
|
|
820
|
-
}
|
|
796
|
+
this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
|
|
821
797
|
}
|
|
822
798
|
};
|
|
823
799
|
|
|
@@ -830,11 +806,7 @@ export class Network implements INetwork {
|
|
|
830
806
|
await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
|
|
831
807
|
await this.publishLightClientOptimisticUpdate(optimisticUpdate);
|
|
832
808
|
} catch (e) {
|
|
833
|
-
|
|
834
|
-
// Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
|
|
835
|
-
if (!isPublishToZeroPeersError(e as Error)) {
|
|
836
|
-
this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
|
|
837
|
-
}
|
|
809
|
+
this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
|
|
838
810
|
}
|
|
839
811
|
};
|
|
840
812
|
|
|
@@ -196,7 +196,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
196
196
|
forkName: fork,
|
|
197
197
|
sampledColumns: chain.custodyConfig.sampledColumns,
|
|
198
198
|
custodyColumns: chain.custodyConfig.custodyColumns,
|
|
199
|
-
|
|
199
|
+
seenTimestampSec,
|
|
200
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
200
201
|
});
|
|
201
202
|
}
|
|
202
203
|
|
|
@@ -640,6 +641,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
640
641
|
metrics?.gossipBlock.elapsedTimeTillProcessed.observe(delaySec);
|
|
641
642
|
|
|
642
643
|
if (isForkPostGloas(blockInput.forkName)) {
|
|
644
|
+
// we should have the payloadInput in the seen cache so no need getOrReload() here
|
|
643
645
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockInput.blockRootHex);
|
|
644
646
|
// This payloadInput should have been created just after gossip validation
|
|
645
647
|
if (!payloadInput) {
|
|
@@ -1189,6 +1191,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1189
1191
|
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
|
|
1190
1192
|
|
|
1191
1193
|
const blockRootHex = toRootHex(envelope.beaconBlockRoot);
|
|
1194
|
+
// a gossip payload cannot be `MAX_PAYLOAD_ENVELOPE_INPUT_CACHE_SIZE` slots after the block
|
|
1195
|
+
// otherwise it'll get to UnknownBlockInput flow
|
|
1192
1196
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
1193
1197
|
|
|
1194
1198
|
if (!payloadInput) {
|
package/src/network/util.ts
CHANGED
|
@@ -23,11 +23,6 @@ export function getConnection(libp2p: Libp2p, peerIdStr: string): Connection | u
|
|
|
23
23
|
return getConnectionsMap(libp2p).get(peerIdStr)?.value[0] ?? undefined;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
|
|
27
|
-
export function isPublishToZeroPeersError(e: Error): boolean {
|
|
28
|
-
return e.message.includes("PublishError.NoPeersSubscribedToTopic");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
// https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
|
|
32
27
|
export function isPublishDuplicateError(e: Error): boolean {
|
|
33
28
|
return e.message.includes("PublishError.Duplicate");
|
package/src/node/nodejs.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {setMaxListeners} from "node:events";
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Registry} from "prom-client";
|
|
4
|
+
import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
|
|
4
5
|
import {hasher} from "@chainsafe/persistent-merkle-tree";
|
|
5
6
|
import {BeaconApiMethods} from "@lodestar/api/beacon/server";
|
|
6
7
|
import {BeaconConfig} from "@lodestar/config";
|
|
7
8
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
8
9
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
9
|
-
import {IBeaconStateView,
|
|
10
|
+
import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
|
|
10
11
|
import {phase0} from "@lodestar/types";
|
|
11
12
|
import {sleep, toRootHex} from "@lodestar/utils";
|
|
12
13
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -39,6 +39,7 @@ export enum BatchStatus {
|
|
|
39
39
|
AwaitingValidation = "AwaitingValidation",
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** Identity of one download attempt*/
|
|
42
43
|
export type Attempt = {
|
|
43
44
|
/** The peer that made the attempt */
|
|
44
45
|
peers: PeerIdStr[];
|
|
@@ -46,6 +47,16 @@ export type Attempt = {
|
|
|
46
47
|
hash: RootHex;
|
|
47
48
|
};
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A failed attempt that has been classified for blame.
|
|
52
|
+
*/
|
|
53
|
+
export type FailedAttempt = Attempt & {
|
|
54
|
+
/**
|
|
55
|
+
* True when the failure is evidence the peers served bad data, so they may be downscored..
|
|
56
|
+
*/
|
|
57
|
+
peerAttributable: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
49
60
|
type TrackedRequest = {
|
|
50
61
|
/** only happen for the 1st batch in checkpoint sync */
|
|
51
62
|
parentPayload: boolean;
|
|
@@ -108,10 +119,12 @@ export type BatchMetadata = {
|
|
|
108
119
|
// Retry counters
|
|
109
120
|
downloadAttempts: number;
|
|
110
121
|
processingAttempts: number;
|
|
122
|
+
executionErrorAttempts: number;
|
|
111
123
|
|
|
112
124
|
// Cumulative peer attribution for failed attempts (only present when non-empty)
|
|
113
125
|
failedDownloadPeers?: string;
|
|
114
126
|
failedProcessingPeers?: string;
|
|
127
|
+
executionErrorPeers?: string;
|
|
115
128
|
};
|
|
116
129
|
|
|
117
130
|
function formatRangeReq(req: {startSlot: Slot; count: number}): string {
|
|
@@ -152,10 +165,10 @@ export class Batch {
|
|
|
152
165
|
state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
153
166
|
/** Peers that provided good data, with column coverage for by_range requests */
|
|
154
167
|
private readonly successfulDownloads = new Map<PeerIdStr, TrackedRequest>();
|
|
155
|
-
/** The
|
|
156
|
-
readonly failedProcessingAttempts:
|
|
157
|
-
/** The
|
|
158
|
-
readonly executionErrorAttempts:
|
|
168
|
+
/** The attempts that have been made and failed to send us this batch. */
|
|
169
|
+
readonly failedProcessingAttempts: FailedAttempt[] = [];
|
|
170
|
+
/** The attempts that have been made and failed because of execution malfunction. */
|
|
171
|
+
readonly executionErrorAttempts: FailedAttempt[] = [];
|
|
159
172
|
/** The number of download retries this batch has undergone due to a failed request. */
|
|
160
173
|
private readonly failedDownloadAttempts: PeerIdStr[] = [];
|
|
161
174
|
private readonly config: ChainForkConfig;
|
|
@@ -419,10 +432,18 @@ export class Batch {
|
|
|
419
432
|
}
|
|
420
433
|
|
|
421
434
|
/**
|
|
422
|
-
* Gives a list of peers from which this batch has had a failed download or
|
|
435
|
+
* Gives a list of peers from which this batch has had a failed download attempt, or a
|
|
436
|
+
* peer-attributable failed processing/execution attempt.
|
|
423
437
|
*/
|
|
424
438
|
getFailedPeers(): PeerIdStr[] {
|
|
425
|
-
return [...this.failedDownloadAttempts, ...this.
|
|
439
|
+
return [...this.failedDownloadAttempts, ...this.getPeerAttributableAttempts().flatMap((a) => a.peers)];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Attempts whose failure is peer-attributable, across both the processing and execution error
|
|
444
|
+
*/
|
|
445
|
+
getPeerAttributableAttempts(): FailedAttempt[] {
|
|
446
|
+
return [...this.failedProcessingAttempts, ...this.executionErrorAttempts].filter((a) => a.peerAttributable);
|
|
426
447
|
}
|
|
427
448
|
|
|
428
449
|
/**
|
|
@@ -457,6 +478,7 @@ export class Batch {
|
|
|
457
478
|
getMetadata(): BatchMetadata {
|
|
458
479
|
const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
|
|
459
480
|
const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
|
|
481
|
+
const executionErrorPeerList = this.executionErrorAttempts.flatMap((a) => a.peers);
|
|
460
482
|
return {
|
|
461
483
|
startEpoch: this.startEpoch,
|
|
462
484
|
startSlot: this.startSlot,
|
|
@@ -468,12 +490,16 @@ export class Batch {
|
|
|
468
490
|
...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
|
|
469
491
|
downloadAttempts: this.failedDownloadAttempts.length,
|
|
470
492
|
processingAttempts: this.failedProcessingAttempts.length,
|
|
493
|
+
executionErrorAttempts: this.executionErrorAttempts.length,
|
|
471
494
|
...(this.failedDownloadAttempts.length > 0 && {
|
|
472
495
|
failedDownloadPeers: this.failedDownloadAttempts.join(","),
|
|
473
496
|
}),
|
|
474
497
|
...(failedProcessingPeerList.length > 0 && {
|
|
475
498
|
failedProcessingPeers: failedProcessingPeerList.join(","),
|
|
476
499
|
}),
|
|
500
|
+
...(executionErrorPeerList.length > 0 && {
|
|
501
|
+
executionErrorPeers: executionErrorPeerList.join(","),
|
|
502
|
+
}),
|
|
477
503
|
};
|
|
478
504
|
}
|
|
479
505
|
|
|
@@ -703,11 +729,7 @@ export class Batch {
|
|
|
703
729
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.Processing));
|
|
704
730
|
}
|
|
705
731
|
|
|
706
|
-
|
|
707
|
-
this.onExecutionEngineError(this.state.attempt);
|
|
708
|
-
} else {
|
|
709
|
-
this.onProcessingError(this.state.attempt);
|
|
710
|
-
}
|
|
732
|
+
this.routeProcessingFailure(err, this.state.attempt);
|
|
711
733
|
}
|
|
712
734
|
|
|
713
735
|
/**
|
|
@@ -718,10 +740,17 @@ export class Batch {
|
|
|
718
740
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingValidation));
|
|
719
741
|
}
|
|
720
742
|
|
|
721
|
-
|
|
722
|
-
|
|
743
|
+
this.routeProcessingFailure(err, this.state.attempt);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
private routeProcessingFailure(err: Error, attempt: Attempt): void {
|
|
747
|
+
const code = err instanceof BlockError || err instanceof PayloadError ? err.type.code : null;
|
|
748
|
+
const failedAttempt: FailedAttempt = {...attempt, peerAttributable: isPeerAttributableFailure(code)};
|
|
749
|
+
|
|
750
|
+
if (isExecutionEngineFailure(code)) {
|
|
751
|
+
this.onExecutionEngineError(failedAttempt);
|
|
723
752
|
} else {
|
|
724
|
-
this.onProcessingError(
|
|
753
|
+
this.onProcessingError(failedAttempt);
|
|
725
754
|
}
|
|
726
755
|
}
|
|
727
756
|
|
|
@@ -735,7 +764,7 @@ export class Batch {
|
|
|
735
764
|
return this.state.attempt;
|
|
736
765
|
}
|
|
737
766
|
|
|
738
|
-
private onExecutionEngineError(attempt:
|
|
767
|
+
private onExecutionEngineError(attempt: FailedAttempt): void {
|
|
739
768
|
this.executionErrorAttempts.push(attempt);
|
|
740
769
|
if (this.executionErrorAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
|
|
741
770
|
throw new BatchError(this.errorType({code: BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS}));
|
|
@@ -746,7 +775,7 @@ export class Batch {
|
|
|
746
775
|
this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
747
776
|
}
|
|
748
777
|
|
|
749
|
-
private onProcessingError(attempt:
|
|
778
|
+
private onProcessingError(attempt: FailedAttempt): void {
|
|
750
779
|
this.failedProcessingAttempts.push(attempt);
|
|
751
780
|
if (this.failedProcessingAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
|
|
752
781
|
throw new BatchError(this.errorType({code: BatchErrorCode.MAX_PROCESSING_ATTEMPTS}));
|
|
@@ -789,18 +818,44 @@ type BatchErrorMetadata = {
|
|
|
789
818
|
|
|
790
819
|
export class BatchError extends LodestarError<BatchErrorType & BatchErrorMetadata> {}
|
|
791
820
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (err.type.code === BlockErrorCode.EXECUTION_ENGINE_ERROR) {
|
|
798
|
-
return true;
|
|
799
|
-
}
|
|
800
|
-
|
|
821
|
+
/**
|
|
822
|
+
* Whether a processing failure is an execution-engine failure. Pre-gloas, all EL errors stay in
|
|
823
|
+
* BlockErrorCode, post-gloas, they stay in PayloadErrorCode.
|
|
824
|
+
*/
|
|
825
|
+
function isExecutionEngineFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
|
|
801
826
|
return (
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
827
|
+
code === BlockErrorCode.EXECUTION_ENGINE_INVALID ||
|
|
828
|
+
code === BlockErrorCode.EXECUTION_ENGINE_ERROR ||
|
|
829
|
+
code === PayloadErrorCode.EXECUTION_ENGINE_INVALID ||
|
|
830
|
+
code === PayloadErrorCode.EXECUTION_ENGINE_ERROR
|
|
805
831
|
);
|
|
806
832
|
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Based on an error code, return if peer is attributable for FailedAttempt.
|
|
836
|
+
*/
|
|
837
|
+
function isPeerAttributableFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
|
|
838
|
+
switch (code) {
|
|
839
|
+
// EL returned a definitive INVALID verdict on the payload
|
|
840
|
+
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
841
|
+
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
842
|
+
// a block or envelope signature is invalid
|
|
843
|
+
case BlockErrorCode.INVALID_SIGNATURE:
|
|
844
|
+
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
845
|
+
// the block's state transition produced an invalid state root, or failed per_slot/per_block processing
|
|
846
|
+
case BlockErrorCode.INVALID_STATE_ROOT:
|
|
847
|
+
case BlockErrorCode.PER_BLOCK_PROCESSING_ERROR:
|
|
848
|
+
// the segment this peer served is internally inconsistent (in-segment link breaks, see PR #9684)
|
|
849
|
+
case BlockErrorCode.NON_LINEAR_SLOTS:
|
|
850
|
+
case BlockErrorCode.NON_LINEAR_PARENT_ROOTS:
|
|
851
|
+
case BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS:
|
|
852
|
+
// the envelope references a mismatched block root, or fails field verification
|
|
853
|
+
case BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH:
|
|
854
|
+
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
855
|
+
// the peer served a block we have explicitly blacklisted
|
|
856
|
+
case BlockErrorCode.BLACKLISTED_BLOCK:
|
|
857
|
+
return true;
|
|
858
|
+
default:
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
}
|
package/src/sync/range/chain.ts
CHANGED
|
@@ -358,9 +358,10 @@ export class SyncChain {
|
|
|
358
358
|
// If a batch exceeds it's retry limit, maybe downscore peers.
|
|
359
359
|
// shouldDownscoreOnBatchError() functions enforces that all BatchErrorCode values are covered
|
|
360
360
|
if (e instanceof BatchError) {
|
|
361
|
-
const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code);
|
|
361
|
+
const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code, this.batches.get(e.type.startEpoch));
|
|
362
362
|
if (shouldReportPeer) {
|
|
363
|
-
|
|
363
|
+
// Only the peers actually at fault are reported, never the whole peerset.
|
|
364
|
+
for (const peer of shouldReportPeer.peers) {
|
|
364
365
|
this.reportPeer(peer, shouldReportPeer.action, shouldReportPeer.reason);
|
|
365
366
|
}
|
|
366
367
|
}
|
|
@@ -768,8 +769,11 @@ export class SyncChain {
|
|
|
768
769
|
|
|
769
770
|
// The last batch attempt is right, all others are wrong. Penalize other peers
|
|
770
771
|
const attemptOk = batch.validationSuccess();
|
|
771
|
-
for (const attempt of batch.
|
|
772
|
+
for (const attempt of batch.getPeerAttributableAttempts()) {
|
|
772
773
|
if (attempt.hash !== attemptOk.hash) {
|
|
774
|
+
// attempt.peers contains block/column/envelope peers.
|
|
775
|
+
// And if there is one bad envelope we will downscore block and column peers to
|
|
776
|
+
// TODO: resolve this
|
|
773
777
|
for (const badAttemptPeer of attempt.peers) {
|
|
774
778
|
if (attemptOk.peers.find((goodPeer) => goodPeer === badAttemptPeer)) {
|
|
775
779
|
// The same peer corrected its previous attempt
|
|
@@ -830,21 +834,32 @@ export class SyncChain {
|
|
|
830
834
|
* If peer should not be downscored, returns null.
|
|
831
835
|
*/
|
|
832
836
|
export function shouldReportPeerOnBatchError(
|
|
833
|
-
code: BatchErrorCode
|
|
834
|
-
|
|
837
|
+
code: BatchErrorCode,
|
|
838
|
+
batch: Batch | undefined
|
|
839
|
+
): {action: PeerAction.LowToleranceError; reason: string; peers: PeerIdStr[]} | null {
|
|
835
840
|
switch (code) {
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
// We drop the chain and report all peers.
|
|
839
|
-
// There are some edge cases with forks that could cause this situation, but it's unlikely.
|
|
841
|
+
// A batch could not be processed after max retry limit. Report
|
|
842
|
+
// only the peers that served a peer-attributable failed attempt.
|
|
840
843
|
case BatchErrorCode.MAX_PROCESSING_ATTEMPTS:
|
|
841
|
-
|
|
844
|
+
case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS: {
|
|
845
|
+
// penalize both the processing-error and execution-error peers
|
|
846
|
+
const attributablePeers = [
|
|
847
|
+
...new Set(batch?.getPeerAttributableAttempts().flatMap((attempt) => attempt.peers) ?? []),
|
|
848
|
+
];
|
|
849
|
+
if (attributablePeers.length === 0) {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
const reason =
|
|
853
|
+
code === BatchErrorCode.MAX_PROCESSING_ATTEMPTS
|
|
854
|
+
? "SyncChainMaxProcessingAttempts"
|
|
855
|
+
: "SyncChainMaxExecutionEngineErrorAttempts";
|
|
856
|
+
return {action: PeerAction.LowToleranceError, reason, peers: attributablePeers};
|
|
857
|
+
}
|
|
842
858
|
|
|
843
859
|
// TODO: Should peers be reported for MAX_DOWNLOAD_ATTEMPTS?
|
|
844
860
|
case BatchErrorCode.MAX_DOWNLOAD_ATTEMPTS:
|
|
845
861
|
case BatchErrorCode.INVALID_COUNT:
|
|
846
862
|
case BatchErrorCode.WRONG_STATUS:
|
|
847
|
-
case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS:
|
|
848
863
|
return null;
|
|
849
864
|
}
|
|
850
865
|
}
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -544,15 +544,7 @@ export class BlockInputSync {
|
|
|
544
544
|
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
-
|
|
548
|
-
if (parentPayloadInput) {
|
|
549
|
-
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
550
|
-
return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
554
|
-
}
|
|
555
|
-
|
|
547
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
556
548
|
return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
|
|
557
549
|
}
|
|
558
550
|
|
|
@@ -894,9 +886,9 @@ export class BlockInputSync {
|
|
|
894
886
|
this.pendingBlocks.delete(blockRootHex);
|
|
895
887
|
|
|
896
888
|
if (isForkPostGloas(fork)) {
|
|
897
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.
|
|
889
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
898
890
|
if (!payloadInput) {
|
|
899
|
-
this.logger.
|
|
891
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
900
892
|
} else {
|
|
901
893
|
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
902
894
|
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
@@ -969,12 +961,12 @@ export class BlockInputSync {
|
|
|
969
961
|
*/
|
|
970
962
|
private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
|
|
971
963
|
const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
964
|
+
|
|
972
965
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
973
966
|
this.pendingPayloads.delete(rootHex);
|
|
974
967
|
return;
|
|
975
968
|
}
|
|
976
969
|
|
|
977
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
978
970
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
979
971
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
980
972
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -990,8 +982,9 @@ export class BlockInputSync {
|
|
|
990
982
|
return;
|
|
991
983
|
}
|
|
992
984
|
|
|
985
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
993
986
|
if (!payloadInput) {
|
|
994
|
-
this.logger.debug("
|
|
987
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
995
988
|
root: rootHex,
|
|
996
989
|
});
|
|
997
990
|
return;
|
|
@@ -1187,7 +1180,7 @@ export class BlockInputSync {
|
|
|
1187
1180
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
1188
1181
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
1189
1182
|
? cacheItem.payloadInput
|
|
1190
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1183
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1191
1184
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1192
1185
|
|
|
1193
1186
|
let i = 0;
|
|
@@ -1220,7 +1213,7 @@ export class BlockInputSync {
|
|
|
1220
1213
|
slot = envelope.message.payload.slotNumber;
|
|
1221
1214
|
}
|
|
1222
1215
|
|
|
1223
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1216
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1224
1217
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1225
1218
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
1226
1219
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|