@lodestar/beacon-node 1.45.0 → 1.46.0-dev.13423cff6d
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 +172 -88
- 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/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/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.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 +5 -0
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +32 -4
- 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/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 +76 -233
- 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/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/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 +206 -98
- 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/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/produceBlock/produceBlockBody.ts +1 -1
- 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 +34 -4
- 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/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/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
- package/src/network/reqresp/handlers/index.ts +21 -12
- 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
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
2
|
+
import {
|
|
3
|
+
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
4
|
+
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
|
|
5
|
+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
6
|
+
MAX_WITHDRAWALS_PER_PAYLOAD,
|
|
7
|
+
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
|
|
8
|
+
} from "@lodestar/params";
|
|
2
9
|
import {
|
|
3
10
|
computeStartSlotAtEpoch,
|
|
4
11
|
getExecutionPayloadEnvelopeSignatureSet,
|
|
@@ -115,6 +122,36 @@ async function validateExecutionPayloadEnvelope(
|
|
|
115
122
|
});
|
|
116
123
|
}
|
|
117
124
|
|
|
125
|
+
// [REJECT] The counts of `execution_requests` are within their respective limits.
|
|
126
|
+
// New in Gloas:EIP7688 — progressive lists are unbounded at the type level, so bounds
|
|
127
|
+
// are enforced here in gossip validation.
|
|
128
|
+
const {executionRequests} = envelope;
|
|
129
|
+
const requestCountLimits: [string, number, number][] = [
|
|
130
|
+
["withdrawals", executionRequests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
|
|
131
|
+
["consolidations", executionRequests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
|
|
132
|
+
["builderDeposits", executionRequests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD],
|
|
133
|
+
["builderExits", executionRequests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
|
|
134
|
+
];
|
|
135
|
+
for (const [name, count, limit] of requestCountLimits) {
|
|
136
|
+
if (count > limit) {
|
|
137
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
138
|
+
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED,
|
|
139
|
+
name,
|
|
140
|
+
count,
|
|
141
|
+
limit,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// [REJECT] The number of withdrawals is within the limit.
|
|
147
|
+
if (payload.withdrawals.length > MAX_WITHDRAWALS_PER_PAYLOAD) {
|
|
148
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
149
|
+
code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED,
|
|
150
|
+
count: payload.withdrawals.length,
|
|
151
|
+
limit: MAX_WITHDRAWALS_PER_PAYLOAD,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
118
155
|
// Get the block state to verify the builder's signature.
|
|
119
156
|
const blockState = await chain.regen
|
|
120
157
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
+
import {GENESIS_EPOCH, GENESIS_SLOT, MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
3
|
import {
|
|
3
4
|
computeEpochAtSlot,
|
|
5
|
+
computeStartSlotAtEpoch,
|
|
4
6
|
createSingleSignatureSetFromComponents,
|
|
5
7
|
getProposerPreferencesSigningRoot,
|
|
6
8
|
} from "@lodestar/state-transition";
|
|
7
|
-
import {ValidatorIndex, gloas} from "@lodestar/types";
|
|
9
|
+
import {Epoch, Slot, ValidatorIndex, gloas} from "@lodestar/types";
|
|
8
10
|
import {toRootHex} from "@lodestar/utils";
|
|
9
11
|
import {GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode} from "../errors/index.js";
|
|
10
12
|
import {IBeaconChain} from "../index.js";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Validates a gossiped `SignedProposerPreferences` per
|
|
14
|
-
* https://github.com/ethereum/consensus-specs/blob/
|
|
16
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.13/specs/gloas/p2p-interface.md#new-proposer_preferences
|
|
15
17
|
*/
|
|
16
18
|
export async function validateGossipProposerPreferences(
|
|
17
19
|
chain: IBeaconChain,
|
|
@@ -22,9 +24,10 @@ export async function validateGossipProposerPreferences(
|
|
|
22
24
|
const dependentRootHex = toRootHex(dependentRoot);
|
|
23
25
|
const proposalEpoch = computeEpochAtSlot(proposalSlot);
|
|
24
26
|
|
|
25
|
-
// [IGNORE] `preferences.proposal_slot` is
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
// [IGNORE] `preferences.proposal_slot` is within the proposer lookahead,
|
|
28
|
+
// allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
29
|
+
const currentEpoch = computeEpochAtSlot(chain.clock.currentSlotWithGossipDisparity);
|
|
30
|
+
if (proposalEpoch > currentEpoch + MIN_SEED_LOOKAHEAD) {
|
|
28
31
|
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
29
32
|
code: ProposerPreferencesErrorCode.INVALID_EPOCH,
|
|
30
33
|
proposalSlot,
|
|
@@ -34,31 +37,65 @@ export async function validateGossipProposerPreferences(
|
|
|
34
37
|
|
|
35
38
|
// [IGNORE] `preferences.proposal_slot` has not already passed, i.e. `proposal_slot > current_slot`,
|
|
36
39
|
// allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
|
37
|
-
|
|
38
|
-
if (proposalSlot <= currentSlot) {
|
|
40
|
+
if (chain.clock.msFromSlot(proposalSlot) > chain.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
|
|
39
41
|
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
40
42
|
code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED,
|
|
41
43
|
proposalSlot,
|
|
42
|
-
currentSlot,
|
|
44
|
+
currentSlot: chain.clock.currentSlot,
|
|
43
45
|
});
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// [IGNORE] The block with root `dependent_root` has been seen by the node.
|
|
49
|
+
const dependentBlock = chain.forkChoice.getBlockHexDefaultStatus(dependentRootHex);
|
|
50
|
+
if (dependentBlock === null) {
|
|
51
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
52
|
+
code: ProposerPreferencesErrorCode.UNKNOWN_DEPENDENT_ROOT,
|
|
53
|
+
proposalSlot,
|
|
54
|
+
dependentRoot: dependentRootHex,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const dependentEpoch = Math.max(GENESIS_EPOCH, proposalEpoch - MIN_SEED_LOOKAHEAD);
|
|
59
|
+
const dependentRootSlot = getDependentRootSlot(dependentEpoch);
|
|
60
|
+
|
|
61
|
+
// [REJECT] The slot of the block with root `preferences.dependent_root` is strictly less than
|
|
62
|
+
// `compute_start_slot_at_epoch(compute_epoch_at_slot(preferences.proposal_slot) - MIN_SEED_LOOKAHEAD)`.
|
|
63
|
+
if (dependentBlock.slot > dependentRootSlot) {
|
|
64
|
+
throw new ProposerPreferencesError(GossipAction.REJECT, {
|
|
65
|
+
code: ProposerPreferencesErrorCode.INVALID_DEPENDENT_ROOT_SLOT,
|
|
66
|
+
dependentRoot: dependentRootHex,
|
|
67
|
+
dependentBlockSlot: dependentBlock.slot,
|
|
68
|
+
dependentRootSlot,
|
|
69
|
+
dependentEpoch,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// [IGNORE] `is_valid_dependent_root(store, preferences.dependent_root, epoch)` returns `True`,
|
|
74
|
+
// where `epoch` is `compute_epoch_at_slot(preferences.proposal_slot) - MIN_SEED_LOOKAHEAD`.
|
|
75
|
+
if (!isValidDependentRoot(chain.forkChoice, dependentBlock, dependentEpoch)) {
|
|
76
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
77
|
+
code: ProposerPreferencesErrorCode.INVALID_DEPENDENT_ROOT,
|
|
78
|
+
dependentRoot: dependentRootHex,
|
|
79
|
+
dependentBlockSlot: dependentBlock.slot,
|
|
80
|
+
dependentEpoch,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
47
84
|
// Resolve the proposer lookahead for the message's branch via head state (fast path) or
|
|
48
85
|
// the previous-root checkpoint state (populated by `processSlotsToNearestCheckpoint` for
|
|
49
|
-
// any imported branch crossing into `
|
|
86
|
+
// any imported branch crossing into `dependentEpoch`). The head-state path also handles
|
|
50
87
|
// narrow timing windows where the checkpoint state isn't yet populated.
|
|
51
88
|
const headState = chain.getHeadState();
|
|
52
89
|
let proposers: ValidatorIndex[] | null = null;
|
|
53
90
|
if (headState.epoch === proposalEpoch && headState.currentDecisionRoot === dependentRootHex) {
|
|
54
91
|
proposers = headState.currentProposers;
|
|
55
|
-
} else if (headState.epoch ===
|
|
92
|
+
} else if (headState.epoch === dependentEpoch && headState.nextDecisionRoot === dependentRootHex) {
|
|
56
93
|
proposers = headState.nextProposers;
|
|
57
94
|
} else {
|
|
58
95
|
// Sync lookup only to not trigger disk reload from gossip input.
|
|
59
|
-
const checkpointState = chain.regen.getCheckpointStateSync({epoch:
|
|
96
|
+
const checkpointState = chain.regen.getCheckpointStateSync({epoch: dependentEpoch, rootHex: dependentRootHex});
|
|
60
97
|
if (checkpointState !== null) {
|
|
61
|
-
// State is at `
|
|
98
|
+
// State is at `dependentEpoch`, so proposers for `proposalSlot` (next epoch from
|
|
62
99
|
// the state's perspective) live in `nextProposers`.
|
|
63
100
|
proposers = checkpointState.nextProposers;
|
|
64
101
|
}
|
|
@@ -118,3 +155,28 @@ export async function validateGossipProposerPreferences(
|
|
|
118
155
|
|
|
119
156
|
chain.proposerPreferencesPool.add(signedProposerPreferences);
|
|
120
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check whether `dependentBlock` is a viable shuffling-dependent root for `epoch`:
|
|
161
|
+
* it is before the epoch boundary and either has a direct child at or after
|
|
162
|
+
* the boundary, or is the current fork-choice head.
|
|
163
|
+
*/
|
|
164
|
+
export function isValidDependentRoot(forkChoice: IForkChoice, dependentBlock: ProtoBlock, epoch: Epoch): boolean {
|
|
165
|
+
const epochStartSlot = computeStartSlotAtEpoch(epoch);
|
|
166
|
+
if (dependentBlock.slot > getDependentRootSlot(epoch)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const block of forkChoice.getBlockSummariesByParentRoot(dependentBlock.blockRoot)) {
|
|
171
|
+
if (block.slot >= epochStartSlot) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return dependentBlock.blockRoot === forkChoice.getHeadRoot();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Return the slot used to select a dependent root for `epoch`, clamped to genesis. */
|
|
180
|
+
function getDependentRootSlot(epoch: Epoch): Slot {
|
|
181
|
+
return Math.max(GENESIS_SLOT, computeStartSlotAtEpoch(epoch) - 1);
|
|
182
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
-
import {DOMAIN_AGGREGATE_AND_PROOF
|
|
2
|
+
import {DOMAIN_AGGREGATE_AND_PROOF} from "@lodestar/params";
|
|
3
3
|
import {ISignatureSet, SignatureSetType, computeSigningRoot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
|
-
import {Epoch, SignedAggregateAndProof, ValidatorIndex
|
|
4
|
+
import {Epoch, SignedAggregateAndProof, ValidatorIndex} from "@lodestar/types";
|
|
5
5
|
|
|
6
6
|
export function getAggregateAndProofSigningRoot(
|
|
7
7
|
config: BeaconConfig,
|
|
@@ -14,7 +14,7 @@ export function getAggregateAndProofSigningRoot(
|
|
|
14
14
|
const slot = computeStartSlotAtEpoch(epoch);
|
|
15
15
|
const fork = config.getForkName(slot);
|
|
16
16
|
const aggregatorDomain = config.getDomainAtFork(fork, DOMAIN_AGGREGATE_AND_PROOF);
|
|
17
|
-
const sszType =
|
|
17
|
+
const sszType = config.getForkTypes(slot).AggregateAndProof;
|
|
18
18
|
return computeSigningRoot(sszType, aggregateAndProof.message, aggregatorDomain);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -469,7 +469,7 @@ export function createValidatorMonitor(
|
|
|
469
469
|
proposerIndex,
|
|
470
470
|
src,
|
|
471
471
|
builderIndex: bid.builderIndex,
|
|
472
|
-
gasLimit: bid.gasLimit,
|
|
472
|
+
gasLimit: Number(bid.gasLimit),
|
|
473
473
|
value: bid.value.toString(),
|
|
474
474
|
parentBlockRoot: toRootHex(bid.parentBlockRoot),
|
|
475
475
|
parentBlockHash: toRootHex(bid.parentBlockHash),
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {ContainerType, VectorCompositeType} from "@chainsafe/ssz";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
3
|
import {DatabaseController, Repository} from "@lodestar/db";
|
|
4
|
+
import {CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS, NEXT_SYNC_COMMITTEE_DEPTH_GLOAS} from "@lodestar/params";
|
|
4
5
|
import {ssz} from "@lodestar/types";
|
|
5
6
|
import {SyncCommitteeWitness} from "../../chain/lightClient/types.js";
|
|
6
7
|
import {Bucket, getBucketNameByValue} from "../buckets.js";
|
|
7
8
|
|
|
8
|
-
// We add a 1-byte prefix where 0 means pre-
|
|
9
|
+
// We add a 1-byte prefix where 0 means pre-Electra, 1 means post-Electra, and 2 means post-Gloas.
|
|
9
10
|
enum PrefixByte {
|
|
10
11
|
PRE_ELECTRA = 0,
|
|
11
12
|
POST_ELECTRA = 1,
|
|
13
|
+
POST_GLOAS = 2,
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const NUM_WITNESS = 4;
|
|
@@ -35,6 +37,35 @@ export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncC
|
|
|
35
37
|
// Overrides for multi-fork
|
|
36
38
|
encodeValue(value: SyncCommitteeWitness): Uint8Array {
|
|
37
39
|
const numWitness = value.witness.length;
|
|
40
|
+
const hasGloasBranches =
|
|
41
|
+
value.currentSyncCommitteeBranch !== undefined || value.nextSyncCommitteeBranch !== undefined;
|
|
42
|
+
|
|
43
|
+
if (hasGloasBranches) {
|
|
44
|
+
if (
|
|
45
|
+
value.currentSyncCommitteeBranch?.length !== CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS ||
|
|
46
|
+
value.nextSyncCommitteeBranch?.length !== NEXT_SYNC_COMMITTEE_DEPTH_GLOAS
|
|
47
|
+
) {
|
|
48
|
+
throw Error(
|
|
49
|
+
`Invalid post-Gloas sync committee branch lengths current=${value.currentSyncCommitteeBranch?.length} next=${value.nextSyncCommitteeBranch?.length}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const type = new ContainerType({
|
|
54
|
+
currentSyncCommitteeBranch: new VectorCompositeType(ssz.Root, CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
55
|
+
nextSyncCommitteeBranch: new VectorCompositeType(ssz.Root, NEXT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
56
|
+
currentSyncCommitteeRoot: ssz.Root,
|
|
57
|
+
nextSyncCommitteeRoot: ssz.Root,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const valueBytes = type.serialize({
|
|
61
|
+
currentSyncCommitteeBranch: value.currentSyncCommitteeBranch,
|
|
62
|
+
nextSyncCommitteeBranch: value.nextSyncCommitteeBranch,
|
|
63
|
+
currentSyncCommitteeRoot: value.currentSyncCommitteeRoot,
|
|
64
|
+
nextSyncCommitteeRoot: value.nextSyncCommitteeRoot,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return prefixData(PrefixByte.POST_GLOAS, valueBytes);
|
|
68
|
+
}
|
|
38
69
|
|
|
39
70
|
if (numWitness !== NUM_WITNESS && numWitness !== NUM_WITNESS_ELECTRA) {
|
|
40
71
|
throw Error(`Number of witness can only be 4 pre-electra or 5 post-electra numWitness=${numWitness}`);
|
|
@@ -51,19 +82,25 @@ export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncC
|
|
|
51
82
|
// We need to differentiate between post-electra and pre-electra witness
|
|
52
83
|
// such that we can deserialize correctly
|
|
53
84
|
const isPostElectra = numWitness === NUM_WITNESS_ELECTRA;
|
|
54
|
-
|
|
55
|
-
prefixByte[0] = isPostElectra ? PrefixByte.POST_ELECTRA : PrefixByte.PRE_ELECTRA;
|
|
56
|
-
|
|
57
|
-
const prefixedData = new Uint8Array(1 + valueBytes.length);
|
|
58
|
-
prefixedData.set(prefixByte, 0);
|
|
59
|
-
prefixedData.set(valueBytes, 1);
|
|
60
|
-
|
|
61
|
-
return prefixedData;
|
|
85
|
+
return prefixData(isPostElectra ? PrefixByte.POST_ELECTRA : PrefixByte.PRE_ELECTRA, valueBytes);
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
decodeValue(data: Uint8Array): SyncCommitteeWitness {
|
|
65
89
|
// First byte is written
|
|
66
90
|
const prefix = data.subarray(0, 1);
|
|
91
|
+
const isPostGloas = prefix[0] === PrefixByte.POST_GLOAS;
|
|
92
|
+
|
|
93
|
+
if (isPostGloas) {
|
|
94
|
+
const type = new ContainerType({
|
|
95
|
+
currentSyncCommitteeBranch: new VectorCompositeType(ssz.Root, CURRENT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
96
|
+
nextSyncCommitteeBranch: new VectorCompositeType(ssz.Root, NEXT_SYNC_COMMITTEE_DEPTH_GLOAS),
|
|
97
|
+
currentSyncCommitteeRoot: ssz.Root,
|
|
98
|
+
nextSyncCommitteeRoot: ssz.Root,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return {witness: [], ...type.deserialize(data.subarray(1))};
|
|
102
|
+
}
|
|
103
|
+
|
|
67
104
|
const isPostElectra = prefix[0] === PrefixByte.POST_ELECTRA;
|
|
68
105
|
|
|
69
106
|
const type = new ContainerType({
|
|
@@ -75,3 +112,14 @@ export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncC
|
|
|
75
112
|
return type.deserialize(data.subarray(1));
|
|
76
113
|
}
|
|
77
114
|
}
|
|
115
|
+
|
|
116
|
+
function prefixData(prefix: PrefixByte, valueBytes: Uint8Array): Uint8Array {
|
|
117
|
+
const prefixByte = new Uint8Array(1);
|
|
118
|
+
prefixByte[0] = prefix;
|
|
119
|
+
|
|
120
|
+
const prefixedData = new Uint8Array(1 + valueBytes.length);
|
|
121
|
+
prefixedData.set(prefixByte, 0);
|
|
122
|
+
prefixedData.set(valueBytes, 1);
|
|
123
|
+
|
|
124
|
+
return prefixedData;
|
|
125
|
+
}
|
|
@@ -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;
|