@lodestar/beacon-node 1.45.0 → 1.46.0-dev.18a1510f34
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 +186 -57
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +1 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +2 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +2 -3
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +173 -89
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +6 -3
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +17 -3
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +2 -17
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +13 -5
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +0 -1
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +44 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +48 -6
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +25 -4
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +11 -4
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +14 -1
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +2 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +11 -6
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +28 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/lightClient/types.d.ts +3 -1
- package/lib/chain/lightClient/types.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +2 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -4
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts +6 -5
- package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts.map +1 -1
- package/lib/chain/seenCache/seenExecutionPayloadBids.js +16 -8
- package/lib/chain/seenCache/seenExecutionPayloadBids.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +10 -6
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +10 -6
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/blobSidecar.js +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +53 -4
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +100 -26
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +29 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +9 -2
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +65 -13
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
- package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
- package/lib/chain/validatorMonitor.js +1 -1
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -1
- package/lib/execution/engine/types.js +1 -1
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/nodeJsMetrics.js +1 -1
- package/lib/metrics/nodeJsMetrics.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.js +2 -7
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/discv5/index.d.ts.map +1 -1
- package/lib/network/discv5/index.js +1 -4
- package/lib/network/discv5/index.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts +2 -2
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +15 -9
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +2 -2
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +79 -236
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +24 -1
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +4 -1
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/libp2p/index.d.ts.map +1 -1
- package/lib/network/libp2p/index.js +2 -9
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.d.ts +4 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +19 -11
- package/lib/network/network.js.map +1 -1
- package/lib/network/peers/datastore.d.ts +1 -1
- package/lib/network/peers/datastore.d.ts.map +1 -1
- package/lib/network/peers/datastore.js +1 -1
- 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 +12 -6
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +2 -1
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +20 -11
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientFinalityUpdate.js +1 -1
- package/lib/network/reqresp/handlers/lightClientFinalityUpdate.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientOptimisticUpdate.js +1 -1
- package/lib/network/reqresp/handlers/lightClientOptimisticUpdate.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +6 -2
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
- package/lib/network/util.d.ts +1 -0
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +4 -0
- package/lib/network/util.js.map +1 -1
- package/lib/sync/constants.d.ts +6 -5
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +6 -5
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +10 -1
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +28 -10
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +30 -15
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/utils/batches.d.ts +18 -0
- package/lib/sync/range/utils/batches.d.ts.map +1 -1
- package/lib/sync/range/utils/batches.js +39 -0
- package/lib/sync/range/utils/batches.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +7 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +7 -2
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/execution.js +1 -1
- package/lib/util/execution.js.map +1 -1
- package/lib/util/profile.d.ts +3 -0
- package/lib/util/profile.d.ts.map +1 -1
- package/lib/util/profile.js +1 -27
- package/lib/util/profile.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/package.json +29 -38
- package/src/api/impl/beacon/blocks/index.ts +221 -63
- package/src/api/impl/config/constants.ts +2 -0
- package/src/api/impl/lodestar/index.ts +2 -3
- package/src/api/impl/validator/index.ts +207 -99
- package/src/api/rest/base.ts +8 -3
- package/src/chain/archiveStore/archiveStore.ts +20 -3
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +0 -1
- package/src/chain/blocks/importBlock.ts +0 -17
- package/src/chain/blocks/utils/chainSegment.ts +16 -5
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -3
- package/src/chain/bls/multithread/index.ts +0 -1
- package/src/chain/chain.ts +61 -2
- package/src/chain/errors/blockError.ts +44 -7
- package/src/chain/errors/executionPayloadBid.ts +12 -4
- package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
- package/src/chain/errors/proposerPreferences.ts +16 -1
- package/src/chain/lightClient/index.ts +12 -7
- package/src/chain/lightClient/proofs.ts +36 -1
- package/src/chain/lightClient/types.ts +3 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +4 -2
- package/src/chain/opPools/proposerPreferencesPool.ts +1 -1
- package/src/chain/prepareNextSlot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +5 -5
- package/src/chain/seenCache/seenExecutionPayloadBids.ts +22 -9
- package/src/chain/validation/aggregateAndProof.ts +12 -6
- package/src/chain/validation/attestation.ts +12 -6
- package/src/chain/validation/blobSidecar.ts +1 -1
- package/src/chain/validation/block.ts +69 -4
- package/src/chain/validation/executionPayloadBid.ts +119 -28
- package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
- package/src/chain/validation/proposerPreferences.ts +75 -13
- package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
- package/src/chain/validatorMonitor.ts +1 -1
- package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
- package/src/execution/engine/interface.ts +1 -1
- package/src/execution/engine/types.ts +1 -1
- package/src/metrics/nodeJsMetrics.ts +1 -1
- package/src/network/core/networkCoreWorkerHandler.ts +2 -6
- package/src/network/discv5/index.ts +1 -4
- package/src/network/gossip/encoding.ts +13 -7
- package/src/network/gossip/gossipsub.ts +7 -2
- package/src/network/gossip/topic.ts +30 -0
- package/src/network/interface.ts +3 -1
- package/src/network/libp2p/index.ts +2 -10
- package/src/network/network.ts +24 -15
- package/src/network/peers/datastore.ts +1 -1
- package/src/network/processor/gossipHandlers.ts +12 -6
- package/src/network/processor/index.ts +2 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
- package/src/network/reqresp/handlers/index.ts +21 -12
- package/src/network/reqresp/handlers/lightClientFinalityUpdate.ts +1 -1
- package/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts +1 -1
- package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -2
- package/src/network/util.ts +5 -0
- package/src/sync/constants.ts +6 -5
- package/src/sync/range/batch.ts +31 -10
- package/src/sync/range/chain.ts +36 -16
- package/src/sync/range/utils/batches.ts +44 -0
- package/src/sync/sync.ts +10 -0
- package/src/sync/unknownBlock.ts +8 -2
- package/src/util/execution.ts +1 -1
- package/src/util/profile.ts +1 -31
- package/src/util/sszBytes.ts +1 -1
- package/lib/bun-wrappers/prometheus-gc-stats.d.ts +0 -2
- package/lib/bun-wrappers/prometheus-gc-stats.d.ts.map +0 -1
- package/lib/bun-wrappers/prometheus-gc-stats.js +0 -8
- package/lib/bun-wrappers/prometheus-gc-stats.js.map +0 -1
- package/lib/network/peers/datastore_bun.d.ts +0 -3
- package/lib/network/peers/datastore_bun.d.ts.map +0 -1
- package/lib/network/peers/datastore_bun.js +0 -3
- package/lib/network/peers/datastore_bun.js.map +0 -1
- package/src/bun-wrappers/prometheus-gc-stats.ts +0 -7
- package/src/network/peers/datastore_bun.ts +0 -3
|
@@ -98,7 +98,7 @@ export type PayloadAttributes = {
|
|
|
98
98
|
withdrawals?: capella.Withdrawal[];
|
|
99
99
|
parentBeaconBlockRoot?: Uint8Array;
|
|
100
100
|
slotNumber?: number; // EIP-7843
|
|
101
|
-
targetGasLimit?:
|
|
101
|
+
targetGasLimit?: bigint; // GLOAS (PayloadAttributesV4, execution-apis#796)
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
export type VersionedHashes = Uint8Array[];
|
|
@@ -458,7 +458,7 @@ export function deserializePayloadAttributes(data: PayloadAttributesRpc): Payloa
|
|
|
458
458
|
withdrawals: data.withdrawals?.map((withdrawal) => deserializeWithdrawal(withdrawal)),
|
|
459
459
|
parentBeaconBlockRoot: data.parentBeaconBlockRoot ? dataToBytes(data.parentBeaconBlockRoot, 32) : undefined,
|
|
460
460
|
slotNumber: data.slotNumber !== undefined ? quantityToNum(data.slotNumber) : undefined,
|
|
461
|
-
targetGasLimit: data.targetGasLimit !== undefined ?
|
|
461
|
+
targetGasLimit: data.targetGasLimit !== undefined ? quantityToBigint(data.targetGasLimit) : undefined,
|
|
462
462
|
};
|
|
463
463
|
}
|
|
464
464
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {Registry, collectDefaultMetrics} from "prom-client";
|
|
2
|
-
import {gcStats} from "
|
|
2
|
+
import {gcStats} from "@chainsafe/prometheus-gc-stats";
|
|
3
3
|
|
|
4
4
|
export function collectNodeJSMetrics(register: Registry, prefix?: string): () => void {
|
|
5
5
|
collectDefaultMetrics({
|
|
@@ -132,10 +132,6 @@ export class WorkerNetworkCore implements INetworkCore {
|
|
|
132
132
|
|
|
133
133
|
const workerOpts: ConstructorParameters<typeof Worker>[1] = {
|
|
134
134
|
workerData,
|
|
135
|
-
};
|
|
136
|
-
if (globalThis.Bun) {
|
|
137
|
-
workerOpts.suppressTranspileTS = true;
|
|
138
|
-
} else {
|
|
139
135
|
/**
|
|
140
136
|
* maxYoungGenerationSizeMb defaults to 152mb through the cli option defaults.
|
|
141
137
|
* That default value was determined via https://github.com/ChainSafe/lodestar/issues/2115 and
|
|
@@ -147,8 +143,8 @@ export class WorkerNetworkCore implements INetworkCore {
|
|
|
147
143
|
* showed that there is a pretty big window of "correct" values but we can always tune as
|
|
148
144
|
* necessary
|
|
149
145
|
*/
|
|
150
|
-
|
|
151
|
-
}
|
|
146
|
+
resourceLimits: {maxYoungGenerationSizeMb: opts.maxYoungGenerationSizeMb},
|
|
147
|
+
};
|
|
152
148
|
|
|
153
149
|
const worker = new Worker(path.join(workerDir, "networkCoreWorker.js"), workerOpts);
|
|
154
150
|
|
|
@@ -51,10 +51,7 @@ export class Discv5Worker extends (EventEmitter as {new (): StrictEventEmitter<E
|
|
|
51
51
|
loggerOpts: opts.logger.toOpts(),
|
|
52
52
|
genesisTime: opts.genesisTime,
|
|
53
53
|
};
|
|
54
|
-
const worker = new Worker("./worker.js", {
|
|
55
|
-
suppressTranspileTS: Boolean(globalThis.Bun),
|
|
56
|
-
workerData,
|
|
57
|
-
} as ConstructorParameters<typeof Worker>[1]);
|
|
54
|
+
const worker = new Worker("./worker.js", {workerData} as ConstructorParameters<typeof Worker>[1]);
|
|
58
55
|
|
|
59
56
|
const workerApi = await spawn<Discv5WorkerApi>(worker, {
|
|
60
57
|
// A Lodestar Node may do very expensive task at start blocking the event loop and causing
|
|
@@ -9,7 +9,7 @@ import {ForkName} from "@lodestar/params";
|
|
|
9
9
|
import {intToBytes} from "@lodestar/utils";
|
|
10
10
|
import {MESSAGE_DOMAIN_VALID_SNAPPY} from "./constants.js";
|
|
11
11
|
import {Eth2GossipsubMetrics} from "./metrics.js";
|
|
12
|
-
import {GossipTopicCache, getGossipSSZType} from "./topic.js";
|
|
12
|
+
import {GossipTopicCache, getGossipSSZMaxSize, getGossipSSZType} from "./topic.js";
|
|
13
13
|
|
|
14
14
|
// Load WASM
|
|
15
15
|
const xxhash = await xxhashFactory();
|
|
@@ -98,7 +98,7 @@ export function msgIdFn(gossipTopicCache: GossipTopicCache, msg: Message): Uint8
|
|
|
98
98
|
export class DataTransformSnappy implements DataTransform {
|
|
99
99
|
constructor(
|
|
100
100
|
private readonly gossipTopicCache: GossipTopicCache,
|
|
101
|
-
private readonly
|
|
101
|
+
private readonly maxPayloadSize: number,
|
|
102
102
|
private readonly metrics: Eth2GossipsubMetrics | null
|
|
103
103
|
) {}
|
|
104
104
|
|
|
@@ -111,17 +111,18 @@ export class DataTransformSnappy implements DataTransform {
|
|
|
111
111
|
inboundTransform(topicStr: string, data: Uint8Array): Uint8Array {
|
|
112
112
|
// check uncompressed data length before we actually decompress
|
|
113
113
|
const uncompressedDataLength = snappyWasm.decompress_len(data);
|
|
114
|
-
if (uncompressedDataLength > this.maxSizePerMessage) {
|
|
115
|
-
throw Error(`ssz_snappy decoded data length ${uncompressedDataLength} > ${this.maxSizePerMessage}`);
|
|
116
|
-
}
|
|
117
114
|
|
|
118
115
|
const topic = this.gossipTopicCache.getTopic(topicStr);
|
|
119
116
|
const sszType = getGossipSSZType(topic);
|
|
117
|
+
const maxSize = getGossipSSZMaxSize(topic, this.maxPayloadSize, sszType);
|
|
120
118
|
this.metrics?.dataTransform.inbound.inc({type: topic.type});
|
|
121
119
|
|
|
122
120
|
if (uncompressedDataLength < sszType.minSize) {
|
|
123
121
|
throw Error(`ssz_snappy decoded data length ${uncompressedDataLength} < ${sszType.minSize}`);
|
|
124
122
|
}
|
|
123
|
+
if (uncompressedDataLength > maxSize) {
|
|
124
|
+
throw Error(`ssz_snappy decoded data length ${uncompressedDataLength} > ${maxSize}`);
|
|
125
|
+
}
|
|
125
126
|
if (uncompressedDataLength > sszType.maxSize) {
|
|
126
127
|
throw Error(`ssz_snappy decoded data length ${uncompressedDataLength} > ${sszType.maxSize}`);
|
|
127
128
|
}
|
|
@@ -139,9 +140,14 @@ export class DataTransformSnappy implements DataTransform {
|
|
|
139
140
|
*/
|
|
140
141
|
outboundTransform(topicStr: string, data: Uint8Array): Uint8Array {
|
|
141
142
|
const topic = this.gossipTopicCache.getTopic(topicStr);
|
|
143
|
+
const sszType = getGossipSSZType(topic);
|
|
144
|
+
const maxSize = getGossipSSZMaxSize(topic, this.maxPayloadSize, sszType);
|
|
142
145
|
this.metrics?.dataTransform.outbound.inc({type: topic.type});
|
|
143
|
-
if (data.length >
|
|
144
|
-
throw Error(`ssz_snappy encoded data length ${data.length} > ${
|
|
146
|
+
if (data.length > maxSize) {
|
|
147
|
+
throw Error(`ssz_snappy encoded data length ${data.length} > ${maxSize}`);
|
|
148
|
+
}
|
|
149
|
+
if (data.length > sszType.maxSize) {
|
|
150
|
+
throw Error(`ssz_snappy encoded data length ${data.length} > ${sszType.maxSize}`);
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
// Using Buffer.alloc() instead of Buffer.allocUnsafe() to mitigate high GC pressure observed in some environments
|
|
@@ -15,7 +15,12 @@ import {type Multiaddr, multiaddr} from "@multiformats/multiaddr";
|
|
|
15
15
|
import {ENR} from "@chainsafe/enr";
|
|
16
16
|
import {routes} from "@lodestar/api";
|
|
17
17
|
import {BeaconConfig, ForkBoundary} from "@lodestar/config";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
ATTESTATION_SUBNET_COUNT,
|
|
20
|
+
MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE,
|
|
21
|
+
SLOTS_PER_EPOCH,
|
|
22
|
+
SYNC_COMMITTEE_SUBNET_COUNT,
|
|
23
|
+
} from "@lodestar/params";
|
|
19
24
|
import {SubnetID} from "@lodestar/types";
|
|
20
25
|
import {Logger, Map2d, Map2dArr} from "@lodestar/utils";
|
|
21
26
|
import {RegistryMetricCreator} from "../../metrics/index.js";
|
|
@@ -179,7 +184,7 @@ export class Eth2Gossipsub {
|
|
|
179
184
|
// Only send IDONTWANT messages if the message size is larger than this
|
|
180
185
|
// This should be large enough to not send IDONTWANT for "small" messages
|
|
181
186
|
// See https://github.com/ChainSafe/lodestar/pull/7077#issuecomment-2383679472
|
|
182
|
-
idontwantMinDataSize:
|
|
187
|
+
idontwantMinDataSize: MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE,
|
|
183
188
|
})(modules.libp2p.services.components) as GossipSubInternal;
|
|
184
189
|
|
|
185
190
|
if (metrics) {
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import {type CompositeTypeAny} from "@chainsafe/ssz";
|
|
1
2
|
import {ForkDigestContext} from "@lodestar/config";
|
|
2
3
|
import {
|
|
3
4
|
ATTESTATION_SUBNET_COUNT,
|
|
4
5
|
ForkName,
|
|
5
6
|
ForkSeq,
|
|
7
|
+
MAX_ATTESTER_SLASHING_SIZE,
|
|
8
|
+
MAX_DATA_COLUMN_SIDECAR_SIZE,
|
|
9
|
+
MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE,
|
|
10
|
+
MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE,
|
|
6
11
|
SYNC_COMMITTEE_SUBNET_COUNT,
|
|
7
12
|
isForkPostAltair,
|
|
8
13
|
isForkPostElectra,
|
|
9
14
|
isForkPostFulu,
|
|
15
|
+
isForkPostGloas,
|
|
10
16
|
} from "@lodestar/params";
|
|
11
17
|
import {Attestation, SingleAttestation, ssz, sszTypesFor} from "@lodestar/types";
|
|
12
18
|
import {GossipAction, GossipActionError, GossipErrorCode} from "../../chain/errors/gossipValidation.js";
|
|
@@ -130,6 +136,30 @@ export function getGossipSSZType(topic: GossipTopic) {
|
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Return the maximum uncompressed SSZ byte length accepted for a gossip object.
|
|
141
|
+
*/
|
|
142
|
+
export function getGossipSSZMaxSize(topic: GossipTopic, maxPayloadSize: number, sszType?: CompositeTypeAny): number {
|
|
143
|
+
const {fork} = topic.boundary;
|
|
144
|
+
// Gloas progressive containers have broad theoretical SSZ max sizes; use the preset p2p bounds instead.
|
|
145
|
+
switch (topic.type) {
|
|
146
|
+
case GossipType.beacon_block:
|
|
147
|
+
return maxPayloadSize;
|
|
148
|
+
case GossipType.beacon_aggregate_and_proof:
|
|
149
|
+
return isForkPostGloas(fork) ? MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE : (sszType ?? getGossipSSZType(topic)).maxSize;
|
|
150
|
+
case GossipType.attester_slashing:
|
|
151
|
+
return isForkPostGloas(fork) ? MAX_ATTESTER_SLASHING_SIZE : (sszType ?? getGossipSSZType(topic)).maxSize;
|
|
152
|
+
case GossipType.data_column_sidecar:
|
|
153
|
+
return isForkPostGloas(fork) ? MAX_DATA_COLUMN_SIDECAR_SIZE : (sszType ?? getGossipSSZType(topic)).maxSize;
|
|
154
|
+
case GossipType.execution_payload:
|
|
155
|
+
return maxPayloadSize;
|
|
156
|
+
case GossipType.execution_payload_bid:
|
|
157
|
+
return MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE;
|
|
158
|
+
default:
|
|
159
|
+
return (sszType ?? getGossipSSZType(topic)).maxSize;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
133
163
|
/**
|
|
134
164
|
* Deserialize a gossip serialized data into an ssz object.
|
|
135
165
|
*/
|
package/src/network/interface.ts
CHANGED
|
@@ -103,7 +103,9 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
103
103
|
publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number>;
|
|
104
104
|
publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number>;
|
|
105
105
|
publishBeaconAttestation(attestation: SingleAttestation, subnet: SubnetID): Promise<number>;
|
|
106
|
-
publishDataColumnSidecar(
|
|
106
|
+
publishDataColumnSidecar(
|
|
107
|
+
dataColumnSideCar: DataColumnSidecar
|
|
108
|
+
): Promise<{sentPeers: number; alreadyPublished: boolean}>;
|
|
107
109
|
publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise<number>;
|
|
108
110
|
publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number>;
|
|
109
111
|
publishProposerSlashing(proposerSlashing: phase0.ProposerSlashing): Promise<number>;
|
|
@@ -9,7 +9,7 @@ import {Libp2pInit, createLibp2p} from "libp2p";
|
|
|
9
9
|
import {Registry} from "prom-client";
|
|
10
10
|
import {ENR} from "@chainsafe/enr";
|
|
11
11
|
import {noise} from "@chainsafe/libp2p-noise";
|
|
12
|
-
import {
|
|
12
|
+
import {defaultCrypto} from "@chainsafe/libp2p-noise/crypto";
|
|
13
13
|
import {quic} from "@chainsafe/libp2p-quic";
|
|
14
14
|
import {Libp2p, LodestarComponents} from "../interface.js";
|
|
15
15
|
import {NetworkOptions, defaultNetworkOptions} from "../options.js";
|
|
@@ -111,14 +111,6 @@ export async function createNodeJsLibp2p(
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
const noiseCrypto = {
|
|
115
|
-
...defaultCrypto,
|
|
116
|
-
};
|
|
117
|
-
if (globalThis.Bun) {
|
|
118
|
-
noiseCrypto.chaCha20Poly1305Decrypt = asCrypto.chaCha20Poly1305Decrypt;
|
|
119
|
-
noiseCrypto.chaCha20Poly1305Encrypt = asCrypto.chaCha20Poly1305Encrypt;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
114
|
return createLibp2p({
|
|
123
115
|
privateKey,
|
|
124
116
|
nodeInfo: {
|
|
@@ -130,7 +122,7 @@ export async function createNodeJsLibp2p(
|
|
|
130
122
|
listen: localMultiaddrs,
|
|
131
123
|
announce: [],
|
|
132
124
|
},
|
|
133
|
-
connectionEncrypters: [noise({crypto:
|
|
125
|
+
connectionEncrypters: [noise({crypto: defaultCrypto})],
|
|
134
126
|
transports,
|
|
135
127
|
streamMuxers: [mplex({disconnectThreshold})],
|
|
136
128
|
peerDiscovery,
|
package/src/network/network.ts
CHANGED
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
} from "./reqresp/utils/collect.js";
|
|
66
66
|
import {collectSequentialBlocksInRange} from "./reqresp/utils/collectSequentialBlocksInRange.js";
|
|
67
67
|
import {CommitteeSubscription} from "./subnets/index.js";
|
|
68
|
-
import {isPublishToZeroPeersError, prettyPrintPeerIdStr} from "./util.js";
|
|
68
|
+
import {isPublishDuplicateError, isPublishToZeroPeersError, prettyPrintPeerIdStr} from "./util.js";
|
|
69
69
|
|
|
70
70
|
type NetworkModules = {
|
|
71
71
|
opts: NetworkOptions;
|
|
@@ -366,7 +366,9 @@ export class Network implements INetwork {
|
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
async publishDataColumnSidecar(
|
|
369
|
+
async publishDataColumnSidecar(
|
|
370
|
+
dataColumnSidecar: DataColumnSidecar
|
|
371
|
+
): Promise<{sentPeers: number; alreadyPublished: boolean}> {
|
|
370
372
|
const slot = isGloasDataColumnSidecar(dataColumnSidecar)
|
|
371
373
|
? dataColumnSidecar.slot
|
|
372
374
|
: dataColumnSidecar.signedBlockHeader.message.slot;
|
|
@@ -374,18 +376,25 @@ export class Network implements INetwork {
|
|
|
374
376
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
375
377
|
|
|
376
378
|
const subnet = computeSubnetForDataColumnSidecar(this.config, dataColumnSidecar);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
379
|
+
try {
|
|
380
|
+
const sentPeers = await this.publishGossip<GossipType.data_column_sidecar>(
|
|
381
|
+
{type: GossipType.data_column_sidecar, boundary, subnet},
|
|
382
|
+
dataColumnSidecar,
|
|
383
|
+
{
|
|
384
|
+
// we ensure having all topic peers via prioritizePeers() function
|
|
385
|
+
// in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
|
|
386
|
+
// because supernode will rebuild and publish missing data column sidecars for us
|
|
387
|
+
// hence we want to track sent peers as 0 instead of an error
|
|
388
|
+
allowPublishToZeroTopicPeers: true,
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
return {sentPeers, alreadyPublished: false};
|
|
392
|
+
} catch (e) {
|
|
393
|
+
if (isPublishDuplicateError(e as Error)) {
|
|
394
|
+
return {sentPeers: 0, alreadyPublished: true};
|
|
387
395
|
}
|
|
388
|
-
|
|
396
|
+
throw e;
|
|
397
|
+
}
|
|
389
398
|
}
|
|
390
399
|
|
|
391
400
|
async publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number> {
|
|
@@ -860,8 +869,8 @@ export class Network implements INetwork {
|
|
|
860
869
|
this.core.setTargetGroupCount(count);
|
|
861
870
|
};
|
|
862
871
|
|
|
863
|
-
private onPublishDataColumns = (sidecars: DataColumnSidecar[]): Promise<
|
|
864
|
-
|
|
872
|
+
private onPublishDataColumns = async (sidecars: DataColumnSidecar[]): Promise<void> => {
|
|
873
|
+
await promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishDataColumnSidecar(sidecar)));
|
|
865
874
|
};
|
|
866
875
|
|
|
867
876
|
private onPublishBlobSidecars = (sidecars: deneb.BlobSidecar[]): Promise<number[]> => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {AbortOptions} from "@libp2p/interface";
|
|
2
2
|
import {BaseDatastore} from "datastore-core";
|
|
3
|
+
import {LevelDatastore} from "datastore-level";
|
|
3
4
|
import {Key, KeyQuery, Pair, Query} from "interface-datastore";
|
|
4
|
-
import {LevelDatastore} from "#datastore-wrapper";
|
|
5
5
|
|
|
6
6
|
type MemoryItem = {
|
|
7
7
|
lastAccessedMs: number;
|
|
@@ -176,7 +176,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
176
176
|
|
|
177
177
|
// optimistically add gossip block to the seen cache
|
|
178
178
|
// if validation fails, we will NOT forward this gossip block to peers
|
|
179
|
-
// - if
|
|
179
|
+
// - if PARENT_BLOCK_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
|
|
180
180
|
// - if other validator errors, blockInput will stay in the seen cache and will be pruned on finalization
|
|
181
181
|
const blockInput = chain.seenBlockInputCache.getByBlock({
|
|
182
182
|
block: signedBlock,
|
|
@@ -187,7 +187,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
// Optimistically seed the payload-envelope cache too, mirroring seenBlockInputCache above.
|
|
190
|
-
// This ensures we have PayloadEnvelopeInput, even through "
|
|
190
|
+
// This ensures we have PayloadEnvelopeInput, even through "PARENT_BLOCK_UNKNOWN" error
|
|
191
191
|
// see https://github.com/ChainSafe/lodestar/issues/9475
|
|
192
192
|
if (isForkPostGloas(fork)) {
|
|
193
193
|
chain.seenPayloadEnvelopeInputCache.add({
|
|
@@ -227,7 +227,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
227
227
|
if (e instanceof BlockGossipError) {
|
|
228
228
|
logger.debug("Gossip block has error", {slot, root: blockShortHex, code: e.type.code});
|
|
229
229
|
if (
|
|
230
|
-
(e.type.code === BlockErrorCode.
|
|
230
|
+
(e.type.code === BlockErrorCode.PARENT_BLOCK_UNKNOWN ||
|
|
231
|
+
e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN) &&
|
|
231
232
|
blockInput
|
|
232
233
|
) {
|
|
233
234
|
chain.emitter.emit(ChainEvent.blockUnknownParent, {
|
|
@@ -661,14 +662,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
661
662
|
break;
|
|
662
663
|
}
|
|
663
664
|
// ALREADY_KNOWN should not happen with ignoreIfKnown=true above
|
|
664
|
-
//
|
|
665
|
+
// PARENT_BLOCK_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
|
|
665
666
|
case BlockErrorCode.ALREADY_KNOWN:
|
|
666
|
-
case BlockErrorCode.
|
|
667
|
+
case BlockErrorCode.PARENT_BLOCK_UNKNOWN:
|
|
667
668
|
case BlockErrorCode.PRESTATE_MISSING:
|
|
668
669
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
669
|
-
// Errors might indicate an issue with our node or the connected EL client
|
|
670
|
+
// Errors might indicate an issue with our node or the connected EL client.
|
|
670
671
|
logLevel = LogLevel.error;
|
|
671
672
|
break;
|
|
673
|
+
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
674
|
+
// the peer served a bad block
|
|
675
|
+
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "ExecutionEngineInvalid");
|
|
676
|
+
logLevel = LogLevel.warn;
|
|
677
|
+
break;
|
|
672
678
|
default:
|
|
673
679
|
// TODO: Should it use PeerId or string?
|
|
674
680
|
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipBlock");
|
|
@@ -461,7 +461,8 @@ export class NetworkProcessor {
|
|
|
461
461
|
break;
|
|
462
462
|
}
|
|
463
463
|
case GossipType.execution_payload_bid: {
|
|
464
|
-
//
|
|
464
|
+
// Search for the parent independently of current head compatibility. A bid may arrive
|
|
465
|
+
// before its parent block, so compatibility is checked later during gossip validation.
|
|
465
466
|
const parentBlockRoot = getParentBlockRootFromSignedExecutionPayloadBidSerialized(message.msg.data);
|
|
466
467
|
const parentBlockHash = getParentBlockHashFromSignedExecutionPayloadBidSerialized(message.msg.data);
|
|
467
468
|
if (
|
|
@@ -107,8 +107,10 @@ export function validateExecutionPayloadEnvelopesByRangeRequest(
|
|
|
107
107
|
// The gloas req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
|
|
108
108
|
// Archival nodes may still serve older retained payloads to allow genesis sync.
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
// Spec: EnvelopesByRange response is bounded by MAX_REQUEST_PAYLOADS (consensus-specs #5383),
|
|
111
|
+
// distinct from the MAX_REQUEST_BLOCKS_DENEB cap used for block-by-range.
|
|
112
|
+
if (count > config.MAX_REQUEST_PAYLOADS) {
|
|
113
|
+
count = config.MAX_REQUEST_PAYLOADS;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
return {startSlot, count};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {Type} from "@chainsafe/ssz";
|
|
2
|
+
import {ProtocolHandler, RespStatus, ResponseError} from "@lodestar/reqresp";
|
|
2
3
|
import {ssz} from "@lodestar/types";
|
|
3
4
|
import {IBeaconChain} from "../../../chain/index.js";
|
|
4
5
|
import {IBeaconDb} from "../../../db/index.js";
|
|
@@ -29,6 +30,14 @@ function notImplemented(method: ReqRespMethod): ProtocolHandler {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function deserializeRequestBody<T>(type: Type<T>, data: Uint8Array): T {
|
|
34
|
+
try {
|
|
35
|
+
return type.deserialize(data);
|
|
36
|
+
} catch (e) {
|
|
37
|
+
throw new ResponseError(RespStatus.INVALID_REQUEST, e instanceof Error ? e.message : String(e));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
/**
|
|
33
42
|
* The ReqRespHandler module handles app-level requests / responses from other peers,
|
|
34
43
|
* fetching state from the chain and database as needed.
|
|
@@ -40,51 +49,51 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh
|
|
|
40
49
|
[ReqRespMethod.Ping]: notImplemented(ReqRespMethod.Ping),
|
|
41
50
|
[ReqRespMethod.Metadata]: notImplemented(ReqRespMethod.Metadata),
|
|
42
51
|
[ReqRespMethod.BeaconBlocksByRange]: (req, peerId, peerClient) => {
|
|
43
|
-
const body = ssz.phase0.BeaconBlocksByRangeRequest
|
|
52
|
+
const body = deserializeRequestBody(ssz.phase0.BeaconBlocksByRangeRequest, req.data);
|
|
44
53
|
return onBeaconBlocksByRange(body, chain, db, peerId, peerClient);
|
|
45
54
|
},
|
|
46
55
|
[ReqRespMethod.BeaconBlocksByRoot]: (req) => {
|
|
47
56
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
48
|
-
const body = BeaconBlocksByRootRequestType(fork, chain.config)
|
|
57
|
+
const body = deserializeRequestBody(BeaconBlocksByRootRequestType(fork, chain.config), req.data);
|
|
49
58
|
return onBeaconBlocksByRoot(body, chain);
|
|
50
59
|
},
|
|
51
60
|
[ReqRespMethod.BeaconBlocksByHead]: (req, peerId, peerClient) => {
|
|
52
|
-
const body = ssz.fulu.BeaconBlocksByHeadRequest
|
|
61
|
+
const body = deserializeRequestBody(ssz.fulu.BeaconBlocksByHeadRequest, req.data);
|
|
53
62
|
return onBeaconBlocksByHead(body, chain, peerId, peerClient);
|
|
54
63
|
},
|
|
55
64
|
[ReqRespMethod.BlobSidecarsByRoot]: (req) => {
|
|
56
65
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
57
|
-
const body = BlobSidecarsByRootRequestType(fork, chain.config)
|
|
66
|
+
const body = deserializeRequestBody(BlobSidecarsByRootRequestType(fork, chain.config), req.data);
|
|
58
67
|
return onBlobSidecarsByRoot(body, chain);
|
|
59
68
|
},
|
|
60
69
|
[ReqRespMethod.BlobSidecarsByRange]: (req) => {
|
|
61
|
-
const body = ssz.deneb.BlobSidecarsByRangeRequest
|
|
70
|
+
const body = deserializeRequestBody(ssz.deneb.BlobSidecarsByRangeRequest, req.data);
|
|
62
71
|
return onBlobSidecarsByRange(body, chain, db);
|
|
63
72
|
},
|
|
64
73
|
[ReqRespMethod.DataColumnSidecarsByRange]: (req, peerId, peerClient) => {
|
|
65
|
-
const body = ssz.fulu.DataColumnSidecarsByRangeRequest
|
|
74
|
+
const body = deserializeRequestBody(ssz.fulu.DataColumnSidecarsByRangeRequest, req.data);
|
|
66
75
|
return onDataColumnSidecarsByRange(body, chain, db, peerId, peerClient);
|
|
67
76
|
},
|
|
68
77
|
[ReqRespMethod.DataColumnSidecarsByRoot]: (req, peerId, peerClient) => {
|
|
69
|
-
const body = DataColumnSidecarsByRootRequestType(chain.config)
|
|
78
|
+
const body = deserializeRequestBody(DataColumnSidecarsByRootRequestType(chain.config), req.data);
|
|
70
79
|
return onDataColumnSidecarsByRoot(body, chain, db, peerId, peerClient);
|
|
71
80
|
},
|
|
72
81
|
|
|
73
82
|
[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: (req, peerId, peerClient) => {
|
|
74
|
-
const body = ExecutionPayloadEnvelopesByRootRequestType(chain.config)
|
|
83
|
+
const body = deserializeRequestBody(ExecutionPayloadEnvelopesByRootRequestType(chain.config), req.data);
|
|
75
84
|
return onExecutionPayloadEnvelopesByRoot(body, chain, db, peerId, peerClient);
|
|
76
85
|
},
|
|
77
86
|
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req, peerId, peerClient) => {
|
|
78
|
-
const body = ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest
|
|
87
|
+
const body = deserializeRequestBody(ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest, req.data);
|
|
79
88
|
return onExecutionPayloadEnvelopesByRange(body, chain, db, peerId, peerClient);
|
|
80
89
|
},
|
|
81
90
|
|
|
82
91
|
[ReqRespMethod.LightClientBootstrap]: (req) => {
|
|
83
|
-
const body = ssz.Root
|
|
92
|
+
const body = deserializeRequestBody(ssz.Root, req.data);
|
|
84
93
|
return onLightClientBootstrap(body, chain);
|
|
85
94
|
},
|
|
86
95
|
[ReqRespMethod.LightClientUpdatesByRange]: (req) => {
|
|
87
|
-
const body = ssz.altair.LightClientUpdatesByRange
|
|
96
|
+
const body = deserializeRequestBody(ssz.altair.LightClientUpdatesByRange, req.data);
|
|
88
97
|
return onLightClientUpdatesByRange(body, chain);
|
|
89
98
|
},
|
|
90
99
|
[ReqRespMethod.LightClientFinalityUpdate]: () => onLightClientFinalityUpdate(chain),
|
|
@@ -12,7 +12,7 @@ export async function* onLightClientFinalityUpdate(chain: IBeaconChain): AsyncIt
|
|
|
12
12
|
throw new ResponseError(RespStatus.RESOURCE_UNAVAILABLE, "No latest finality update available");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const boundary = chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(update.
|
|
15
|
+
const boundary = chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(update.attestedHeader.beacon.slot));
|
|
16
16
|
const type = responseSszTypeByMethod[ReqRespMethod.LightClientFinalityUpdate](boundary.fork, 0);
|
|
17
17
|
yield {
|
|
18
18
|
data: type.serialize(update),
|
|
@@ -12,7 +12,7 @@ export async function* onLightClientOptimisticUpdate(chain: IBeaconChain): Async
|
|
|
12
12
|
throw new ResponseError(RespStatus.RESOURCE_UNAVAILABLE, "No latest optimistic update available");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const boundary = chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(update.
|
|
15
|
+
const boundary = chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(update.attestedHeader.beacon.slot));
|
|
16
16
|
const type = responseSszTypeByMethod[ReqRespMethod.LightClientOptimisticUpdate](boundary.fork, 0);
|
|
17
17
|
yield {
|
|
18
18
|
data: type.serialize(update),
|
|
@@ -19,11 +19,15 @@ export async function* onLightClientUpdatesByRange(
|
|
|
19
19
|
assertLightClientServer(chain.lightClientServer);
|
|
20
20
|
|
|
21
21
|
const count = Math.min(MAX_REQUEST_LIGHT_CLIENT_UPDATES, requestBody.count);
|
|
22
|
+
if (!Number.isSafeInteger(requestBody.startPeriod)) {
|
|
23
|
+
throw new ResponseError(RespStatus.INVALID_REQUEST, "startPeriod out of range");
|
|
24
|
+
}
|
|
22
25
|
let started = false;
|
|
23
|
-
for (let
|
|
26
|
+
for (let i = 0; i < count; i++) {
|
|
27
|
+
const period = requestBody.startPeriod + i;
|
|
24
28
|
try {
|
|
25
29
|
const update = await chain.lightClientServer.getUpdate(period);
|
|
26
|
-
const boundary = chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(update.
|
|
30
|
+
const boundary = chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(update.attestedHeader.beacon.slot));
|
|
27
31
|
const type = responseSszTypeByMethod[ReqRespMethod.LightClientUpdatesByRange](boundary.fork, 0);
|
|
28
32
|
|
|
29
33
|
yield {
|
package/src/network/util.ts
CHANGED
|
@@ -27,3 +27,8 @@ export function getConnection(libp2p: Libp2p, peerIdStr: string): Connection | u
|
|
|
27
27
|
export function isPublishToZeroPeersError(e: Error): boolean {
|
|
28
28
|
return e.message.includes("PublishError.NoPeersSubscribedToTopic");
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
// https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
|
|
32
|
+
export function isPublishDuplicateError(e: Error): boolean {
|
|
33
|
+
return e.message.includes("PublishError.Duplicate");
|
|
34
|
+
}
|
package/src/sync/constants.ts
CHANGED
|
@@ -15,12 +15,13 @@ export const MAX_BATCH_DOWNLOAD_ATTEMPTS = 5;
|
|
|
15
15
|
export const RATE_LIMITED_PEER_BACKOFF_MS = 5_000;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Consider batch faulty
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* Consider a batch faulty once its failed processing attempts EXCEED this number, i.e. the batch is
|
|
19
|
+
* processed up to `MAX_BATCH_PROCESSING_ATTEMPTS + 1` times before teardown (the check is `>`, same
|
|
20
|
+
* convention as `MAX_BATCH_DOWNLOAD_ATTEMPTS`).
|
|
21
|
+
* Some processing failures can be caused by a different batch, so range sync classifies the fault
|
|
22
|
+
* before deciding which batch to redownload.
|
|
22
23
|
**/
|
|
23
|
-
export const MAX_BATCH_PROCESSING_ATTEMPTS =
|
|
24
|
+
export const MAX_BATCH_PROCESSING_ATTEMPTS = 3;
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Number of slots to offset batches.
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -64,6 +64,7 @@ export type DownloadSuccessState = {
|
|
|
64
64
|
status: BatchStatus.AwaitingProcessing;
|
|
65
65
|
blocks: IBlockInput[];
|
|
66
66
|
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
|
|
67
|
+
attempt: Attempt;
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
export type BatchState =
|
|
@@ -589,7 +590,11 @@ export class Batch {
|
|
|
589
590
|
}
|
|
590
591
|
|
|
591
592
|
if (allComplete) {
|
|
592
|
-
|
|
593
|
+
const attempt: Attempt = {
|
|
594
|
+
peers: this.getSuccessfulPeers(),
|
|
595
|
+
hash: hashBlocks(blocks, this.config),
|
|
596
|
+
};
|
|
597
|
+
this.state = {status: BatchStatus.AwaitingProcessing, blocks, payloadEnvelopes: newPayloadEnvelopes, attempt};
|
|
593
598
|
} else {
|
|
594
599
|
this.state = {status: BatchStatus.AwaitingDownload, blocks, payloadEnvelopes: newPayloadEnvelopes};
|
|
595
600
|
this.requests = this.getRequests(blocks);
|
|
@@ -646,15 +651,11 @@ export class Batch {
|
|
|
646
651
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingProcessing));
|
|
647
652
|
}
|
|
648
653
|
|
|
649
|
-
const blocks = this.state
|
|
650
|
-
|
|
651
|
-
const hash = hashBlocks(blocks, this.config); // tracks blocks to report peer on processing error
|
|
652
|
-
// Reset successfulDownloads in case another download attempt needs to be made. When Attempt is successful or not
|
|
653
|
-
// the peers that the data came from will be handled by the Attempt that goes for processing.
|
|
654
|
-
const peers = this.getSuccessfulPeers();
|
|
654
|
+
const {blocks, payloadEnvelopes, attempt} = this.state;
|
|
655
|
+
// No need to track successfulDownloads anymore, the batch goes to Processing status.
|
|
655
656
|
this.successfulDownloads.clear();
|
|
656
|
-
this.state = {status: BatchStatus.Processing, blocks, payloadEnvelopes, attempt
|
|
657
|
-
return {blocks, payloadEnvelopes, peers};
|
|
657
|
+
this.state = {status: BatchStatus.Processing, blocks, payloadEnvelopes, attempt};
|
|
658
|
+
return {blocks, payloadEnvelopes, peers: attempt.peers};
|
|
658
659
|
}
|
|
659
660
|
|
|
660
661
|
/**
|
|
@@ -673,6 +674,26 @@ export class Batch {
|
|
|
673
674
|
};
|
|
674
675
|
}
|
|
675
676
|
|
|
677
|
+
/**
|
|
678
|
+
* Processing -> AwaitingProcessing
|
|
679
|
+
*
|
|
680
|
+
* The batch's own blocks are valid but processing failed because a previous batch did not
|
|
681
|
+
* deliver the parent. Keep the downloaded blocks and process them again once the previous
|
|
682
|
+
* batch is repaired.
|
|
683
|
+
*/
|
|
684
|
+
retainForReprocessing(): void {
|
|
685
|
+
if (this.state.status !== BatchStatus.Processing) {
|
|
686
|
+
throw new BatchError(this.wrongStatusErrorType(BatchStatus.Processing));
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
this.state = {
|
|
690
|
+
status: BatchStatus.AwaitingProcessing,
|
|
691
|
+
blocks: this.state.blocks,
|
|
692
|
+
payloadEnvelopes: this.state.payloadEnvelopes,
|
|
693
|
+
attempt: this.state.attempt,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
676
697
|
/**
|
|
677
698
|
* Processing -> AwaitingDownload
|
|
678
699
|
*/
|
|
@@ -750,7 +771,7 @@ export enum BatchErrorCode {
|
|
|
750
771
|
INVALID_COUNT = "BATCH_ERROR_INVALID_COUNT",
|
|
751
772
|
MAX_DOWNLOAD_ATTEMPTS = "BATCH_ERROR_MAX_DOWNLOAD_ATTEMPTS",
|
|
752
773
|
MAX_PROCESSING_ATTEMPTS = "BATCH_ERROR_MAX_PROCESSING_ATTEMPTS",
|
|
753
|
-
MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS = "
|
|
774
|
+
MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS = "BATCH_ERROR_MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS",
|
|
754
775
|
}
|
|
755
776
|
|
|
756
777
|
type BatchErrorType =
|