@lodestar/beacon-node 1.49.0 → 1.50.0-dev.1f38fe79fb

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.
Files changed (110) hide show
  1. package/lib/api/impl/beacon/pool/index.js +4 -4
  2. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  3. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  4. package/lib/api/impl/beacon/state/index.js +28 -0
  5. package/lib/api/impl/beacon/state/index.js.map +1 -1
  6. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  7. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  8. package/lib/chain/archiveStore/archiveStore.js +26 -3
  9. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  10. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
  11. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  12. package/lib/chain/archiveStore/utils/pruneHistory.js +4 -3
  13. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  14. package/lib/chain/chain.js +3 -3
  15. package/lib/chain/chain.js.map +1 -1
  16. package/lib/chain/initState.d.ts +2 -9
  17. package/lib/chain/initState.d.ts.map +1 -1
  18. package/lib/chain/initState.js +5 -38
  19. package/lib/chain/initState.js.map +1 -1
  20. package/lib/chain/opPools/opPool.d.ts +5 -4
  21. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  22. package/lib/chain/opPools/opPool.js +62 -18
  23. package/lib/chain/opPools/opPool.js.map +1 -1
  24. package/lib/chain/opPools/utils.d.ts +4 -1
  25. package/lib/chain/opPools/utils.d.ts.map +1 -1
  26. package/lib/chain/opPools/utils.js +11 -1
  27. package/lib/chain/opPools/utils.js.map +1 -1
  28. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  29. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  30. package/lib/chain/validation/attesterSlashing.js +3 -0
  31. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  32. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  33. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  34. package/lib/chain/validation/proposerSlashing.js +3 -0
  35. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  36. package/lib/db/beacon.d.ts.map +1 -1
  37. package/lib/db/beacon.js +2 -0
  38. package/lib/db/beacon.js.map +1 -1
  39. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  40. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  41. package/lib/db/repositories/proposerSlashing.js +5 -1
  42. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  43. package/lib/execution/engine/http.d.ts.map +1 -1
  44. package/lib/execution/engine/http.js +5 -4
  45. package/lib/execution/engine/http.js.map +1 -1
  46. package/lib/execution/engine/jsonRpcHttpClient.d.ts +2 -1
  47. package/lib/execution/engine/jsonRpcHttpClient.d.ts.map +1 -1
  48. package/lib/execution/engine/jsonRpcHttpClient.js +6 -1
  49. package/lib/execution/engine/jsonRpcHttpClient.js.map +1 -1
  50. package/lib/execution/engine/transportMetrics.d.ts +13 -0
  51. package/lib/execution/engine/transportMetrics.d.ts.map +1 -0
  52. package/lib/execution/engine/transportMetrics.js +2 -0
  53. package/lib/execution/engine/transportMetrics.js.map +1 -0
  54. package/lib/index.d.ts +1 -1
  55. package/lib/index.d.ts.map +1 -1
  56. package/lib/index.js +1 -1
  57. package/lib/index.js.map +1 -1
  58. package/lib/metrics/metrics/beacon.d.ts +1 -1
  59. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  60. package/lib/metrics/metrics/beacon.js.map +1 -1
  61. package/lib/metrics/metrics/lodestar.d.ts +6 -0
  62. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  63. package/lib/metrics/metrics/lodestar.js +10 -0
  64. package/lib/metrics/metrics/lodestar.js.map +1 -1
  65. package/lib/network/metadata.d.ts +1 -1
  66. package/lib/network/metadata.d.ts.map +1 -1
  67. package/lib/network/metadata.js +17 -17
  68. package/lib/network/metadata.js.map +1 -1
  69. package/lib/network/peers/datastore.d.ts +9 -9
  70. package/lib/network/peers/datastore.d.ts.map +1 -1
  71. package/lib/network/peers/datastore.js +36 -36
  72. package/lib/network/peers/datastore.js.map +1 -1
  73. package/lib/network/processor/gossipHandlers.js +4 -4
  74. package/lib/network/processor/gossipHandlers.js.map +1 -1
  75. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  76. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
  77. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  78. package/lib/sync/unknownBlock.d.ts +9 -5
  79. package/lib/sync/unknownBlock.d.ts.map +1 -1
  80. package/lib/sync/unknownBlock.js +83 -42
  81. package/lib/sync/unknownBlock.js.map +1 -1
  82. package/lib/util/execution.d.ts +2 -0
  83. package/lib/util/execution.d.ts.map +1 -1
  84. package/lib/util/execution.js +6 -6
  85. package/lib/util/execution.js.map +1 -1
  86. package/package.json +15 -15
  87. package/src/api/impl/beacon/pool/index.ts +4 -4
  88. package/src/api/impl/beacon/state/index.ts +36 -0
  89. package/src/chain/archiveStore/archiveStore.ts +29 -4
  90. package/src/chain/archiveStore/utils/pruneHistory.ts +6 -4
  91. package/src/chain/chain.ts +3 -3
  92. package/src/chain/initState.ts +8 -60
  93. package/src/chain/opPools/opPool.ts +91 -18
  94. package/src/chain/opPools/utils.ts +31 -3
  95. package/src/chain/validation/attesterSlashing.ts +7 -4
  96. package/src/chain/validation/proposerSlashing.ts +7 -4
  97. package/src/db/beacon.ts +2 -0
  98. package/src/db/repositories/proposerSlashing.ts +6 -1
  99. package/src/execution/engine/http.ts +6 -4
  100. package/src/execution/engine/jsonRpcHttpClient.ts +9 -2
  101. package/src/execution/engine/transportMetrics.ts +9 -0
  102. package/src/index.ts +1 -1
  103. package/src/metrics/metrics/beacon.ts +1 -1
  104. package/src/metrics/metrics/lodestar.ts +10 -0
  105. package/src/network/metadata.ts +17 -17
  106. package/src/network/peers/datastore.ts +36 -36
  107. package/src/network/processor/gossipHandlers.ts +4 -4
  108. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
  109. package/src/sync/unknownBlock.ts +94 -46
  110. package/src/util/execution.ts +10 -6
