@lodestar/beacon-node 1.51.0-dev.2531f8fae8 → 1.51.0-dev.2aff7aaeb7
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 +6 -3
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/validator/index.d.ts +6 -0
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +38 -25
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js +4 -2
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +2 -10
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +2 -14
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +10 -7
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +21 -16
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +4 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +17 -6
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSignatures.js +7 -5
- package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +1 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/bls/interface.d.ts +2 -0
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +8 -6
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +1 -1
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/worker.js +1 -1
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.js +1 -1
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.js +1 -1
- package/lib/chain/bls/utils.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.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +7 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/interface.d.ts +1 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +23 -10
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/attestationPool.d.ts +1 -1
- package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/attestationPool.js +15 -4
- package/lib/chain/opPools/attestationPool.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +17 -7
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.d.ts.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js +9 -3
- package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.d.ts.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js +11 -3
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +1 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +13 -13
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts +6 -0
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +53 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/db/flatFileStore/flatFileStore.js +5 -5
- package/lib/db/flatFileStore/flatFileStore.js.map +1 -1
- package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -1
- package/lib/db/flatFileStore/slotIndex.js +2 -2
- package/lib/db/flatFileStore/slotIndex.js.map +1 -1
- package/lib/execution/builder/apiClient.d.ts +14 -8
- package/lib/execution/builder/apiClient.d.ts.map +1 -1
- package/lib/execution/builder/apiClient.js +12 -10
- package/lib/execution/builder/apiClient.js.map +1 -1
- package/lib/execution/builder/http.d.ts +2 -0
- package/lib/execution/builder/http.d.ts.map +1 -1
- package/lib/execution/builder/http.js +2 -0
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +2 -2
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/execution/engine/utils.d.ts.map +1 -1
- package/lib/execution/engine/utils.js +3 -2
- package/lib/execution/engine/utils.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +4 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +10 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/server/http.js +5 -5
- package/lib/metrics/server/http.js.map +1 -1
- package/lib/network/core/networkCore.d.ts +2 -0
- package/lib/network/core/networkCore.d.ts.map +1 -1
- package/lib/network/core/networkCore.js +3 -0
- package/lib/network/core/networkCore.js.map +1 -1
- package/lib/network/core/networkCoreWorker.js +1 -0
- package/lib/network/core/networkCoreWorker.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -0
- package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.js +3 -0
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/core/types.d.ts +2 -0
- package/lib/network/core/types.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts +1 -0
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +10 -0
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/interface.d.ts +2 -0
- 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 +5 -1
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.d.ts +2 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +3 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/options.d.ts +2 -0
- package/lib/network/options.d.ts.map +1 -1
- package/lib/network/options.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts +2 -1
- package/lib/network/peers/peerManager.d.ts.map +1 -1
- package/lib/network/peers/peerManager.js +28 -1
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/peers/utils/assertPeerRelevance.d.ts +9 -1
- package/lib/network/peers/utils/assertPeerRelevance.d.ts.map +1 -1
- package/lib/network/peers/utils/assertPeerRelevance.js +32 -21
- package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts +4 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.js +8 -16
- package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +24 -16
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/node/nodejs.d.ts +3 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +38 -20
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/options.d.ts +3 -1
- package/lib/node/options.d.ts.map +1 -1
- package/lib/node/options.js +4 -2
- package/lib/node/options.js.map +1 -1
- package/lib/sync/sync.d.ts +7 -0
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +61 -3
- package/lib/sync/sync.js.map +1 -1
- package/lib/util/file.d.ts.map +1 -1
- package/lib/util/file.js +8 -9
- package/lib/util/file.js.map +1 -1
- package/lib/util/sszBytes.d.ts +5 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +33 -0
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +16 -17
- package/src/api/impl/beacon/blocks/index.ts +7 -3
- package/src/api/impl/beacon/state/utils.ts +1 -1
- package/src/api/impl/validator/index.ts +43 -29
- package/src/chain/GetBlobsTracker.ts +4 -2
- package/src/chain/blocks/importExecutionPayload.ts +2 -20
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +32 -21
- package/src/chain/blocks/types.ts +4 -1
- package/src/chain/blocks/verifyBlock.ts +30 -16
- package/src/chain/blocks/verifyBlocksSignatures.ts +7 -5
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +1 -3
- package/src/chain/bls/interface.ts +2 -0
- package/src/chain/bls/multithread/index.ts +9 -5
- package/src/chain/bls/multithread/types.ts +1 -1
- package/src/chain/bls/multithread/worker.ts +1 -5
- package/src/chain/bls/singleThread.ts +1 -1
- package/src/chain/bls/utils.ts +1 -1
- package/src/chain/chain.ts +1 -1
- package/src/chain/errors/executionPayloadEnvelope.ts +8 -0
- package/src/chain/interface.ts +1 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +25 -9
- package/src/chain/opPools/attestationPool.ts +16 -4
- package/src/chain/opPools/payloadAttestationPool.ts +18 -7
- package/src/chain/opPools/syncCommitteeMessagePool.ts +10 -3
- package/src/chain/opPools/syncContributionAndProofPool.ts +13 -3
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/validation/executionPayloadBid.ts +14 -13
- package/src/chain/validation/executionPayloadEnvelope.ts +68 -1
- package/src/db/flatFileStore/flatFileStore.ts +7 -7
- package/src/db/flatFileStore/slotIndex.ts +4 -3
- package/src/execution/builder/apiClient.ts +16 -13
- package/src/execution/builder/http.ts +4 -0
- package/src/execution/engine/types.ts +3 -1
- package/src/execution/engine/utils.ts +4 -2
- package/src/metrics/metrics/lodestar.ts +10 -0
- package/src/metrics/server/http.ts +5 -5
- package/src/network/core/networkCore.ts +5 -0
- package/src/network/core/networkCoreWorker.ts +1 -0
- package/src/network/core/networkCoreWorkerHandler.ts +4 -0
- package/src/network/core/types.ts +2 -0
- package/src/network/gossip/gossipsub.ts +11 -0
- package/src/network/interface.ts +2 -0
- package/src/network/libp2p/index.ts +5 -1
- package/src/network/network.ts +5 -0
- package/src/network/options.ts +2 -0
- package/src/network/peers/peerManager.ts +28 -3
- package/src/network/peers/utils/assertPeerRelevance.ts +39 -23
- package/src/network/peers/utils/prioritizePeers.ts +14 -22
- package/src/network/processor/gossipHandlers.ts +29 -18
- package/src/node/nodejs.ts +40 -21
- package/src/node/options.ts +5 -1
- package/src/sync/sync.ts +65 -4
- package/src/util/file.ts +8 -9
- package/src/util/sszBytes.ts +40 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {ForkName, isForkPostFulu} from "@lodestar/params";
|
|
2
|
-
import {
|
|
1
|
+
import {ForkName, GENESIS_EPOCH, isForkPostFulu} from "@lodestar/params";
|
|
2
|
+
import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
3
|
+
import {Epoch, ForkDigest, Root, Slot, Status, fulu, ssz} from "@lodestar/types";
|
|
3
4
|
import {toHex, toRootHex} from "@lodestar/utils";
|
|
4
5
|
|
|
5
6
|
// TODO: Why this value? (From Lighthouse)
|
|
@@ -9,6 +10,8 @@ export enum IrrelevantPeerCode {
|
|
|
9
10
|
INCOMPATIBLE_FORKS = "IRRELEVANT_PEER_INCOMPATIBLE_FORKS",
|
|
10
11
|
DIFFERENT_CLOCKS = "IRRELEVANT_PEER_DIFFERENT_CLOCKS",
|
|
11
12
|
DIFFERENT_FINALIZED = "IRRELEVANT_PEER_DIFFERENT_FINALIZED",
|
|
13
|
+
FINALIZED_EPOCH_TOO_HIGH = "IRRELEVANT_PEER_FINALIZED_EPOCH_TOO_HIGH",
|
|
14
|
+
INVALID_FINALIZED_ROOT = "IRRELEVANT_PEER_INVALID_FINALIZED_ROOT",
|
|
12
15
|
NO_EARLIEST_AVAILABLE_SLOT = "NO_EARLIEST_AVAILABLE_SLOT",
|
|
13
16
|
}
|
|
14
17
|
|
|
@@ -16,6 +19,8 @@ type IrrelevantPeerType =
|
|
|
16
19
|
| {code: IrrelevantPeerCode.INCOMPATIBLE_FORKS; ours: ForkDigest; theirs: ForkDigest}
|
|
17
20
|
| {code: IrrelevantPeerCode.DIFFERENT_CLOCKS; slotDiff: number}
|
|
18
21
|
| {code: IrrelevantPeerCode.NO_EARLIEST_AVAILABLE_SLOT}
|
|
22
|
+
| {code: IrrelevantPeerCode.FINALIZED_EPOCH_TOO_HIGH; finalizedEpoch: Epoch; maxFinalizedEpoch: Epoch}
|
|
23
|
+
| {code: IrrelevantPeerCode.INVALID_FINALIZED_ROOT}
|
|
19
24
|
| {code: IrrelevantPeerCode.DIFFERENT_FINALIZED; expectedRoot: Root; remoteRoot: Root};
|
|
20
25
|
|
|
21
26
|
/**
|
|
@@ -45,29 +50,35 @@ export function assertPeerRelevance(
|
|
|
45
50
|
return {code: IrrelevantPeerCode.DIFFERENT_CLOCKS, slotDiff};
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
// A checkpoint can be finalized at the earliest at the start of epoch + 2
|
|
54
|
+
const maxFinalizedEpoch = Math.max(
|
|
55
|
+
GENESIS_EPOCH,
|
|
56
|
+
computeEpochAtSlot(Math.max(currentSlot, 0) + FUTURE_SLOT_TOLERANCE) - 2
|
|
57
|
+
);
|
|
58
|
+
if (remote.finalizedEpoch > maxFinalizedEpoch) {
|
|
59
|
+
return {
|
|
60
|
+
code: IrrelevantPeerCode.FINALIZED_EPOCH_TOO_HIGH,
|
|
61
|
+
finalizedEpoch: remote.finalizedEpoch,
|
|
62
|
+
maxFinalizedEpoch,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (remote.finalizedEpoch > GENESIS_EPOCH && isZeroRoot(remote.finalizedRoot)) {
|
|
67
|
+
return {code: IrrelevantPeerCode.INVALID_FINALIZED_ROOT};
|
|
68
|
+
}
|
|
51
69
|
|
|
70
|
+
// The remote's finalized epoch is equal to ours, but the block root is different to the one in our chain.
|
|
71
|
+
// Therefore, the node is on a different chain and we should not communicate with them.
|
|
52
72
|
if (
|
|
53
|
-
remote.finalizedEpoch
|
|
54
|
-
!isZeroRoot(
|
|
55
|
-
!
|
|
73
|
+
remote.finalizedEpoch === local.finalizedEpoch &&
|
|
74
|
+
!isZeroRoot(local.finalizedRoot) &&
|
|
75
|
+
!ssz.Root.equals(remote.finalizedRoot, local.finalizedRoot)
|
|
56
76
|
) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const expectedRoot = remote.finalizedEpoch === local.finalizedEpoch ? local.finalizedRoot : null;
|
|
63
|
-
|
|
64
|
-
if (expectedRoot !== null && !ssz.Root.equals(remoteRoot, expectedRoot)) {
|
|
65
|
-
return {
|
|
66
|
-
code: IrrelevantPeerCode.DIFFERENT_FINALIZED,
|
|
67
|
-
expectedRoot: expectedRoot, // forkChoice returns Tree BranchNode which the logger prints as {}
|
|
68
|
-
remoteRoot: remoteRoot,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
77
|
+
return {
|
|
78
|
+
code: IrrelevantPeerCode.DIFFERENT_FINALIZED,
|
|
79
|
+
expectedRoot: local.finalizedRoot,
|
|
80
|
+
remoteRoot: remote.finalizedRoot,
|
|
81
|
+
};
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
if (isForkPostFulu(forkName) && (remote as fulu.Status).earliestAvailableSlot === undefined) {
|
|
@@ -76,7 +87,8 @@ export function assertPeerRelevance(
|
|
|
76
87
|
};
|
|
77
88
|
}
|
|
78
89
|
|
|
79
|
-
// Note:
|
|
90
|
+
// Note: for a behind finalized checkpoint, we validate it more on the main thread against our history,
|
|
91
|
+
// see BeaconSync.verifyPastFinalizedCheckpoint
|
|
80
92
|
return null;
|
|
81
93
|
}
|
|
82
94
|
|
|
@@ -93,6 +105,10 @@ export function renderIrrelevantPeerType(type: IrrelevantPeerType): string {
|
|
|
93
105
|
return `DIFFERENT_CLOCKS slotDiff: ${type.slotDiff}`;
|
|
94
106
|
case IrrelevantPeerCode.DIFFERENT_FINALIZED:
|
|
95
107
|
return `DIFFERENT_FINALIZED root: ${toRootHex(type.remoteRoot)} expected: ${toRootHex(type.expectedRoot)}`;
|
|
108
|
+
case IrrelevantPeerCode.FINALIZED_EPOCH_TOO_HIGH:
|
|
109
|
+
return `FINALIZED_EPOCH_TOO_HIGH finalizedEpoch: ${type.finalizedEpoch} max: ${type.maxFinalizedEpoch}`;
|
|
110
|
+
case IrrelevantPeerCode.INVALID_FINALIZED_ROOT:
|
|
111
|
+
return "INVALID_FINALIZED_ROOT zero root for a non-genesis finalized epoch";
|
|
96
112
|
case IrrelevantPeerCode.NO_EARLIEST_AVAILABLE_SLOT:
|
|
97
113
|
return "No earliestAvailableSlot announced via peer Status";
|
|
98
114
|
}
|
|
@@ -49,6 +49,8 @@ const syncnetsZero = BitArray.fromBitLen(SYNC_COMMITTEE_SUBNET_COUNT);
|
|
|
49
49
|
|
|
50
50
|
type SubnetDiscvQuery = {subnet: SubnetID; toSlot: number; maxPeersToDiscover: number};
|
|
51
51
|
|
|
52
|
+
export type RequestedSubnetWithPeerCount = RequestedSubnet & {peerCount: number};
|
|
53
|
+
|
|
52
54
|
/**
|
|
53
55
|
* A map of das custody group index to maxPeersToDiscover
|
|
54
56
|
*/
|
|
@@ -145,8 +147,8 @@ export function prioritizePeers(
|
|
|
145
147
|
samplingGroups: CustodyIndex[] | null;
|
|
146
148
|
score: number;
|
|
147
149
|
}[],
|
|
148
|
-
activeAttnets:
|
|
149
|
-
activeSyncnets:
|
|
150
|
+
activeAttnets: RequestedSubnetWithPeerCount[],
|
|
151
|
+
activeSyncnets: RequestedSubnetWithPeerCount[],
|
|
150
152
|
samplingGroups: CustodyIndex[] | undefined,
|
|
151
153
|
opts: PrioritizePeersOpts,
|
|
152
154
|
config: ChainConfig,
|
|
@@ -231,8 +233,8 @@ export function prioritizePeers(
|
|
|
231
233
|
*/
|
|
232
234
|
function requestSubnetPeers(
|
|
233
235
|
connectedPeers: PeerInfo[],
|
|
234
|
-
activeAttnets:
|
|
235
|
-
activeSyncnets:
|
|
236
|
+
activeAttnets: RequestedSubnetWithPeerCount[],
|
|
237
|
+
activeSyncnets: RequestedSubnetWithPeerCount[],
|
|
236
238
|
ourSamplingGroups: CustodyIndex[] | undefined,
|
|
237
239
|
opts: PrioritizePeersOpts,
|
|
238
240
|
config: ChainConfig,
|
|
@@ -252,54 +254,44 @@ function requestSubnetPeers(
|
|
|
252
254
|
|
|
253
255
|
// attnets, do we need queries for more peers
|
|
254
256
|
if (activeAttnets.length > 0) {
|
|
255
|
-
/** Map of peers per subnet, peer may be in multiple arrays */
|
|
256
|
-
const peersPerSubnet = new Map<number, number>();
|
|
257
|
-
|
|
258
257
|
for (const peer of connectedPeers) {
|
|
259
258
|
const trueBitIndices = peer.attnetsTrueBitIndices;
|
|
260
259
|
let dutyCount = 0;
|
|
261
260
|
for (const {subnet} of activeAttnets) {
|
|
262
261
|
if (trueBitIndices.includes(subnet)) {
|
|
263
262
|
dutyCount += 1;
|
|
264
|
-
peersPerSubnet.set(subnet, 1 + (peersPerSubnet.get(subnet) ?? 0));
|
|
265
263
|
}
|
|
266
264
|
}
|
|
267
265
|
dutiesByPeer.set(peer, dutyCount);
|
|
268
266
|
}
|
|
269
267
|
|
|
270
|
-
for (const {subnet, toSlot} of activeAttnets) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (peersInSubnet < targetSubnetPeers) {
|
|
268
|
+
for (const {subnet, toSlot, peerCount} of activeAttnets) {
|
|
269
|
+
metrics?.peerManager.peersPerActiveSubnet.observe({type: SubnetType.attnets}, peerCount);
|
|
270
|
+
if (peerCount < targetSubnetPeers) {
|
|
274
271
|
// We need more peers
|
|
275
|
-
attnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers -
|
|
272
|
+
attnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers - peerCount});
|
|
276
273
|
}
|
|
277
274
|
}
|
|
278
275
|
}
|
|
279
276
|
|
|
280
277
|
// syncnets, do we need queries for more peers
|
|
281
278
|
if (activeSyncnets.length > 0) {
|
|
282
|
-
/** Map of peers per subnet, peer may be in multiple arrays */
|
|
283
|
-
const peersPerSubnet = new Map<number, number>();
|
|
284
|
-
|
|
285
279
|
for (const peer of connectedPeers) {
|
|
286
280
|
const trueBitIndices = peer.syncnetsTrueBitIndices;
|
|
287
281
|
let dutyCount = dutiesByPeer.get(peer) ?? 0;
|
|
288
282
|
for (const {subnet} of activeSyncnets) {
|
|
289
283
|
if (trueBitIndices.includes(subnet)) {
|
|
290
284
|
dutyCount += 1;
|
|
291
|
-
peersPerSubnet.set(subnet, 1 + (peersPerSubnet.get(subnet) ?? 0));
|
|
292
285
|
}
|
|
293
286
|
}
|
|
294
287
|
dutiesByPeer.set(peer, dutyCount);
|
|
295
288
|
}
|
|
296
289
|
|
|
297
|
-
for (const {subnet, toSlot} of activeSyncnets) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (peersInSubnet < targetSubnetPeers) {
|
|
290
|
+
for (const {subnet, toSlot, peerCount} of activeSyncnets) {
|
|
291
|
+
metrics?.peerManager.peersPerActiveSubnet.observe({type: SubnetType.syncnets}, peerCount);
|
|
292
|
+
if (peerCount < targetSubnetPeers) {
|
|
301
293
|
// We need more peers
|
|
302
|
-
syncnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers -
|
|
294
|
+
syncnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers - peerCount});
|
|
303
295
|
}
|
|
304
296
|
}
|
|
305
297
|
}
|
|
@@ -66,7 +66,10 @@ import {
|
|
|
66
66
|
validateGossipGloasDataColumnSidecar,
|
|
67
67
|
} from "../../chain/validation/dataColumnSidecar.js";
|
|
68
68
|
import {validateGossipExecutionPayloadBid} from "../../chain/validation/executionPayloadBid.js";
|
|
69
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
validateGossipExecutionPayloadEnvelope,
|
|
71
|
+
validateGossipExecutionPayloadEnvelopeSerialized,
|
|
72
|
+
} from "../../chain/validation/executionPayloadEnvelope.js";
|
|
70
73
|
import {InclusionListValidationResult, validateGossipInclusionList} from "../../chain/validation/inclusionList.js";
|
|
71
74
|
import {
|
|
72
75
|
AggregateAndProofValidationResult,
|
|
@@ -705,10 +708,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
705
708
|
|
|
706
709
|
// Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
|
|
707
710
|
// which is all the information we need so there is no reason to delay until execution payload arrives
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
// Columns fetched here feed payloadInput.addColumn, which resolves waitForAllData for any
|
|
711
|
-
// in-flight importExecutionPayload. No processExecutionPayload trigger needed from this path.
|
|
711
|
+
// Columns fetched here feed payloadInput.addColumn, which resolves the waitForAllData of a
|
|
712
|
+
// pending payload import. No processExecutionPayload trigger needed from this path.
|
|
712
713
|
chain.getBlobsTracker.triggerGetBlobs(payloadInput);
|
|
713
714
|
}
|
|
714
715
|
})
|
|
@@ -929,7 +930,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
929
930
|
}
|
|
930
931
|
|
|
931
932
|
// NOTE: we do NOT call chain.processExecutionPayload here. That is triggered only by
|
|
932
|
-
// envelope arrival (gossip or API).
|
|
933
|
+
// envelope arrival (gossip or API). A pending payload import is awaiting
|
|
933
934
|
// payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
|
|
934
935
|
|
|
935
936
|
scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
|
|
@@ -1278,6 +1279,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1278
1279
|
seenTimestampSec,
|
|
1279
1280
|
}: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
|
|
1280
1281
|
const {serializedData} = gossipData;
|
|
1282
|
+
validateGossipExecutionPayloadEnvelopeSerialized(chain, serializedData);
|
|
1281
1283
|
const signedEnvelope = sszDeserialize(topic, serializedData);
|
|
1282
1284
|
const envelope = signedEnvelope.message;
|
|
1283
1285
|
|
|
@@ -1330,18 +1332,22 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1330
1332
|
});
|
|
1331
1333
|
}
|
|
1332
1334
|
|
|
1335
|
+
// Add the envelope in the same tick as validation, otherwise a by-root download that completes
|
|
1336
|
+
// before the next event loop sees no envelope, validates it again and adds it first
|
|
1337
|
+
if (!payloadInput.hasPayloadEnvelope()) {
|
|
1338
|
+
chain.serializedCache.set(signedEnvelope, serializedData);
|
|
1339
|
+
|
|
1340
|
+
payloadInput.addPayloadEnvelope({
|
|
1341
|
+
envelope: signedEnvelope,
|
|
1342
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
1343
|
+
seenTimestampSec,
|
|
1344
|
+
peerIdStr,
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1333
1348
|
// Handler - deferred to next event loop so the validation result propagates first
|
|
1334
1349
|
callInNextEventLoop(() => {
|
|
1335
1350
|
try {
|
|
1336
|
-
chain.serializedCache.set(signedEnvelope, serializedData);
|
|
1337
|
-
|
|
1338
|
-
payloadInput.addPayloadEnvelope({
|
|
1339
|
-
envelope: signedEnvelope,
|
|
1340
|
-
source: PayloadEnvelopeInputSource.gossip,
|
|
1341
|
-
seenTimestampSec,
|
|
1342
|
-
peerIdStr,
|
|
1343
|
-
});
|
|
1344
|
-
|
|
1345
1351
|
scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
|
|
1346
1352
|
|
|
1347
1353
|
chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
|
|
@@ -1351,7 +1357,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1351
1357
|
blockRoot: blockRootHex,
|
|
1352
1358
|
});
|
|
1353
1359
|
|
|
1354
|
-
|
|
1360
|
+
// Signature is verified above only for this envelope, one added by another source is verified at import
|
|
1361
|
+
const validSignature = payloadInput.getPayloadEnvelope() === signedEnvelope;
|
|
1362
|
+
chain.processExecutionPayload(payloadInput, {validSignature}).catch((e) => {
|
|
1355
1363
|
// Adjust verbosity based on error type
|
|
1356
1364
|
let logLevel: LogLevel;
|
|
1357
1365
|
|
|
@@ -1369,11 +1377,14 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1369
1377
|
// Errors might indicate an issue with our node or the connected EL client
|
|
1370
1378
|
logLevel = LogLevel.error;
|
|
1371
1379
|
break;
|
|
1372
|
-
// INVALID_SIGNATURE
|
|
1380
|
+
// INVALID_SIGNATURE only happens for an envelope added by another source
|
|
1373
1381
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1374
1382
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1375
1383
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1376
|
-
|
|
1384
|
+
// Penalize only when the stored envelope is the one this peer sent, not one added by another source
|
|
1385
|
+
if (validSignature) {
|
|
1386
|
+
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
|
|
1387
|
+
}
|
|
1377
1388
|
// The builder may have signed another envelope that is valid, keeping this one would make
|
|
1378
1389
|
// by-root and range sync reuse it and never import the payload
|
|
1379
1390
|
chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
package/src/node/nodejs.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {setMaxListeners} from "node:events";
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Registry} from "prom-client";
|
|
4
|
+
import {hasher} from "@chainsafe/persistent-merkle-tree";
|
|
5
|
+
import {BeaconApiMethods} from "@lodestar/api/beacon/server";
|
|
6
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
7
|
+
import type {LoggerNode} from "@lodestar/logger/node";
|
|
4
8
|
import {
|
|
5
9
|
init as initNativeMetrics,
|
|
6
10
|
registerLocalValidator as registerNativeLocalValidator,
|
|
7
11
|
scrapeMetrics as scrapeNativeMetrics,
|
|
8
12
|
unregisterLocalValidator as unregisterNativeLocalValidator,
|
|
9
|
-
} from "@
|
|
10
|
-
import {type PubkeyCache} from "@
|
|
11
|
-
import {hasher} from "@chainsafe/persistent-merkle-tree";
|
|
12
|
-
import {BeaconApiMethods} from "@lodestar/api/beacon/server";
|
|
13
|
-
import {BeaconConfig} from "@lodestar/config";
|
|
14
|
-
import type {LoggerNode} from "@lodestar/logger/node";
|
|
13
|
+
} from "@lodestar/native/metrics";
|
|
14
|
+
import {type PubkeyCache} from "@lodestar/native/pubkeys";
|
|
15
15
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
16
16
|
import {
|
|
17
17
|
IBeaconStateView,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
isStatePostGloas,
|
|
21
21
|
} from "@lodestar/state-transition";
|
|
22
22
|
import {Slot} from "@lodestar/types";
|
|
23
|
-
import {sleep, toRootHex} from "@lodestar/utils";
|
|
23
|
+
import {defer, sleep, toRootHex} from "@lodestar/utils";
|
|
24
24
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
25
25
|
import {BeaconRestApiServer, getApi} from "../api/index.js";
|
|
26
26
|
import {getDataColumnSidecarsMinEpoch} from "../chain/archiveStore/utils/archiveBlocks.js";
|
|
@@ -117,6 +117,7 @@ export class BeaconNode {
|
|
|
117
117
|
|
|
118
118
|
status: BeaconNodeStatus;
|
|
119
119
|
private controller?: AbortController;
|
|
120
|
+
private closePromise: Promise<void> | undefined;
|
|
120
121
|
|
|
121
122
|
constructor({
|
|
122
123
|
opts,
|
|
@@ -297,6 +298,7 @@ export class BeaconNode {
|
|
|
297
298
|
: undefined,
|
|
298
299
|
builderApiClientOpts: {
|
|
299
300
|
timeout: opts.executionBuilder.timeout,
|
|
301
|
+
bidDeadline: opts.executionBuilder.bidDeadline,
|
|
300
302
|
// Sent with all builder api requests, unless the node runs in private mode
|
|
301
303
|
userAgent: opts.executionBuilder.userAgent,
|
|
302
304
|
},
|
|
@@ -392,23 +394,40 @@ export class BeaconNode {
|
|
|
392
394
|
/**
|
|
393
395
|
* Stop beacon node and its sub-components.
|
|
394
396
|
*/
|
|
395
|
-
|
|
396
|
-
if (this.
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
397
|
+
close(): Promise<void> {
|
|
398
|
+
if (this.closePromise !== undefined) return this.closePromise;
|
|
399
|
+
const completion = defer<void>();
|
|
400
|
+
this.closePromise = completion.promise;
|
|
401
|
+
this.status = BeaconNodeStatus.closing;
|
|
402
|
+
void this.closeInternal().then(completion.resolve, completion.reject);
|
|
403
|
+
return this.closePromise;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private async closeInternal(): Promise<void> {
|
|
407
|
+
const errors: unknown[] = [];
|
|
408
|
+
for (const cleanup of [
|
|
409
|
+
() => this.sync.close(),
|
|
410
|
+
() => this.restApi?.close(),
|
|
411
|
+
() => this.network.close(),
|
|
412
|
+
() => this.metricsServer?.close(),
|
|
413
|
+
() => this.monitoring?.close(),
|
|
414
|
+
() => this.chain.persistToDisk(),
|
|
415
|
+
() => this.chain.close(),
|
|
405
416
|
// Abort signal last: close() calls above clear intervals/timeouts so no new
|
|
406
417
|
// operations get scheduled. If we aborted first, a still-pending interval could
|
|
407
418
|
// fire and schedule a new operation after abort, leaving it stuck and delaying shutdown.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
419
|
+
() => this.controller?.abort(),
|
|
420
|
+
() => sleep(DELAY_BEFORE_CLOSING_DB_MS),
|
|
421
|
+
() => this.db.close(),
|
|
422
|
+
]) {
|
|
423
|
+
try {
|
|
424
|
+
await cleanup();
|
|
425
|
+
} catch (error) {
|
|
426
|
+
errors.push(error);
|
|
427
|
+
}
|
|
412
428
|
}
|
|
429
|
+
this.status = BeaconNodeStatus.closed;
|
|
430
|
+
if (errors.length === 1) throw errors[0];
|
|
431
|
+
if (errors.length > 1) throw new AggregateError(errors, "Beacon node cleanup failed");
|
|
413
432
|
}
|
|
414
433
|
}
|
package/src/node/options.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import {BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS} from "../api/impl/validator/index.js";
|
|
1
2
|
import {ApiOptions, defaultApiOptions} from "../api/options.js";
|
|
2
3
|
import {ArchiveMode, DEFAULT_ARCHIVE_MODE, IChainOptions, defaultChainOptions} from "../chain/options.js";
|
|
3
4
|
import {ValidatorMonitorOpts, defaultValidatorMonitorOpts} from "../chain/validatorMonitor.js";
|
|
4
5
|
import {DatabaseOptions, defaultDbOptions} from "../db/options.js";
|
|
6
|
+
import {BUILDER_BID_LAG_BUFFER_MS} from "../execution/builder/apiClient.js";
|
|
5
7
|
import {
|
|
6
8
|
EngineApiMode,
|
|
7
9
|
ExecutionBuilderOpts,
|
|
@@ -20,9 +22,11 @@ import {SyncOptions, defaultSyncOptions} from "../sync/options.js";
|
|
|
20
22
|
// Re-export so the CLI doesn't need to depend on lodestar-api
|
|
21
23
|
export {allNamespaces} from "../api/rest/index.js";
|
|
22
24
|
export type {EngineApiMode};
|
|
23
|
-
// Re-export to use as default values in CLI args
|
|
25
|
+
// Re-export to use as default values and for validation in CLI args
|
|
24
26
|
export {
|
|
25
27
|
ArchiveMode,
|
|
28
|
+
BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS,
|
|
29
|
+
BUILDER_BID_LAG_BUFFER_MS,
|
|
26
30
|
DEFAULT_ARCHIVE_MODE,
|
|
27
31
|
defaultExecutionBuilderHttpOpts,
|
|
28
32
|
defaultExecutionEngineHttpOpts,
|
package/src/sync/sync.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
|
-
import {
|
|
2
|
+
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
|
+
import {Slot, Status} from "@lodestar/types";
|
|
3
4
|
import {Logger, toRootHex} from "@lodestar/utils";
|
|
4
5
|
import {IBeaconChain} from "../chain/index.js";
|
|
5
|
-
import {GENESIS_SLOT} from "../constants/constants.js";
|
|
6
|
+
import {GENESIS_EPOCH, GENESIS_SLOT} from "../constants/constants.js";
|
|
7
|
+
import {GoodByeReasonCode} from "../constants/index.js";
|
|
8
|
+
import {IBeaconDb} from "../db/index.js";
|
|
6
9
|
import {ExecutionEngineState} from "../execution/index.js";
|
|
7
10
|
import {Metrics} from "../metrics/index.js";
|
|
8
|
-
import {INetwork, NetworkEvent, NetworkEventData} from "../network/index.js";
|
|
11
|
+
import {INetwork, NetworkEvent, NetworkEventData, PeerAction} from "../network/index.js";
|
|
9
12
|
import {ClockEvent} from "../util/clock.js";
|
|
10
13
|
import {isOptimisticBlock} from "../util/forkChoice.js";
|
|
14
|
+
import {PeerIdStr} from "../util/peerId.js";
|
|
11
15
|
import {MIN_EPOCH_TO_START_GOSSIP} from "./constants.js";
|
|
12
16
|
import {IBeaconSync, SyncChainDebugState, SyncModules, SyncState, SyncingStatus, syncStateMetric} from "./interface.js";
|
|
13
17
|
import {SyncOptions} from "./options.js";
|
|
@@ -19,6 +23,7 @@ export class BeaconSync implements IBeaconSync {
|
|
|
19
23
|
private readonly logger: Logger;
|
|
20
24
|
private readonly network: INetwork;
|
|
21
25
|
private readonly chain: IBeaconChain;
|
|
26
|
+
private readonly db: IBeaconDb;
|
|
22
27
|
private readonly metrics: Metrics | null;
|
|
23
28
|
private readonly opts: SyncOptions;
|
|
24
29
|
|
|
@@ -30,10 +35,11 @@ export class BeaconSync implements IBeaconSync {
|
|
|
30
35
|
private readonly slotImportTolerance: Slot;
|
|
31
36
|
|
|
32
37
|
constructor(opts: SyncOptions, modules: SyncModules) {
|
|
33
|
-
const {config, chain, metrics, network, logger} = modules;
|
|
38
|
+
const {config, chain, db, metrics, network, logger} = modules;
|
|
34
39
|
this.opts = opts;
|
|
35
40
|
this.network = network;
|
|
36
41
|
this.chain = chain;
|
|
42
|
+
this.db = db;
|
|
37
43
|
this.metrics = metrics;
|
|
38
44
|
this.logger = logger;
|
|
39
45
|
this.rangeSync = new RangeSync(modules, opts);
|
|
@@ -192,6 +198,11 @@ export class BeaconSync implements IBeaconSync {
|
|
|
192
198
|
*/
|
|
193
199
|
private addPeer = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
|
|
194
200
|
const localStatus = this.chain.getStatus();
|
|
201
|
+
// Genesis and equal finalized epochs are already checked by assertPeerRelevance in the network worker
|
|
202
|
+
if (data.status.finalizedEpoch > GENESIS_EPOCH && data.status.finalizedEpoch < localStatus.finalizedEpoch) {
|
|
203
|
+
void this.verifyPastFinalizedCheckpoint(data.peer, data.status);
|
|
204
|
+
}
|
|
205
|
+
|
|
195
206
|
const syncType = getPeerSyncType(localStatus, data.status, this.chain.forkChoice, this.slotImportTolerance);
|
|
196
207
|
this.logger.verbose("Peer sync type classified", {
|
|
197
208
|
peer: data.peer,
|
|
@@ -216,6 +227,56 @@ export class BeaconSync implements IBeaconSync {
|
|
|
216
227
|
this.updateSyncState();
|
|
217
228
|
};
|
|
218
229
|
|
|
230
|
+
/**
|
|
231
|
+
* A behind peer's finalized checkpoint must be the canonical block in effect at the start slot of its epoch
|
|
232
|
+
*/
|
|
233
|
+
async verifyPastFinalizedCheckpoint(peer: PeerIdStr, remote: Status): Promise<void> {
|
|
234
|
+
const startSlot = computeStartSlotAtEpoch(remote.finalizedEpoch);
|
|
235
|
+
const root = toRootHex(remote.finalizedRoot);
|
|
236
|
+
try {
|
|
237
|
+
let reason: string | null = null;
|
|
238
|
+
// After a finality jump, finalized blocks stay in fork choice until the archiver migrates them.
|
|
239
|
+
// Fork choice is pruned only after archiving, so a miss here means the db already has this range.
|
|
240
|
+
const canonicalBlock = this.chain.forkChoice.getCanonicalBlockClosestLteSlot(startSlot);
|
|
241
|
+
if (canonicalBlock !== null) {
|
|
242
|
+
if (canonicalBlock.blockRoot === root) return;
|
|
243
|
+
reason = "finalized_root_not_canonical";
|
|
244
|
+
} else {
|
|
245
|
+
const slot = await this.db.blockArchive.getSlotByRoot(remote.finalizedRoot);
|
|
246
|
+
if (slot !== null && slot > startSlot) {
|
|
247
|
+
reason = "finalized_root_after_epoch_start";
|
|
248
|
+
} else if (slot !== startSlot) {
|
|
249
|
+
const childSlot = await this.db.blockArchive.getSlotByParentRoot(remote.finalizedRoot);
|
|
250
|
+
if (childSlot !== null) {
|
|
251
|
+
if (childSlot <= startSlot) reason = "finalized_root_not_in_effect_at_epoch_start";
|
|
252
|
+
} else if (slot === null && startSlot > this.chain.earliestAvailableSlot) {
|
|
253
|
+
// childSlot == null too, neither the root nor a child is canonical
|
|
254
|
+
// or they are canonical and both pruned but peer's finalized epoch starts after earliestAvailableSlot
|
|
255
|
+
reason = "unknown_finalized_root";
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (reason !== null) {
|
|
261
|
+
this.logger.debug("Peer finalized checkpoint not in our chain", {
|
|
262
|
+
peer,
|
|
263
|
+
finalizedEpoch: remote.finalizedEpoch,
|
|
264
|
+
root,
|
|
265
|
+
reason,
|
|
266
|
+
});
|
|
267
|
+
this.metrics?.syncPeerFinalizedCheckpointMismatch.inc({reason});
|
|
268
|
+
this.network.reportPeer(peer, PeerAction.Fatal, "PeerFinalizedCheckpointMismatch");
|
|
269
|
+
await this.network.goodbyeAndDisconnect(peer, GoodByeReasonCode.IRRELEVANT_NETWORK);
|
|
270
|
+
}
|
|
271
|
+
} catch (e) {
|
|
272
|
+
this.logger.debug(
|
|
273
|
+
"Error verifying peer finalized checkpoint",
|
|
274
|
+
{peer, finalizedEpoch: remote.finalizedEpoch, root},
|
|
275
|
+
e as Error
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
219
280
|
/**
|
|
220
281
|
* Must be called by libp2p when a peer is removed from the peer manager
|
|
221
282
|
*/
|
package/src/util/file.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {promisify} from "node:util";
|
|
1
|
+
import {readFile as fsReadFile, mkdir, readdir, stat, unlink, writeFile} from "node:fs/promises";
|
|
3
2
|
|
|
4
3
|
/** Ensure a directory exists */
|
|
5
4
|
export async function ensureDir(path: string): Promise<void> {
|
|
6
5
|
try {
|
|
7
|
-
await
|
|
6
|
+
await stat(path);
|
|
8
7
|
} catch (_) {
|
|
9
8
|
// not exists
|
|
10
|
-
await
|
|
9
|
+
await mkdir(path, {recursive: true});
|
|
11
10
|
}
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
/** Write data to a file if it does not exist */
|
|
15
14
|
export async function writeIfNotExist(filepath: string, bytes: Uint8Array): Promise<boolean> {
|
|
16
15
|
try {
|
|
17
|
-
await
|
|
16
|
+
await stat(filepath);
|
|
18
17
|
return false;
|
|
19
18
|
// file exists, do nothing
|
|
20
19
|
} catch (_) {
|
|
21
20
|
// not exists
|
|
22
|
-
await
|
|
21
|
+
await writeFile(filepath, bytes);
|
|
23
22
|
return true;
|
|
24
23
|
}
|
|
25
24
|
}
|
|
@@ -27,7 +26,7 @@ export async function writeIfNotExist(filepath: string, bytes: Uint8Array): Prom
|
|
|
27
26
|
/** Remove a file if it exists */
|
|
28
27
|
export async function removeFile(path: string): Promise<boolean> {
|
|
29
28
|
try {
|
|
30
|
-
await
|
|
29
|
+
await unlink(path);
|
|
31
30
|
return true;
|
|
32
31
|
} catch (_) {
|
|
33
32
|
// may not exists
|
|
@@ -37,7 +36,7 @@ export async function removeFile(path: string): Promise<boolean> {
|
|
|
37
36
|
|
|
38
37
|
export async function readFile(path: string): Promise<Uint8Array | null> {
|
|
39
38
|
try {
|
|
40
|
-
return await
|
|
39
|
+
return await fsReadFile(path);
|
|
41
40
|
} catch (_) {
|
|
42
41
|
return null;
|
|
43
42
|
}
|
|
@@ -45,7 +44,7 @@ export async function readFile(path: string): Promise<Uint8Array | null> {
|
|
|
45
44
|
|
|
46
45
|
export async function readFileNames(folderPath: string): Promise<string[]> {
|
|
47
46
|
try {
|
|
48
|
-
return await
|
|
47
|
+
return await readdir(folderPath);
|
|
49
48
|
} catch (_) {
|
|
50
49
|
return [];
|
|
51
50
|
}
|
package/src/util/sszBytes.ts
CHANGED
|
@@ -652,6 +652,46 @@ export function getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(data: U
|
|
|
652
652
|
return "0x" + blockRootBuf.toString("hex");
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
// transactions offset within ExecutionPayload fixed portion, see SLOT_NUMBER_OFFSET_IN_EXECUTION_PAYLOAD:
|
|
656
|
+
// everything up to and including blockHash(32) = 504, followed by withdrawals_offset(4)
|
|
657
|
+
const PAYLOAD_START_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
658
|
+
ENVELOPE_START_IN_SIGNED + EXECUTION_PAYLOAD_ENVELOPE_FIXED_SIZE; // 180
|
|
659
|
+
const TRANSACTIONS_OFFSET_POSITION_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
660
|
+
PAYLOAD_START_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + 504; // 684
|
|
661
|
+
const WITHDRAWALS_OFFSET_POSITION_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
662
|
+
TRANSACTIONS_OFFSET_POSITION_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + 4; // 688
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Transaction count of the payload, read from the SSZ offsets without deserializing the envelope.
|
|
666
|
+
* Returns null on malformed offsets, deserialization reports the exact error.
|
|
667
|
+
*/
|
|
668
|
+
export function getTransactionCountFromExecutionPayloadEnvelopeSerialized(data: Uint8Array): number | null {
|
|
669
|
+
if (data.length < WITHDRAWALS_OFFSET_POSITION_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + 4) {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
674
|
+
const transactionsOffset = view.getUint32(TRANSACTIONS_OFFSET_POSITION_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE, true);
|
|
675
|
+
const withdrawalsOffset = view.getUint32(WITHDRAWALS_OFFSET_POSITION_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE, true);
|
|
676
|
+
if (transactionsOffset > withdrawalsOffset) {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
if (transactionsOffset === withdrawalsOffset) {
|
|
680
|
+
return 0;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const transactionsStart = PAYLOAD_START_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + transactionsOffset;
|
|
684
|
+
if (transactionsStart + 4 > data.length) {
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
// first offset of a non-empty list is the size of its offset table, 4 bytes per element
|
|
688
|
+
const firstTransactionOffset = view.getUint32(transactionsStart, true);
|
|
689
|
+
if (firstTransactionOffset % 4 !== 0 || firstTransactionOffset > withdrawalsOffset - transactionsOffset) {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
return firstTransactionOffset / 4;
|
|
693
|
+
}
|
|
694
|
+
|
|
655
695
|
/**
|
|
656
696
|
* BeaconState of all forks (up until Electra, check with new forks)
|
|
657
697
|
* class BeaconState(Container):
|