@lodestar/beacon-node 1.41.0-dev.afd446235e → 1.41.0-dev.bb273175f2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +3 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +1 -0
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +26 -10
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +3 -8
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/archiveStore/utils/updateBackfillRange.js +1 -1
- package/lib/chain/archiveStore/utils/updateBackfillRange.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +20 -2
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +47 -0
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/blockInput/types.d.ts +13 -1
- package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/types.js +1 -0
- package/lib/chain/blocks/blockInput/types.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +27 -4
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksDataAvailability.js +3 -0
- package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +4 -0
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +4 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.d.ts +12 -3
- package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +101 -96
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +11 -11
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +3 -3
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +30 -24
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +4 -1
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +6 -2
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +15 -7
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +1 -1
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.js +3 -3
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +8 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/blobSidecar.js +2 -2
- package/lib/chain/validation/blobSidecar.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +6 -3
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +1 -1
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +1 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +4 -4
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +1 -2
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/constants/network.d.ts +1 -2
- package/lib/constants/network.d.ts.map +1 -1
- package/lib/constants/network.js +0 -1
- package/lib/constants/network.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts +0 -8
- package/lib/network/peers/peerManager.d.ts.map +1 -1
- package/lib/network/peers/peerManager.js +3 -48
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +4 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +3 -2
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +3 -2
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +3 -2
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/util/clock.d.ts +6 -0
- package/lib/util/clock.d.ts.map +1 -1
- package/lib/util/clock.js +9 -3
- package/lib/util/clock.js.map +1 -1
- package/package.json +26 -26
- package/src/api/impl/beacon/blocks/index.ts +3 -2
- package/src/api/impl/debug/index.ts +1 -0
- package/src/api/impl/validator/index.ts +26 -12
- package/src/chain/archiveStore/archiveStore.ts +5 -5
- package/src/chain/archiveStore/utils/archiveBlocks.ts +4 -5
- package/src/chain/archiveStore/utils/updateBackfillRange.ts +1 -1
- package/src/chain/blocks/blockInput/blockInput.ts +68 -3
- package/src/chain/blocks/blockInput/types.ts +13 -0
- package/src/chain/blocks/importBlock.ts +35 -4
- package/src/chain/blocks/verifyBlocksDataAvailability.ts +3 -0
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +9 -2
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +7 -2
- package/src/chain/blocks/writeBlockInputToDb.ts +119 -101
- package/src/chain/chain.ts +26 -14
- package/src/chain/emitter.ts +3 -3
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -2
- package/src/chain/forkChoice/index.ts +39 -21
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +1 -2
- package/src/chain/regen/queued.ts +7 -2
- package/src/chain/regen/regen.ts +9 -3
- package/src/chain/seenCache/seenGossipBlockInput.ts +16 -7
- package/src/chain/validation/aggregateAndProof.ts +1 -1
- package/src/chain/validation/attestation.ts +3 -3
- package/src/chain/validation/attesterSlashing.ts +9 -0
- package/src/chain/validation/blobSidecar.ts +2 -2
- package/src/chain/validation/block.ts +9 -4
- package/src/chain/validation/dataColumnSidecar.ts +1 -1
- package/src/chain/validation/executionPayloadBid.ts +1 -2
- package/src/chain/validation/executionPayloadEnvelope.ts +4 -4
- package/src/chain/validation/payloadAttestationMessage.ts +1 -2
- package/src/constants/network.ts +0 -1
- package/src/network/peers/peerManager.ts +4 -61
- package/src/network/processor/gossipHandlers.ts +12 -3
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +3 -2
- package/src/network/reqresp/handlers/blobSidecarsByRange.ts +3 -2
- package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +3 -2
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
- package/src/util/clock.ts +9 -4
|
@@ -3,6 +3,7 @@ import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
|
3
3
|
import {
|
|
4
4
|
ForkName,
|
|
5
5
|
ForkPostFulu,
|
|
6
|
+
ForkPostGloas,
|
|
6
7
|
ForkPreGloas,
|
|
7
8
|
SLOTS_PER_EPOCH,
|
|
8
9
|
isForkPostDeneb,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
BlockInput,
|
|
21
22
|
BlockInputBlobs,
|
|
22
23
|
BlockInputColumns,
|
|
24
|
+
BlockInputNoData,
|
|
23
25
|
BlockInputPreData,
|
|
24
26
|
BlockWithSource,
|
|
25
27
|
DAType,
|
|
@@ -179,12 +181,19 @@ export class SeenBlockInput {
|
|
|
179
181
|
if (!blockInput) {
|
|
180
182
|
const {forkName, daOutOfRange} = this.buildCommonProps(block.message.slot);
|
|
181
183
|
|
|
182
|
-
// TODO GLOAS: Implement
|
|
183
184
|
if (isForkPostGloas(forkName)) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
// Post-gloas
|
|
186
|
+
blockInput = BlockInputNoData.createFromBlock({
|
|
187
|
+
block: block as SignedBeaconBlock<ForkPostGloas>,
|
|
188
|
+
blockRootHex,
|
|
189
|
+
daOutOfRange,
|
|
190
|
+
forkName,
|
|
191
|
+
source,
|
|
192
|
+
seenTimestampSec,
|
|
193
|
+
peerIdStr,
|
|
194
|
+
});
|
|
195
|
+
} else if (!isForkPostDeneb(forkName)) {
|
|
196
|
+
// Pre-deneb
|
|
188
197
|
blockInput = BlockInputPreData.createFromBlock({
|
|
189
198
|
block,
|
|
190
199
|
blockRootHex,
|
|
@@ -194,8 +203,8 @@ export class SeenBlockInput {
|
|
|
194
203
|
seenTimestampSec,
|
|
195
204
|
peerIdStr,
|
|
196
205
|
});
|
|
197
|
-
// Fulu Only
|
|
198
206
|
} else if (isForkPostFulu(forkName)) {
|
|
207
|
+
// Fulu Only
|
|
199
208
|
blockInput = BlockInputColumns.createFromBlock({
|
|
200
209
|
block: block as SignedBeaconBlock<ForkPostFulu & ForkPreGloas>,
|
|
201
210
|
blockRootHex,
|
|
@@ -207,8 +216,8 @@ export class SeenBlockInput {
|
|
|
207
216
|
seenTimestampSec,
|
|
208
217
|
peerIdStr,
|
|
209
218
|
});
|
|
210
|
-
// Deneb and Electra
|
|
211
219
|
} else {
|
|
220
|
+
// Deneb and Electra
|
|
212
221
|
blockInput = BlockInputBlobs.createFromBlock({
|
|
213
222
|
block: block as SignedBeaconBlock<ForkBlobsDA>,
|
|
214
223
|
blockRootHex,
|
|
@@ -81,7 +81,7 @@ async function validateAggregateAndProof(
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
// [REJECT] `aggregate.data.index == 0` if `block.slot == aggregate.data.slot`.
|
|
84
|
-
const block = chain.forkChoice.
|
|
84
|
+
const block = chain.forkChoice.getBlockDefaultStatus(attData.beaconBlockRoot);
|
|
85
85
|
|
|
86
86
|
// If block is unknown, we don't handle it here. It will throw error later on at `verifyHeadBlockAndTargetRoot()`
|
|
87
87
|
if (block !== null && block.slot === attData.slot && attData.index !== 0) {
|
|
@@ -186,7 +186,7 @@ export async function validateGossipAttestationsSameAttData(
|
|
|
186
186
|
chain.seenAttesters.add(targetEpoch, validatorIndex);
|
|
187
187
|
} else {
|
|
188
188
|
step0ResultOrErrors[oldIndex] = {
|
|
189
|
-
err: new AttestationError(GossipAction.
|
|
189
|
+
err: new AttestationError(GossipAction.REJECT, {
|
|
190
190
|
code: AttestationErrorCode.INVALID_SIGNATURE,
|
|
191
191
|
}),
|
|
192
192
|
};
|
|
@@ -307,7 +307,7 @@ async function validateAttestationNoSignatureCheck(
|
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
// [REJECT] `attestation.data.index == 0` if `block.slot == attestation.data.slot`.
|
|
310
|
-
const block = chain.forkChoice.
|
|
310
|
+
const block = chain.forkChoice.getBlockDefaultStatus(attData.beaconBlockRoot);
|
|
311
311
|
|
|
312
312
|
// block being null will be handled by `verifyHeadBlockAndTargetRoot`
|
|
313
313
|
if (block !== null && block.slot === attSlot && attData.index !== 0) {
|
|
@@ -756,7 +756,7 @@ export function getAttestationDataSigningRoot(config: BeaconConfig, data: phase0
|
|
|
756
756
|
function verifyHeadBlockIsKnown(chain: IBeaconChain, beaconBlockRoot: Root): ProtoBlock {
|
|
757
757
|
// TODO (LH): Enforce a maximum skip distance for unaggregated attestations.
|
|
758
758
|
|
|
759
|
-
const headBlock = chain.forkChoice.
|
|
759
|
+
const headBlock = chain.forkChoice.getBlockDefaultStatus(beaconBlockRoot);
|
|
760
760
|
if (headBlock === null) {
|
|
761
761
|
throw new AttestationError(GossipAction.IGNORE, {
|
|
762
762
|
code: AttestationErrorCode.UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT,
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
assertValidAttesterSlashing,
|
|
3
3
|
getAttesterSlashableIndices,
|
|
4
4
|
getAttesterSlashingSignatureSets,
|
|
5
|
+
isSlashableValidator,
|
|
5
6
|
} from "@lodestar/state-transition";
|
|
6
7
|
import {AttesterSlashing} from "@lodestar/types";
|
|
7
8
|
import {AttesterSlashingError, AttesterSlashingErrorCode, GossipAction} from "../errors/index.js";
|
|
@@ -58,6 +59,14 @@ export async function validateAttesterSlashing(
|
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
const currentEpoch = state.epochCtx.epoch;
|
|
63
|
+
if (!intersectingIndices.some((index) => isSlashableValidator(state.validators.getReadonly(index), currentEpoch))) {
|
|
64
|
+
throw new AttesterSlashingError(GossipAction.REJECT, {
|
|
65
|
+
code: AttesterSlashingErrorCode.INVALID,
|
|
66
|
+
error: Error("AttesterSlashing has no slashable validators"),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
61
70
|
const signatureSets = getAttesterSlashingSignatureSets(chain.config, state.slot, attesterSlashing);
|
|
62
71
|
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
|
63
72
|
throw new AttesterSlashingError(GossipAction.REJECT, {
|
|
@@ -78,7 +78,7 @@ export async function validateGossipBlobSidecar(
|
|
|
78
78
|
// already know this block.
|
|
79
79
|
const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobSidecar.signedBlockHeader.message);
|
|
80
80
|
const blockHex = toRootHex(blockRoot);
|
|
81
|
-
if (chain.forkChoice.
|
|
81
|
+
if (chain.forkChoice.getBlockHexDefaultStatus(blockHex) !== null) {
|
|
82
82
|
throw new BlobSidecarGossipError(GossipAction.IGNORE, {code: BlobSidecarErrorCode.ALREADY_KNOWN, root: blockHex});
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -89,7 +89,7 @@ export async function validateGossipBlobSidecar(
|
|
|
89
89
|
// gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is
|
|
90
90
|
// retrieved).
|
|
91
91
|
const parentRoot = toRootHex(blobSidecar.signedBlockHeader.message.parentRoot);
|
|
92
|
-
const parentBlock = chain.forkChoice.
|
|
92
|
+
const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
93
93
|
if (parentBlock === null) {
|
|
94
94
|
// If fork choice does *not* consider the parent to be a descendant of the finalized block,
|
|
95
95
|
// then there are two more cases:
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isExecutionEnabled,
|
|
10
10
|
isExecutionStateType,
|
|
11
11
|
} from "@lodestar/state-transition";
|
|
12
|
-
import {SignedBeaconBlock, deneb, gloas} from "@lodestar/types";
|
|
12
|
+
import {SignedBeaconBlock, deneb, gloas, isGloasBeaconBlock} from "@lodestar/types";
|
|
13
13
|
import {byteArrayEquals, sleep, toRootHex} from "@lodestar/utils";
|
|
14
14
|
import {BlockErrorCode, BlockGossipError, GossipAction} from "../errors/index.js";
|
|
15
15
|
import {IBeaconChain} from "../interface.js";
|
|
@@ -56,7 +56,7 @@ export async function validateGossipBlock(
|
|
|
56
56
|
// check, we will load the parent and state from disk only to find out later that we
|
|
57
57
|
// already know this block.
|
|
58
58
|
const blockRoot = toRootHex(config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block));
|
|
59
|
-
if (chain.forkChoice.
|
|
59
|
+
if (chain.forkChoice.getBlockHexDefaultStatus(blockRoot) !== null) {
|
|
60
60
|
throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -72,7 +72,12 @@ export async function validateGossipBlock(
|
|
|
72
72
|
// [REJECT] The current finalized_checkpoint is an ancestor of block -- i.e.
|
|
73
73
|
// get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == store.finalized_checkpoint.root
|
|
74
74
|
const parentRoot = toRootHex(block.parentRoot);
|
|
75
|
-
const parentBlock =
|
|
75
|
+
const parentBlock = isGloasBeaconBlock(block)
|
|
76
|
+
? chain.forkChoice.getBlockHexAndBlockHash(
|
|
77
|
+
parentRoot,
|
|
78
|
+
toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
79
|
+
)
|
|
80
|
+
: chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
76
81
|
if (parentBlock === null) {
|
|
77
82
|
// If fork choice does *not* consider the parent to be a descendant of the finalized block,
|
|
78
83
|
// then there are two more cases:
|
|
@@ -104,7 +109,7 @@ export async function validateGossipBlock(
|
|
|
104
109
|
|
|
105
110
|
// [REJECT] The block is from a higher slot than its parent.
|
|
106
111
|
if (parentBlock.slot >= blockSlot) {
|
|
107
|
-
throw new BlockGossipError(GossipAction.
|
|
112
|
+
throw new BlockGossipError(GossipAction.REJECT, {
|
|
108
113
|
code: BlockErrorCode.NOT_LATER_THAN_PARENT,
|
|
109
114
|
parentSlot: parentBlock.slot,
|
|
110
115
|
slot: blockSlot,
|
|
@@ -73,7 +73,7 @@ export async function validateGossipDataColumnSidecar(
|
|
|
73
73
|
// 6) [IGNORE] The sidecar's block's parent (defined by block_header.parent_root) has been seen (via gossip
|
|
74
74
|
// or non-gossip sources)
|
|
75
75
|
const parentRoot = toRootHex(blockHeader.parentRoot);
|
|
76
|
-
const parentBlock = chain.forkChoice.
|
|
76
|
+
const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
77
77
|
if (parentBlock === null) {
|
|
78
78
|
// If fork choice does *not* consider the parent to be a descendant of the finalized block,
|
|
79
79
|
// then there are two more cases:
|
|
@@ -113,8 +113,7 @@ async function validateExecutionPayloadBid(
|
|
|
113
113
|
|
|
114
114
|
// [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon
|
|
115
115
|
// block in fork choice.
|
|
116
|
-
|
|
117
|
-
if (block === null) {
|
|
116
|
+
if (!chain.forkChoice.hasBlock(bid.parentBlockRoot)) {
|
|
118
117
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
119
118
|
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
120
119
|
parentBlockRoot: parentBlockRootHex,
|
|
@@ -37,7 +37,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
37
37
|
// the block is retrieved).
|
|
38
38
|
// TODO GLOAS: Need to review this, we should queue the envelope for later
|
|
39
39
|
// processing if the block is not yet known, otherwise we would ignore it here
|
|
40
|
-
const block = chain.forkChoice.
|
|
40
|
+
const block = chain.forkChoice.getBlockDefaultStatus(envelope.beaconBlockRoot);
|
|
41
41
|
if (block === null) {
|
|
42
42
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
43
43
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN,
|
|
@@ -79,7 +79,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if (block.builderIndex
|
|
82
|
+
if (block.builderIndex == null || block.blockHashFromBid == null) {
|
|
83
83
|
// This indicates this block is a pre-gloas block which is wrong
|
|
84
84
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
85
85
|
code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
|
|
@@ -97,11 +97,11 @@ async function validateExecutionPayloadEnvelope(
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
// [REJECT] `payload.block_hash == bid.block_hash`
|
|
100
|
-
if (toRootHex(payload.blockHash) !== block.
|
|
100
|
+
if (toRootHex(payload.blockHash) !== block.blockHashFromBid) {
|
|
101
101
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
102
102
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
|
|
103
103
|
envelopeBlockHash: toRootHex(payload.blockHash),
|
|
104
|
-
bidBlockHash: block.
|
|
104
|
+
bidBlockHash: block.blockHashFromBid,
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -59,8 +59,7 @@ async function validatePayloadAttestationMessage(
|
|
|
59
59
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
60
60
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
61
61
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
62
|
-
|
|
63
|
-
if (block === null) {
|
|
62
|
+
if (!chain.forkChoice.hasBlock(data.beaconBlockRoot)) {
|
|
64
63
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
65
64
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
66
65
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
package/src/constants/network.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Connection,
|
|
1
|
+
import {Connection, PeerId, PrivateKey} from "@libp2p/interface";
|
|
2
2
|
import {BitArray} from "@chainsafe/ssz";
|
|
3
3
|
import {BeaconConfig} from "@lodestar/config";
|
|
4
4
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
@@ -162,9 +162,6 @@ export class PeerManager {
|
|
|
162
162
|
|
|
163
163
|
// A single map of connected peers with all necessary data to handle PINGs, STATUS, and metrics
|
|
164
164
|
private connectedPeers: Map<PeerIdStr, PeerData>;
|
|
165
|
-
/** Track one in-flight identify call per peer/connection id */
|
|
166
|
-
private readonly identifyInProgress = new Map<PeerIdStr, string>();
|
|
167
|
-
|
|
168
165
|
private opts: PeerManagerOpts;
|
|
169
166
|
private intervals: NodeJS.Timeout[] = [];
|
|
170
167
|
|
|
@@ -195,7 +192,6 @@ export class PeerManager {
|
|
|
195
192
|
|
|
196
193
|
this.libp2p.services.components.events.addEventListener(Libp2pEvent.connectionOpen, this.onLibp2pPeerConnect);
|
|
197
194
|
this.libp2p.services.components.events.addEventListener(Libp2pEvent.connectionClose, this.onLibp2pPeerDisconnect);
|
|
198
|
-
this.libp2p.services.components.events.addEventListener(Libp2pEvent.peerIdentify, this.onPeerIdentify);
|
|
199
195
|
this.networkEventBus.on(NetworkEvent.reqRespRequest, this.onRequest);
|
|
200
196
|
|
|
201
197
|
this.lastStatus = this.statusCache.get();
|
|
@@ -239,7 +235,6 @@ export class PeerManager {
|
|
|
239
235
|
Libp2pEvent.connectionClose,
|
|
240
236
|
this.onLibp2pPeerDisconnect
|
|
241
237
|
);
|
|
242
|
-
this.libp2p.services.components.events.removeEventListener(Libp2pEvent.peerIdentify, this.onPeerIdentify);
|
|
243
238
|
this.networkEventBus.off(NetworkEvent.reqRespRequest, this.onRequest);
|
|
244
239
|
for (const interval of this.intervals) clearInterval(interval);
|
|
245
240
|
}
|
|
@@ -490,25 +485,7 @@ export class PeerManager {
|
|
|
490
485
|
// peers that close identify right after connection open or turn out to be
|
|
491
486
|
// irrelevant.
|
|
492
487
|
if (peerData?.agentVersion === null) {
|
|
493
|
-
|
|
494
|
-
const connection = getConnection(this.libp2p, peerIdStr);
|
|
495
|
-
if (!connection || connection.status !== "open") {
|
|
496
|
-
this.logger.debug("Peer has no open connection for identify", {peerId: prettyPrintPeerId(peer)});
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const identifyKey = connection.id;
|
|
501
|
-
if (this.identifyInProgress.get(peerIdStr) === identifyKey) {
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
this.identifyInProgress.set(peerIdStr, identifyKey);
|
|
506
|
-
void this.identifyPeer(peerIdStr, prettyPrintPeerId(peer), connection, identifyKey).finally(() => {
|
|
507
|
-
// Clear only if this identify attempt is still the active one for this peer
|
|
508
|
-
if (this.identifyInProgress.get(peerIdStr) === identifyKey) {
|
|
509
|
-
this.identifyInProgress.delete(peerIdStr);
|
|
510
|
-
}
|
|
511
|
-
});
|
|
488
|
+
void this.identifyPeer(peer.toString(), prettyPrintPeerId(peer), getConnection(this.libp2p, peer.toString()));
|
|
512
489
|
}
|
|
513
490
|
}
|
|
514
491
|
}
|
|
@@ -867,7 +844,6 @@ export class PeerManager {
|
|
|
867
844
|
|
|
868
845
|
// remove the ping and status timer for the peer
|
|
869
846
|
this.connectedPeers.delete(peerIdStr);
|
|
870
|
-
this.identifyInProgress.delete(peerIdStr);
|
|
871
847
|
|
|
872
848
|
this.logger.verbose(logMessage, logContext);
|
|
873
849
|
this.networkEventBus.emit(NetworkEvent.peerDisconnected, {peer: peerIdStr});
|
|
@@ -885,47 +861,14 @@ export class PeerManager {
|
|
|
885
861
|
}
|
|
886
862
|
}
|
|
887
863
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
* This captures agentVersion from identify-push or successful inbound/outbound identify,
|
|
891
|
-
* even if our explicit identify request failed earlier.
|
|
892
|
-
*/
|
|
893
|
-
private onPeerIdentify = (evt: CustomEvent<IdentifyResult>): void => {
|
|
894
|
-
const {peerId, agentVersion} = evt.detail;
|
|
895
|
-
if (!agentVersion) return;
|
|
896
|
-
|
|
897
|
-
const peerIdStr = peerId.toString();
|
|
898
|
-
const peerData = this.connectedPeers.get(peerIdStr);
|
|
899
|
-
if (!peerData) return;
|
|
900
|
-
|
|
901
|
-
peerData.agentVersion = agentVersion;
|
|
902
|
-
peerData.agentClient = getKnownClientFromAgentVersion(agentVersion);
|
|
903
|
-
this.identifyInProgress.delete(peerIdStr);
|
|
904
|
-
};
|
|
905
|
-
|
|
906
|
-
private async identifyPeer(
|
|
907
|
-
peerIdStr: string,
|
|
908
|
-
peerIdPretty: string,
|
|
909
|
-
connection: Connection,
|
|
910
|
-
identifyKey: string
|
|
911
|
-
): Promise<void> {
|
|
912
|
-
if (this.identifyInProgress.get(peerIdStr) !== identifyKey) {
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
if (connection.status !== "open") {
|
|
864
|
+
private async identifyPeer(peerIdStr: string, peerIdPretty: string, connection?: Connection): Promise<void> {
|
|
865
|
+
if (!connection || connection.status !== "open") {
|
|
917
866
|
this.logger.debug("Peer has no open connection for identify", {peerId: peerIdPretty});
|
|
918
867
|
return;
|
|
919
868
|
}
|
|
920
869
|
|
|
921
870
|
try {
|
|
922
871
|
const result = await this.libp2p.services.identify.identify(connection);
|
|
923
|
-
|
|
924
|
-
// A newer identify attempt may have superseded this one (e.g. reconnect).
|
|
925
|
-
if (this.identifyInProgress.get(peerIdStr) !== identifyKey) {
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
872
|
const agentVersion = result.agentVersion;
|
|
930
873
|
if (agentVersion) {
|
|
931
874
|
const connectedPeerData = this.connectedPeers.get(peerIdStr);
|
|
@@ -2,6 +2,7 @@ import {routes} from "@lodestar/api";
|
|
|
2
2
|
import {BeaconConfig, ChainForkConfig} from "@lodestar/config";
|
|
3
3
|
import {
|
|
4
4
|
ForkName,
|
|
5
|
+
ForkPostDeneb,
|
|
5
6
|
ForkPostElectra,
|
|
6
7
|
ForkPreElectra,
|
|
7
8
|
ForkSeq,
|
|
@@ -70,6 +71,7 @@ import {validateGossipPayloadAttestationMessage} from "../../chain/validation/pa
|
|
|
70
71
|
import {OpSource} from "../../chain/validatorMonitor.js";
|
|
71
72
|
import {Metrics} from "../../metrics/index.js";
|
|
72
73
|
import {kzgCommitmentToVersionedHash} from "../../util/blobs.js";
|
|
74
|
+
import {getBlobKzgCommitments} from "../../util/dataColumns.ts";
|
|
73
75
|
import {INetworkCore} from "../core/index.js";
|
|
74
76
|
import {NetworkEventBus} from "../events.js";
|
|
75
77
|
import {
|
|
@@ -417,9 +419,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
417
419
|
chain.getBlobsTracker.triggerGetBlobs(blockInput);
|
|
418
420
|
} else {
|
|
419
421
|
metrics?.blockInputFetchStats.totalDataAvailableBlockInputs.inc();
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
422
|
+
const blobCount = getBlobKzgCommitments(
|
|
423
|
+
blockInput.forkName,
|
|
424
|
+
signedBlock as SignedBeaconBlock<ForkPostDeneb>
|
|
425
|
+
).length;
|
|
426
|
+
metrics?.blockInputFetchStats.totalDataAvailableBlockInputBlobs.inc(blobCount);
|
|
423
427
|
}
|
|
424
428
|
|
|
425
429
|
chain
|
|
@@ -852,6 +856,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
852
856
|
} catch (e) {
|
|
853
857
|
logger.error("Error adding to payloadAttestation pool", {}, e as Error);
|
|
854
858
|
}
|
|
859
|
+
chain.forkChoice.notifyPtcMessages(
|
|
860
|
+
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
861
|
+
[validationResult.validatorCommitteeIndex],
|
|
862
|
+
payloadAttestationMessage.data.payloadPresent
|
|
863
|
+
);
|
|
855
864
|
},
|
|
856
865
|
[GossipType.execution_payload_bid]: async ({
|
|
857
866
|
gossipData,
|
|
@@ -47,9 +47,10 @@ export async function* onBeaconBlocksByRange(
|
|
|
47
47
|
|
|
48
48
|
// Non-finalized range of blocks
|
|
49
49
|
if (endSlot > finalizedSlot) {
|
|
50
|
-
const
|
|
50
|
+
const headBlock = chain.forkChoice.getHead();
|
|
51
|
+
const headRoot = headBlock.blockRoot;
|
|
51
52
|
// TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
|
|
52
|
-
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot);
|
|
53
|
+
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
|
|
53
54
|
// getAllAncestorBlocks response includes the head node, so it's the full chain.
|
|
54
55
|
|
|
55
56
|
// Iterate head chain with ascending block numbers
|
|
@@ -34,9 +34,10 @@ export async function* onBlobSidecarsByRange(
|
|
|
34
34
|
|
|
35
35
|
// Non-finalized range of blobs
|
|
36
36
|
if (endSlot > finalizedSlot) {
|
|
37
|
-
const
|
|
37
|
+
const headBlock = chain.forkChoice.getHead();
|
|
38
|
+
const headRoot = headBlock.blockRoot;
|
|
38
39
|
// TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
|
|
39
|
-
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot);
|
|
40
|
+
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
|
|
40
41
|
|
|
41
42
|
// Iterate head chain with ascending block numbers
|
|
42
43
|
for (let i = headChain.length - 1; i >= 0; i--) {
|
|
@@ -20,7 +20,7 @@ export async function* onBlobSidecarsByRoot(
|
|
|
20
20
|
for (const blobIdentifier of requestBody) {
|
|
21
21
|
const {blockRoot, index} = blobIdentifier;
|
|
22
22
|
const blockRootHex = toRootHex(blockRoot);
|
|
23
|
-
const block = chain.forkChoice.
|
|
23
|
+
const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex);
|
|
24
24
|
|
|
25
25
|
// NOTE: Only support non-finalized blocks.
|
|
26
26
|
// SPEC: Clients MUST support requesting blocks and sidecars since the latest finalized epoch.
|
|
@@ -78,8 +78,9 @@ export async function* onDataColumnSidecarsByRange(
|
|
|
78
78
|
|
|
79
79
|
// Non-finalized range of columns
|
|
80
80
|
if (endSlot > finalizedSlot) {
|
|
81
|
-
const
|
|
82
|
-
const
|
|
81
|
+
const headBlock = chain.forkChoice.getHead();
|
|
82
|
+
const headRoot = headBlock.blockRoot;
|
|
83
|
+
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
|
|
83
84
|
|
|
84
85
|
// Iterate head chain with ascending block numbers
|
|
85
86
|
for (let i = headChain.length - 1; i >= 0; i--) {
|
|
@@ -34,7 +34,7 @@ export async function* onDataColumnSidecarsByRoot(
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const blockRootHex = toRootHex(blockRoot);
|
|
37
|
-
const block = chain.forkChoice.
|
|
37
|
+
const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex);
|
|
38
38
|
// If the block is not in fork choice, it may be finalized. Attempt to find its slot in block archive
|
|
39
39
|
const slot = block ? block.slot : await db.blockArchive.getSlotByRoot(blockRoot);
|
|
40
40
|
|
package/src/util/clock.ts
CHANGED
|
@@ -92,15 +92,17 @@ export class Clock extends EventEmitter implements IClock {
|
|
|
92
92
|
}
|
|
93
93
|
return slot;
|
|
94
94
|
}
|
|
95
|
-
|
|
96
95
|
/**
|
|
97
96
|
* If it's too close to next slot given MAXIMUM_GOSSIP_CLOCK_DISPARITY, return currentSlot + 1.
|
|
98
97
|
* Otherwise return currentSlot
|
|
98
|
+
*
|
|
99
|
+
* Spec: phase0/p2p-interface.md - gossip validation uses `current_time + MAXIMUM_GOSSIP_CLOCK_DISPARITY < message_time`
|
|
100
|
+
* to reject future messages (strict `<`), so the boundary (exactly equal) is accepted, hence `<=` here.
|
|
99
101
|
*/
|
|
100
102
|
get currentSlotWithGossipDisparity(): Slot {
|
|
101
103
|
const currentSlot = this.currentSlot;
|
|
102
104
|
const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
|
|
103
|
-
return nextSlotTime - Date.now()
|
|
105
|
+
return nextSlotTime - Date.now() <= this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
get currentEpoch(): Epoch {
|
|
@@ -121,6 +123,9 @@ export class Clock extends EventEmitter implements IClock {
|
|
|
121
123
|
|
|
122
124
|
/**
|
|
123
125
|
* Check if a slot is current slot given MAXIMUM_GOSSIP_CLOCK_DISPARITY.
|
|
126
|
+
*
|
|
127
|
+
* Uses `<=` for disparity checks because the spec rejects with strict `<`
|
|
128
|
+
* (phase0/p2p-interface.md), meaning the boundary (exactly equal) is accepted.
|
|
124
129
|
*/
|
|
125
130
|
isCurrentSlotGivenGossipDisparity(slot: Slot): boolean {
|
|
126
131
|
const currentSlot = this.currentSlot;
|
|
@@ -129,12 +134,12 @@ export class Clock extends EventEmitter implements IClock {
|
|
|
129
134
|
}
|
|
130
135
|
const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
|
|
131
136
|
// we're too close to next slot, accept next slot
|
|
132
|
-
if (nextSlotTime - Date.now()
|
|
137
|
+
if (nextSlotTime - Date.now() <= this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
|
|
133
138
|
return slot === currentSlot + 1;
|
|
134
139
|
}
|
|
135
140
|
const currentSlotTime = computeTimeAtSlot(this.config, currentSlot, this.genesisTime) * 1000;
|
|
136
141
|
// we've just passed the current slot, accept previous slot
|
|
137
|
-
if (Date.now() - currentSlotTime
|
|
142
|
+
if (Date.now() - currentSlotTime <= this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
|
|
138
143
|
return slot === currentSlot - 1;
|
|
139
144
|
}
|
|
140
145
|
return false;
|