@@ -1,9 +1,37 @@
1
1
  import {Signature} from "@chainsafe/lodestar-z/blst";
2
- import {BLS_WITHDRAWAL_PREFIX} from "@lodestar/params";
3
- import {IBeaconStateView} from "@lodestar/state-transition";
4
- import {Slot, capella} from "@lodestar/types";
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
  */
@@ -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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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
- if (buffers.length !== versionedHashesHex.length) {
555
- throw Error(`Invalid buffers length=${buffers.length} versionedHashes=${versionedHashesHex.length}`);
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: JSON.stringify(json),
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"}}}
@@ -0,0 +1,9 @@
1
+ import {Counter} from "@lodestar/utils";
2
+
3
+ /**
4
+ * Common metrics type for both json rpc and ssz transport.
5
+ */
6
+ export type EngineTransportMetrics = {
7
+ requestBytes: Counter<{routeId: string}>;
8
+ responseBytes: Counter<{routeId: string}>;
9
+ };
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 {checkAndPersistAnchorState} from "./chain/index.js";
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: "ok" | "unavailable" | "mismatch"}>({
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)",
@@ -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 _metadata: fulu.Metadata;
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._metadata = opts.metadata ?? {
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._metadata.attnets));
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._metadata.syncnets));
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._metadata.custodyGroupCount));
72
+ this.onSetValue(ENRKey.cgc, serializeCgc(this.metadata.custodyGroupCount));
73
73
  }
74
74
 
75
75
  get seqNumber(): bigint {
76
- return this._metadata.seqNumber;
76
+ return this.metadata.seqNumber;
77
77
  }
78
78
 
79
79
  get syncnets(): BitArray {
80
- return this._metadata.syncnets;
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._metadata.seqNumber++;
86
- this._metadata.syncnets = syncnets;
85
+ this.metadata.seqNumber++;
86
+ this.metadata.syncnets = syncnets;
87
87
  }
88
88
 
