@lodestar/beacon-node 1.49.0 → 1.50.0-dev.023ad6c012
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 -1
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +4 -4
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +28 -0
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +26 -3
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +4 -3
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +4 -0
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/chain.d.ts +5 -4
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +14 -16
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
- package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.js +2 -0
- package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +3 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +2 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/initState.d.ts +2 -9
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +5 -38
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.d.ts +5 -4
- package/lib/chain/opPools/opPool.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.js +62 -18
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +4 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +11 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +5 -2
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts +4 -4
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +3 -0
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.d.ts +3 -3
- package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +3 -0
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +2 -0
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/repositories/proposerSlashing.d.ts +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
- package/lib/db/repositories/proposerSlashing.js +5 -1
- package/lib/db/repositories/proposerSlashing.js.map +1 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +5 -4
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/jsonRpcHttpClient.d.ts +2 -1
- package/lib/execution/engine/jsonRpcHttpClient.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcHttpClient.js +6 -1
- package/lib/execution/engine/jsonRpcHttpClient.js.map +1 -1
- package/lib/execution/engine/transportMetrics.d.ts +13 -0
- package/lib/execution/engine/transportMetrics.d.ts.map +1 -0
- package/lib/execution/engine/transportMetrics.js +2 -0
- package/lib/execution/engine/transportMetrics.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +1 -1
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +6 -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/network/metadata.d.ts +1 -1
- package/lib/network/metadata.d.ts.map +1 -1
- package/lib/network/metadata.js +17 -17
- package/lib/network/metadata.js.map +1 -1
- package/lib/network/peers/datastore.d.ts +9 -9
- package/lib/network/peers/datastore.d.ts.map +1 -1
- package/lib/network/peers/datastore.js +36 -36
- 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 +7 -4
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +7 -3
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +9 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +77 -10
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/sync/range/batch.d.ts +6 -0
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +10 -0
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +50 -18
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +9 -5
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +88 -46
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/execution.d.ts +11 -11
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +11 -7
- package/lib/util/execution.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +7 -4
- package/src/api/impl/beacon/pool/index.ts +4 -4
- package/src/api/impl/beacon/state/index.ts +36 -0
- package/src/chain/archiveStore/archiveStore.ts +29 -4
- package/src/chain/archiveStore/utils/pruneHistory.ts +6 -4
- package/src/chain/blocks/importBlock.ts +4 -0
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/chain.ts +17 -19
- package/src/chain/errors/envelopeReconstructionError.ts +3 -0
- package/src/chain/errors/gossipValidation.ts +2 -0
- package/src/chain/initState.ts +8 -60
- package/src/chain/interface.ts +2 -2
- package/src/chain/opPools/opPool.ts +91 -18
- package/src/chain/opPools/utils.ts +31 -3
- package/src/chain/prepareNextSlot.ts +6 -3
- package/src/chain/produceBlock/produceBlockBody.ts +8 -1
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
- package/src/chain/validation/attesterSlashing.ts +7 -4
- package/src/chain/validation/proposerSlashing.ts +7 -4
- package/src/db/beacon.ts +2 -0
- package/src/db/repositories/proposerSlashing.ts +6 -1
- package/src/execution/engine/http.ts +6 -4
- package/src/execution/engine/jsonRpcHttpClient.ts +9 -2
- package/src/execution/engine/transportMetrics.ts +9 -0
- package/src/index.ts +1 -1
- package/src/metrics/metrics/beacon.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +10 -0
- package/src/network/metadata.ts +17 -17
- package/src/network/peers/datastore.ts +36 -36
- package/src/network/processor/gossipHandlers.ts +7 -4
- package/src/network/processor/gossipValidatorFn.ts +13 -3
- package/src/network/processor/index.ts +89 -10
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
- package/src/sync/range/batch.ts +11 -0
- package/src/sync/range/range.ts +50 -18
- package/src/sync/unknownBlock.ts +99 -50
- package/src/util/execution.ts +26 -14
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
import {Signature} from "@chainsafe/lodestar-z/blst";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
+
import {BLS_WITHDRAWAL_PREFIX, DOMAIN_BEACON_ATTESTER, DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
|
|
4
|
+
import {IBeaconStateView, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
5
|
+
import {AttesterSlashing, Domain, Slot, capella, phase0} from "@lodestar/types";
|
|
5
6
|
import {AggregateFast, AggregateFastElectra} from "./attestationPool.js";
|
|
6
7
|
|
|
8
|
+
export function getProposerSlashingSignatureDomain(
|
|
9
|
+
config: BeaconConfig,
|
|
10
|
+
stateSlot: Slot,
|
|
11
|
+
slashing: phase0.ProposerSlashing
|
|
12
|
+
): Domain {
|
|
13
|
+
return config.getDomain(stateSlot, DOMAIN_BEACON_PROPOSER, Number(slashing.signedHeader1.message.slot));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getAttesterSlashingSignatureDomains(
|
|
17
|
+
config: BeaconConfig,
|
|
18
|
+
stateSlot: Slot,
|
|
19
|
+
slashing: AttesterSlashing
|
|
20
|
+
): [Domain, Domain] {
|
|
21
|
+
return [
|
|
22
|
+
config.getDomain(
|
|
23
|
+
stateSlot,
|
|
24
|
+
DOMAIN_BEACON_ATTESTER,
|
|
25
|
+
computeStartSlotAtEpoch(Number(slashing.attestation1.data.target.epoch))
|
|
26
|
+
),
|
|
27
|
+
config.getDomain(
|
|
28
|
+
stateSlot,
|
|
29
|
+
DOMAIN_BEACON_ATTESTER,
|
|
30
|
+
computeStartSlotAtEpoch(Number(slashing.attestation2.data.target.epoch))
|
|
31
|
+
),
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
/**
|
|
8
36
|
* Prune a Map indexed by slot to keep the most recent slots, up to `slotsRetained`
|
|
9
37
|
*/
|
|
@@ -244,6 +244,10 @@ export class PrepareNextSlotScheduler {
|
|
|
244
244
|
parentBlockHash = preparedState.latestExecutionPayloadHeader.blockHash;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
// The payload_attributes event must carry the same hashes we send to the EL
|
|
248
|
+
const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice, this.logger);
|
|
249
|
+
const finalizedBlockHash = getFinalizedExecutionBlockHash(this.chain.forkChoice);
|
|
250
|
+
|
|
247
251
|
let payloadAttributes: PayloadAttributes | undefined;
|
|
248
252
|
// If emitPayloadAttributes is true emit a SSE payloadAttributes event for
|
|
249
253
|
// every slot. Without the flag, only emit the event if we are proposing in the next slot.
|
|
@@ -256,6 +260,8 @@ export class PrepareNextSlotScheduler {
|
|
|
256
260
|
prepareSlot,
|
|
257
261
|
parentBlockRoot: fromHex(updatedHead.blockRoot),
|
|
258
262
|
parentBlockHash,
|
|
263
|
+
safeBlockHash,
|
|
264
|
+
finalizedBlockHash,
|
|
259
265
|
feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
260
266
|
});
|
|
261
267
|
this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
|
|
@@ -267,9 +273,6 @@ export class PrepareNextSlotScheduler {
|
|
|
267
273
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
268
274
|
this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
|
|
269
275
|
|
|
270
|
-
const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice, this.logger);
|
|
271
|
-
const finalizedBlockHash = getFinalizedExecutionBlockHash(this.chain.forkChoice);
|
|
272
|
-
|
|
273
276
|
// awaiting here instead of throwing an async call because there is no other task
|
|
274
277
|
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
275
278
|
// try/catch wrapper here.
|
|
@@ -845,6 +845,8 @@ export function getPayloadAttributesForSSE(
|
|
|
845
845
|
prepareSlot,
|
|
846
846
|
parentBlockRoot,
|
|
847
847
|
parentBlockHash,
|
|
848
|
+
safeBlockHash,
|
|
849
|
+
finalizedBlockHash,
|
|
848
850
|
feeRecipient,
|
|
849
851
|
}: {
|
|
850
852
|
/**
|
|
@@ -855,6 +857,8 @@ export function getPayloadAttributesForSSE(
|
|
|
855
857
|
prepareSlot: Slot;
|
|
856
858
|
parentBlockRoot: Root;
|
|
857
859
|
parentBlockHash: Bytes32;
|
|
860
|
+
safeBlockHash: RootHex;
|
|
861
|
+
finalizedBlockHash: RootHex;
|
|
858
862
|
feeRecipient: string;
|
|
859
863
|
}
|
|
860
864
|
): SSEPayloadAttributes {
|
|
@@ -874,7 +878,10 @@ export function getPayloadAttributesForSSE(
|
|
|
874
878
|
payloadAttributes,
|
|
875
879
|
} as SSEPayloadAttributes;
|
|
876
880
|
|
|
877
|
-
if (
|
|
881
|
+
if (isForkPostGloas(fork)) {
|
|
882
|
+
(ssePayloadAttributes as gloas.SSEPayloadAttributes).safeBlockHash = fromHex(safeBlockHash);
|
|
883
|
+
(ssePayloadAttributes as gloas.SSEPayloadAttributes).finalizedBlockHash = fromHex(finalizedBlockHash);
|
|
884
|
+
} else {
|
|
878
885
|
// Removed in Gloas, builders can get the block number from the EL via the block hash if required
|
|
879
886
|
(ssePayloadAttributes as bellatrix.SSEPayloadAttributes).parentBlockNumber = prepareState.payloadBlockNumber;
|
|
880
887
|
}
|
|
@@ -47,8 +47,9 @@ export type SeenPayloadEnvelopeInputModules = {
|
|
|
47
47
|
* Created whenever we have a block because it needs block bid.
|
|
48
48
|
* Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
|
|
49
49
|
* (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* hold more during forks and range sync, or while an entry's payload is not FULL on the head's
|
|
51
|
+
* branch or its columns are still being gathered. Such entries are evicted on a later block
|
|
52
|
+
* import, on finalization or by the size cap.
|
|
52
53
|
*/
|
|
53
54
|
export class SeenPayloadEnvelopeInput {
|
|
54
55
|
private readonly config: ChainForkConfig;
|
|
@@ -264,18 +265,35 @@ export class SeenPayloadEnvelopeInput {
|
|
|
264
265
|
}
|
|
265
266
|
}
|
|
266
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Removes a PayloadEnvelopeInput whose envelope failed import with a definitive invalid verdict. The envelope
|
|
270
|
+
* cannot be replaced in place, sync recreates the entry via `getOrReload()` or `add()` to fetch another one.
|
|
271
|
+
* No-op if the cached entry is a different object, it may already be the recreated one.
|
|
272
|
+
*/
|
|
273
|
+
removeInvalid(payloadInput: PayloadEnvelopeInput): void {
|
|
274
|
+
if (this.payloadInputs.get(payloadInput.blockRootHex) === payloadInput) {
|
|
275
|
+
this.evictPayloadInput(payloadInput, "invalid");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
267
279
|
pruneBelowParent(parentBlock: ProtoBlock): void {
|
|
268
|
-
for (const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
for (const input of this.payloadInputs.values()) {
|
|
281
|
+
if (
|
|
282
|
+
input.slot < parentBlock.slot &&
|
|
283
|
+
// Don't evict while columns are still being gathered: writeDataColumnsToDb awaits the same
|
|
284
|
+
// hasComputedAllData() before persisting. Such entries are pruned by a later call.
|
|
285
|
+
input.hasComputedAllData() &&
|
|
286
|
+
// Only evict once the payload is FULL (revealed/imported) on the parent's branch. On an EMPTY/PENDING
|
|
287
|
+
// branch we may still need to download the FULL envelope (see #9475), and without the entry gossip
|
|
288
|
+
// for the block is ignored until by-root sync reloads it.
|
|
289
|
+
this.forkChoice.isDescendant(
|
|
290
|
+
input.blockRootHex,
|
|
291
|
+
PayloadStatus.FULL,
|
|
292
|
+
parentBlock.blockRoot,
|
|
293
|
+
parentBlock.payloadStatus
|
|
294
|
+
)
|
|
295
|
+
) {
|
|
296
|
+
this.evictPayloadInput(input, "belowParent");
|
|
279
297
|
}
|
|
280
298
|
}
|
|
281
299
|
}
|
|
@@ -4,14 +4,15 @@ import {
|
|
|
4
4
|
getAttesterSlashingSignatureSets,
|
|
5
5
|
isSlashableValidator,
|
|
6
6
|
} from "@lodestar/state-transition";
|
|
7
|
-
import {AttesterSlashing} from "@lodestar/types";
|
|
7
|
+
import {AttesterSlashing, Domain} from "@lodestar/types";
|
|
8
8
|
import {AttesterSlashingError, AttesterSlashingErrorCode, GossipAction} from "../errors/index.js";
|
|
9
9
|
import {IBeaconChain} from "../index.js";
|
|
10
|
+
import {getAttesterSlashingSignatureDomains} from "../opPools/utils.js";
|
|
10
11
|
|
|
11
12
|
export async function validateApiAttesterSlashing(
|
|
12
13
|
chain: IBeaconChain,
|
|
13
14
|
attesterSlashing: AttesterSlashing
|
|
14
|
-
): Promise<
|
|
15
|
+
): Promise<[Domain, Domain]> {
|
|
15
16
|
const prioritizeBls = true;
|
|
16
17
|
return validateAttesterSlashing(chain, attesterSlashing, prioritizeBls);
|
|
17
18
|
}
|
|
@@ -19,7 +20,7 @@ export async function validateApiAttesterSlashing(
|
|
|
19
20
|
export async function validateGossipAttesterSlashing(
|
|
20
21
|
chain: IBeaconChain,
|
|
21
22
|
attesterSlashing: AttesterSlashing
|
|
22
|
-
): Promise<
|
|
23
|
+
): Promise<[Domain, Domain]> {
|
|
23
24
|
return validateAttesterSlashing(chain, attesterSlashing);
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -27,7 +28,7 @@ export async function validateAttesterSlashing(
|
|
|
27
28
|
chain: IBeaconChain,
|
|
28
29
|
attesterSlashing: AttesterSlashing,
|
|
29
30
|
prioritizeBls = false
|
|
30
|
-
): Promise<
|
|
31
|
+
): Promise<[Domain, Domain]> {
|
|
31
32
|
// [IGNORE] At least one index in the intersection of the attesting indices of each attestation has not yet been seen
|
|
32
33
|
// in any prior attester_slashing (i.e.
|
|
33
34
|
// attester_slashed_indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices
|
|
@@ -61,9 +62,11 @@ export async function validateAttesterSlashing(
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
const signatureSets = getAttesterSlashingSignatureSets(chain.config, state.slot, attesterSlashing);
|
|
65
|
+
const verifiedDomains = getAttesterSlashingSignatureDomains(chain.config, state.slot, attesterSlashing);
|
|
64
66
|
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
|
65
67
|
throw new AttesterSlashingError(GossipAction.REJECT, {
|
|
66
68
|
code: AttesterSlashingErrorCode.INVALID_SIGNATURE,
|
|
67
69
|
});
|
|
68
70
|
}
|
|
71
|
+
return verifiedDomains;
|
|
69
72
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {assertValidProposerSlashing, getProposerSlashingSignatureSets} from "@lodestar/state-transition";
|
|
2
|
-
import {phase0} from "@lodestar/types";
|
|
2
|
+
import {Domain, phase0} from "@lodestar/types";
|
|
3
3
|
import {GossipAction, ProposerSlashingError, ProposerSlashingErrorCode} from "../errors/index.js";
|
|
4
4
|
import {IBeaconChain} from "../index.js";
|
|
5
|
+
import {getProposerSlashingSignatureDomain} from "../opPools/utils.js";
|
|
5
6
|
|
|
6
7
|
export async function validateApiProposerSlashing(
|
|
7
8
|
chain: IBeaconChain,
|
|
8
9
|
proposerSlashing: phase0.ProposerSlashing
|
|
9
|
-
): Promise<
|
|
10
|
+
): Promise<Domain> {
|
|
10
11
|
const prioritizeBls = true;
|
|
11
12
|
return validateProposerSlashing(chain, proposerSlashing, prioritizeBls);
|
|
12
13
|
}
|
|
@@ -14,7 +15,7 @@ export async function validateApiProposerSlashing(
|
|
|
14
15
|
export async function validateGossipProposerSlashing(
|
|
15
16
|
chain: IBeaconChain,
|
|
16
17
|
proposerSlashing: phase0.ProposerSlashing
|
|
17
|
-
): Promise<
|
|
18
|
+
): Promise<Domain> {
|
|
18
19
|
return validateProposerSlashing(chain, proposerSlashing);
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -22,7 +23,7 @@ async function validateProposerSlashing(
|
|
|
22
23
|
chain: IBeaconChain,
|
|
23
24
|
proposerSlashing: phase0.ProposerSlashing,
|
|
24
25
|
prioritizeBls = false
|
|
25
|
-
): Promise<
|
|
26
|
+
): Promise<Domain> {
|
|
26
27
|
// [IGNORE] The proposer slashing is the first valid proposer slashing received for the proposer with index
|
|
27
28
|
// proposer_slashing.signed_header_1.message.proposer_index.
|
|
28
29
|
if (chain.opPool.hasSeenProposerSlashing(proposerSlashing.signedHeader1.message.proposerIndex)) {
|
|
@@ -46,9 +47,11 @@ async function validateProposerSlashing(
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const signatureSets = getProposerSlashingSignatureSets(chain.config, state.slot, proposerSlashing);
|
|
50
|
+
const verifiedDomain = getProposerSlashingSignatureDomain(chain.config, state.slot, proposerSlashing);
|
|
49
51
|
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
|
50
52
|
throw new ProposerSlashingError(GossipAction.REJECT, {
|
|
51
53
|
code: ProposerSlashingErrorCode.INVALID_SIGNATURE,
|
|
52
54
|
});
|
|
53
55
|
}
|
|
56
|
+
return verifiedDomain;
|
|
54
57
|
}
|
package/src/db/beacon.ts
CHANGED
|
@@ -122,6 +122,8 @@ export class BeaconDb implements IBeaconDb {
|
|
|
122
122
|
async pruneHotDb(): Promise<void> {
|
|
123
123
|
// Prune all hot blobs
|
|
124
124
|
await this.blobSidecars.batchDelete(await this.blobSidecars.keys());
|
|
125
|
+
// New columns are persisted in flat files, so this bucket only contains legacy hot data.
|
|
126
|
+
await this.deleteBucketData(Bucket.allForks_dataColumnSidecars);
|
|
125
127
|
// Prune all hot blocks
|
|
126
128
|
// TODO: Enable once it's deemed safe
|
|
127
129
|
// await this.block.batchDelete(await this.block.keys());
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {Db, Repository} from "@lodestar/db";
|
|
2
|
+
import {BUCKET_LENGTH, Db, Repository} from "@lodestar/db";
|
|
3
3
|
import {ValidatorIndex, phase0, ssz} from "@lodestar/types";
|
|
4
|
+
import {bytesToInt} from "@lodestar/utils";
|
|
4
5
|
import {Bucket, getBucketNameByValue} from "../buckets.js";
|
|
5
6
|
|
|
6
7
|
export class ProposerSlashingRepository extends Repository<ValidatorIndex, phase0.ProposerSlashing> {
|
|
@@ -12,4 +13,8 @@ export class ProposerSlashingRepository extends Repository<ValidatorIndex, phase
|
|
|
12
13
|
getId(value: phase0.ProposerSlashing): ValidatorIndex {
|
|
13
14
|
return value.signedHeader1.message.proposerIndex;
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
decodeKey(data: Uint8Array): ValidatorIndex {
|
|
18
|
+
return bytesToInt(data.subarray(BUCKET_LENGTH), "be");
|
|
19
|
+
}
|
|
15
20
|
}
|
|
@@ -516,12 +516,13 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
516
516
|
): Promise<(BlobAndProof | null)[]>;
|
|
517
517
|
async getBlobs(
|
|
518
518
|
fork: ForkName,
|
|
519
|
-
versionedHashes: VersionedHashes
|
|
519
|
+
versionedHashes: VersionedHashes,
|
|
520
|
+
buffers?: Uint8Array[]
|
|
520
521
|
): Promise<BlobAndProofV2[] | (BlobAndProof | null)[] | null> {
|
|
521
522
|
assertReqSizeLimit(versionedHashes.length, MAX_VERSIONED_HASHES);
|
|
522
523
|
const versionedHashesHex = versionedHashes.map(bytesToData);
|
|
523
524
|
if (isForkPostFulu(fork)) {
|
|
524
|
-
return await this.getBlobsV2(versionedHashesHex);
|
|
525
|
+
return await this.getBlobsV2(versionedHashesHex, buffers);
|
|
525
526
|
}
|
|
526
527
|
return await this.getBlobsV1(versionedHashesHex);
|
|
527
528
|
}
|
|
@@ -551,8 +552,9 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
551
552
|
|
|
552
553
|
private async getBlobsV2(versionedHashesHex: string[], buffers?: Uint8Array[]) {
|
|
553
554
|
if (buffers) {
|
|
554
|
-
|
|
555
|
-
|
|
555
|
+
// Callers preallocate one buffer per max blobs of the epoch, only the first entries are used
|
|
556
|
+
if (buffers.length < versionedHashesHex.length) {
|
|
557
|
+
throw Error(`Not enough buffers length=${buffers.length} versionedHashes=${versionedHashesHex.length}`);
|
|
556
558
|
}
|
|
557
559
|
|
|
558
560
|
for (const [i, buffer] of buffers.entries()) {
|
|
@@ -2,6 +2,7 @@ import {EventEmitter} from "node:events";
|
|
|
2
2
|
import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
3
3
|
import {ErrorAborted, Gauge, Histogram, TimeoutError, fetch, isValidHttpUrl, retry} from "@lodestar/utils";
|
|
4
4
|
import {JwtClaim, encodeJwtToken} from "./jwt.js";
|
|
5
|
+
import {EngineTransportMetrics} from "./transportMetrics.js";
|
|
5
6
|
import {IJson, RpcPayload} from "./utils.js";
|
|
6
7
|
|
|
7
8
|
export enum JsonRpcHttpClientEvent {
|
|
@@ -55,7 +56,7 @@ export type ReqOpts = {
|
|
|
55
56
|
shouldRetry?: (lastError: Error) => boolean;
|
|
56
57
|
};
|
|
57
58
|
|
|
58
|
-
export type JsonRpcHttpClientMetrics = {
|
|
59
|
+
export type JsonRpcHttpClientMetrics = EngineTransportMetrics & {
|
|
59
60
|
requestTime: Histogram<{routeId: string}>;
|
|
60
61
|
streamTime: Histogram<{routeId: string}>;
|
|
61
62
|
requestErrors: Gauge<{routeId: string}>;
|
|
@@ -270,15 +271,21 @@ export class JsonRpcHttpClient implements IJsonRpcHttpClient {
|
|
|
270
271
|
headers.Authorization = `Bearer ${token}`;
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
const body = JSON.stringify(json);
|
|
275
|
+
// Json rpc bodies are ASCII (json syntax, 0x hex, digits) so String#length is the byte count.
|
|
276
|
+
// Buffer.byteLength would add a ~4ms scan of a 35MB getBlobsV2 body for no accuracy gain.
|
|
277
|
+
this.metrics?.requestBytes.inc({routeId}, body.length);
|
|
278
|
+
|
|
273
279
|
const res = await fetch(url, {
|
|
274
280
|
method: "post",
|
|
275
|
-
body
|
|
281
|
+
body,
|
|
276
282
|
headers,
|
|
277
283
|
signal: controller.signal,
|
|
278
284
|
});
|
|
279
285
|
|
|
280
286
|
const streamTimer = this.metrics?.streamTime.startTimer({routeId});
|
|
281
287
|
const bodyText = await res.text();
|
|
288
|
+
this.metrics?.responseBytes.inc({routeId}, bodyText.length);
|
|
282
289
|
if (!res.ok) {
|
|
283
290
|
// Infura errors:
|
|
284
291
|
// - No project ID: Forbidden: {"jsonrpc":"2.0","id":0,"error":{"code":-32600,"message":"project ID is required","data":{"reason":"project ID not provided","see":"https://infura.io/dashboard"}}}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
export type {RestApiServerMetrics, RestApiServerModules, RestApiServerOpts} from "./api/rest/base.js";
|
|
4
4
|
export {RestApiServer} from "./api/rest/base.js";
|
|
5
|
-
export {
|
|
5
|
+
export {persistAnchorState} from "./chain/index.js";
|
|
6
6
|
export {DbCPStateDatastore} from "./chain/stateCache/datastore/db.js";
|
|
7
7
|
export {FileCPStateDatastore} from "./chain/stateCache/datastore/file.js";
|
|
8
8
|
export {BeaconDb, type IBeaconDb} from "./db/index.js";
|
|
@@ -216,7 +216,7 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
|
|
|
216
216
|
}),
|
|
217
217
|
},
|
|
218
218
|
payloadEnvelopeReconstruction: {
|
|
219
|
-
envelopes: register.counter<{result: "
|
|
219
|
+
envelopes: register.counter<{result: "success" | "unavailable" | "mismatch"}>({
|
|
220
220
|
name: "beacon_payload_envelope_reconstruction_envelopes_total",
|
|
221
221
|
help: "Archived header envelopes rebuilt from EL bodies, by outcome",
|
|
222
222
|
labelNames: ["result"],
|
|
@@ -2053,6 +2053,16 @@ export function createLodestarMetrics(
|
|
|
2053
2053
|
help: "ExecutionEngineHttp client - total count of request retries",
|
|
2054
2054
|
labelNames: ["routeId"],
|
|
2055
2055
|
}),
|
|
2056
|
+
requestBytes: register.counter<{routeId: string}>({
|
|
2057
|
+
name: "lodestar_execution_engine_http_client_request_bytes_total",
|
|
2058
|
+
help: "ExecutionEngineHttp client - total bytes sent to the execution client by routeId",
|
|
2059
|
+
labelNames: ["routeId"],
|
|
2060
|
+
}),
|
|
2061
|
+
responseBytes: register.counter<{routeId: string}>({
|
|
2062
|
+
name: "lodestar_execution_engine_http_client_response_bytes_total",
|
|
2063
|
+
help: "ExecutionEngineHttp client - total bytes received from the execution client by routeId",
|
|
2064
|
+
labelNames: ["routeId"],
|
|
2065
|
+
}),
|
|
2056
2066
|
requestUsedFallbackUrl: register.gauge<{routeId: string}>({
|
|
2057
2067
|
name: "lodestar_execution_engine_http_client_request_used_fallback_url_total",
|
|
2058
2068
|
help: "ExecutionEngineHttp client - total count of requests on fallback url(s)",
|
package/src/network/metadata.ts
CHANGED
|
@@ -42,13 +42,13 @@ export class MetadataController {
|
|
|
42
42
|
private onSetValue: (key: string, value: Uint8Array) => void;
|
|
43
43
|
private networkConfig: NetworkConfig;
|
|
44
44
|
private logger: Logger;
|
|
45
|
-
private
|
|
45
|
+
private metadata: fulu.Metadata;
|
|
46
46
|
|
|
47
47
|
constructor(opts: MetadataOpts, modules: MetadataModules) {
|
|
48
48
|
this.networkConfig = modules.networkConfig;
|
|
49
49
|
this.logger = modules.logger;
|
|
50
50
|
this.onSetValue = modules.onSetValue;
|
|
51
|
-
this.
|
|
51
|
+
this.metadata = opts.metadata ?? {
|
|
52
52
|
...ssz.fulu.Metadata.defaultValue(),
|
|
53
53
|
custodyGroupCount: modules.networkConfig.custodyConfig.targetCustodyGroupCount,
|
|
54
54
|
};
|
|
@@ -58,61 +58,61 @@ export class MetadataController {
|
|
|
58
58
|
// updateEth2Field() MUST be called with clock epoch
|
|
59
59
|
this.updateEth2Field(currentEpoch);
|
|
60
60
|
|
|
61
|
-
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.
|
|
61
|
+
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.metadata.attnets));
|
|
62
62
|
|
|
63
63
|
const config = this.networkConfig.config;
|
|
64
64
|
|
|
65
65
|
if (config.getForkSeq(computeStartSlotAtEpoch(currentEpoch)) >= ForkSeq.altair) {
|
|
66
66
|
// Only persist syncnets if altair fork is already activated. If currentFork is altair but head is phase0
|
|
67
67
|
// adding syncnets to the ENR is not a problem, we will just have a useless field for a few hours.
|
|
68
|
-
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.
|
|
68
|
+
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.metadata.syncnets));
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Set CGC regardless of fork. It may be useful to clients before Fulu, and will be ignored otherwise.
|
|
72
|
-
this.onSetValue(ENRKey.cgc, serializeCgc(this.
|
|
72
|
+
this.onSetValue(ENRKey.cgc, serializeCgc(this.metadata.custodyGroupCount));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
get seqNumber(): bigint {
|
|
76
|
-
return this.
|
|
76
|
+
return this.metadata.seqNumber;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
get syncnets(): BitArray {
|
|
80
|
-
return this.
|
|
80
|
+
return this.metadata.syncnets;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
set syncnets(syncnets: BitArray) {
|
|
84
84
|
this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(syncnets));
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
85
|
+
this.metadata.seqNumber++;
|
|
86
|
+
this.metadata.syncnets = syncnets;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
get attnets(): BitArray {
|
|
90
|
-
return this.
|
|
90
|
+
return this.metadata.attnets;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
set attnets(attnets: BitArray) {
|
|
94
94
|
this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(attnets));
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
95
|
+
this.metadata.seqNumber++;
|
|
96
|
+
this.metadata.attnets = attnets;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
get custodyGroupCount(): number {
|
|
100
|
-
return this.
|
|
100
|
+
return this.metadata.custodyGroupCount;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
set custodyGroupCount(custodyGroupCount: number) {
|
|
104
|
-
if (custodyGroupCount === this.
|
|
104
|
+
if (custodyGroupCount === this.metadata.custodyGroupCount) {
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
this.onSetValue(ENRKey.cgc, serializeCgc(custodyGroupCount));
|
|
108
108
|
this.logger.debug("Updated cgc field in ENR", {custodyGroupCount});
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
109
|
+
this.metadata.seqNumber++;
|
|
110
|
+
this.metadata.custodyGroupCount = custodyGroupCount;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** Consumers that need the phase0.Metadata type can just ignore the .syncnets property */
|
|
114
114
|
get json(): fulu.Metadata {
|
|
115
|
-
return this.
|
|
115
|
+
return this.metadata;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|