89
89
  get attnets(): BitArray {
90
- return this._metadata.attnets;
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._metadata.seqNumber++;
96
- this._metadata.attnets = attnets;
95
+ this.metadata.seqNumber++;
96
+ this.metadata.attnets = attnets;
97
97
  }
98
98
 
99
99
  get custodyGroupCount(): number {
100
- return this._metadata.custodyGroupCount;
100
+ return this.metadata.custodyGroupCount;
101
101
  }
102
102
 
103
103
  set custodyGroupCount(custodyGroupCount: number) {
104
- if (custodyGroupCount === this._metadata.custodyGroupCount) {
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._metadata.seqNumber++;
110
- this._metadata.custodyGroupCount = custodyGroupCount;
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._metadata;
115
+ return this.metadata;
116
116
  }
117
117
 
118
118
  /**
@@ -16,23 +16,23 @@ type AwaitGenerator<T, TReturn = any, TNext = any> = Generator<T, TReturn, TNext
16
16
  * This has a memory issue because all peer data stays in memory and loaded at startup time
17
17
  * This is written for libp2p >=0.35, we maintain the same mechanism but with bounded data structure
18
18
  * This datastore includes a memory datastore and fallback to db datastore
19
- * Use an in-memory datastore with last accessed time and _maxMemoryItems, on start it's empty (lazy load)
19
+ * Use an in-memory datastore with last accessed time and maxMemoryItems, on start it's empty (lazy load)
20
20
  * - get: Search in-memory datastore first, if not found search from db.
21
21
  * - If found from db, add back to the in-memory datastore
22
22
  * - Update lastAccessedMs
23
- * - put: move oldest items from memory to db if there are more than _maxMemoryItems items in memory
24
- * - update memory datastore, only update db datastore if there are at least _threshold dirty items
23
+ * - put: move oldest items from memory to db if there are more than maxMemoryItems items in memory
24
+ * - update memory datastore, only update db datastore if there are at least threshold dirty items
25
25
  * - Update lastAccessedMs
26
26
  */
27
27
  export class Eth2PeerDataStore extends BaseDatastore {
28
- private _dbDatastore: LevelDatastore;
29
- private _memoryDatastore: Map<string, MemoryItem>;
28
+ private dbDatastore: LevelDatastore;
29
+ private memoryDatastore: Map<string, MemoryItem>;
30
30
  /** Same to PersistentPeerStore of the old libp2p implementation */
31
- private _dirtyItems = new Set<string>();
31
+ private dirtyItems = new Set<string>();
32
32
  /** If there are more dirty items than threshold, commit data to db */
33
- private _threshold: number;
33
+ private threshold: number;
34
34
  /** If there are more memory items than this, prune oldest ones from memory and move to db */
35
- private _maxMemoryItems: number;
35
+ private maxMemoryItems: number;
36
36
 
37
37
  constructor(
38
38
  dbDatastore: LevelDatastore | string,
@@ -47,18 +47,18 @@ export class Eth2PeerDataStore extends BaseDatastore {
47
47
  throw Error(`Threshold ${threshold} should be at most maxMemoryItems ${maxMemoryItems}`);
48
48
  }
49
49
 
50
- this._dbDatastore = typeof dbDatastore === "string" ? new LevelDatastore(dbDatastore) : dbDatastore;
51
- this._memoryDatastore = new Map();
52
- this._threshold = threshold;
53
- this._maxMemoryItems = maxMemoryItems;
50
+ this.dbDatastore = typeof dbDatastore === "string" ? new LevelDatastore(dbDatastore) : dbDatastore;
51
+ this.memoryDatastore = new Map();
52
+ this.threshold = threshold;
53
+ this.maxMemoryItems = maxMemoryItems;
54
54
  }
55
55
 
56
56
  async open(): Promise<void> {
57
- return this._dbDatastore.open();
57
+ return this.dbDatastore.open();
58
58
  }
59
59
 
60
60
  async close(): Promise<void> {
61
- return this._dbDatastore.close();
61
+ return this.dbDatastore.close();
62
62
  }
63
63
 
64
64
  async put(key: Key, val: Uint8Array, _options?: AbortOptions): Promise<Key> {
@@ -67,23 +67,23 @@ export class Eth2PeerDataStore extends BaseDatastore {
67
67
 
68
68
  /**
69
69
  * Same interface to put with "fromDb" option, if this item is updated back from db
70
- * Move oldest items from memory data store to db if it's over this._maxMemoryItems
70
+ * Move oldest items from memory data store to db if it's over this.maxMemoryItems
71
71
  */
72
72
  async _put(key: Key, val: Uint8Array, fromDb = false): Promise<Key> {
73
- while (this._memoryDatastore.size >= this._maxMemoryItems) {
73
+ while (this.memoryDatastore.size >= this.maxMemoryItems) {
74
74
  // it's likely this is called only 1 time
75
75
  await this.pruneMemoryDatastore();
76
76
  }
77
77
 
78
78
  const keyStr = key.toString();
79
- const memoryItem = this._memoryDatastore.get(keyStr);
79
+ const memoryItem = this.memoryDatastore.get(keyStr);
80
80
  if (memoryItem) {
81
81
  // update existing
82
82
  memoryItem.lastAccessedMs = Date.now();
83
83
  memoryItem.data = val;
84
84
  } else {
85
85
  // new
86
- this._memoryDatastore.set(keyStr, {data: val, lastAccessedMs: Date.now()});
86
+ this.memoryDatastore.set(keyStr, {data: val, lastAccessedMs: Date.now()});
87
87
  }
88
88
 
89
89
  if (!fromDb) await this._addDirtyItem(keyStr);
@@ -98,15 +98,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
98
98
  */
99
99
  async get(key: Key, options?: AbortOptions): Promise<Uint8Array> {
100
100
  const keyStr = key.toString();
101
- const memoryItem = this._memoryDatastore.get(keyStr);
101
+ const memoryItem = this.memoryDatastore.get(keyStr);
102
102
  if (memoryItem) {
103
103
  memoryItem.lastAccessedMs = Date.now();
104
104
  return memoryItem.data;
105
105
  }
106
106
 
107
107
  // this throws error if not found
108
- const dbValue = await this._dbDatastore.get(key, options);
109
- // don't call this._memoryDatastore.set directly
108
+ const dbValue = await this.dbDatastore.get(key, options);
109
+ // don't call this.memoryDatastore.set directly
110
110
  // we want to get through prune() logic with fromDb as true
111
111
  await this._put(key, dbValue, true);
112
112
  return dbValue;
@@ -125,30 +125,30 @@ export class Eth2PeerDataStore extends BaseDatastore {
125
125
  }
126
126
 
127
127
  async delete(key: Key, options?: AbortOptions): Promise<void> {
128
- this._memoryDatastore.delete(key.toString());
129
- await this._dbDatastore.delete(key, options);
128
+ this.memoryDatastore.delete(key.toString());
129
+ await this.dbDatastore.delete(key, options);
130
130
  }
131
131
 
132
132
  async *_all(q: Query, options?: AbortOptions): AwaitGenerator<Pair> {
133
- for (const [key, value] of this._memoryDatastore.entries()) {
133
+ for (const [key, value] of this.memoryDatastore.entries()) {
134
134
  yield {
135
135
  key: new Key(key),
136
136
  value: value.data,
137
137
  };
138
138
  }
139
- yield* this._dbDatastore.query(q, options);
139
+ yield* this.dbDatastore.query(q, options);
140
140
  }
141
141
 
142
142
  async *_allKeys(q: KeyQuery, options?: AbortOptions): AwaitGenerator<Key> {
143
- for (const key of this._memoryDatastore.keys()) {
143
+ for (const key of this.memoryDatastore.keys()) {
144
144
  yield new Key(key);
145
145
  }
146
- yield* this._dbDatastore.queryKeys(q, options);
146
+ yield* this.dbDatastore.queryKeys(q, options);
147
147
  }
148
148
 
149
149
  private async _addDirtyItem(keyStr: string): Promise<void> {
150
- this._dirtyItems.add(keyStr);
151
- if (this._dirtyItems.size >= this._threshold) {
150
+ this.dirtyItems.add(keyStr);
151
+ if (this.dirtyItems.size >= this.threshold) {
152
152
  try {
153
153
  await this._commitData();
154
154
  } catch (_e) {}
@@ -156,15 +156,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
156
156
  }
157
157
 
158
158
  private async _commitData(): Promise<void> {
159
- const batch = this._dbDatastore.batch();
160
- for (const keyStr of this._dirtyItems) {
161
- const memoryItem = this._memoryDatastore.get(keyStr);
159
+ const batch = this.dbDatastore.batch();
160
+ for (const keyStr of this.dirtyItems) {
161
+ const memoryItem = this.memoryDatastore.get(keyStr);
162
162
  if (memoryItem) {
163
163
  batch.put(new Key(keyStr), memoryItem.data);
164
164
  }
165
165
  }
166
166
  await batch.commit();
167
- this._dirtyItems.clear();
167
+ this.dirtyItems.clear();
168
168
  }
169
169
 
170
170
  /**
@@ -175,7 +175,7 @@ export class Eth2PeerDataStore extends BaseDatastore {
175
175
  let oldestKey: string | undefined = undefined;
176
176
  let oldestValue: Uint8Array | undefined = undefined;
177
177
 
178
- for (const [key, value] of this._memoryDatastore) {
178
+ for (const [key, value] of this.memoryDatastore) {
179
179
  if (value.lastAccessedMs < oldestAccessedMs) {
180
180
  oldestAccessedMs = value.lastAccessedMs;
181
181
  oldestKey = key;
@@ -184,8 +184,8 @@ export class Eth2PeerDataStore extends BaseDatastore {
184
184
  }
185
185
 
186
186
  if (oldestKey && oldestValue) {
187
- await this._dbDatastore.put(new Key(oldestKey), oldestValue);
188
- this._memoryDatastore.delete(oldestKey);
187
+ await this.dbDatastore.put(new Key(oldestKey), oldestValue);
188
+ this.memoryDatastore.delete(oldestKey);
189
189
  }
190
190
  }
191
191
  }
@@ -1065,12 +1065,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1065
1065
  const {serializedData} = gossipData;
1066
1066
  const {fork} = topic.boundary;
1067
1067
  const attesterSlashing = sszDeserialize(topic, serializedData);
1068
- await validateGossipAttesterSlashing(chain, attesterSlashing);
1068
+ const verifiedDomains = await validateGossipAttesterSlashing(chain, attesterSlashing);
1069
1069
 
1070
1070
  // Handler - deferred to next event loop so the validation result propagates first
1071
1071
  callInNextEventLoop(() => {
1072
1072
  try {
1073
- chain.opPool.insertAttesterSlashing(fork, attesterSlashing);
1073
+ chain.opPool.insertAttesterSlashing(fork, attesterSlashing, verifiedDomains);
1074
1074
  chain.forkChoice.onAttesterSlashing(attesterSlashing);
1075
1075
  chain.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing);
1076
1076
  } catch (e) {
@@ -1089,12 +1089,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1089
1089
  }: GossipHandlerParamGeneric<GossipType.proposer_slashing>) => {
1090
1090
  const {serializedData} = gossipData;
1091
1091
  const proposerSlashing = sszDeserialize(topic, serializedData);
1092
- await validateGossipProposerSlashing(chain, proposerSlashing);
1092
+ const verifiedDomain = await validateGossipProposerSlashing(chain, proposerSlashing);
1093
1093
 
1094
1094
  // Handler - deferred to next event loop so the validation result propagates first
1095
1095
  callInNextEventLoop(() => {
1096
1096
  try {
1097
- chain.opPool.insertProposerSlashing(proposerSlashing);
1097
+ chain.opPool.insertProposerSlashing(proposerSlashing, verifiedDomain);
1098
1098
  chain.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing);
1099
1099
  } catch (e) {
1100
1100
  logger.debug(