@lodestar/beacon-node 1.51.0-dev.a1662c7de7 → 1.51.0-dev.a1ecd6c34f

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 (31) hide show
  1. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/state/index.js +8 -2
  3. package/lib/api/impl/beacon/state/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.d.ts +2 -1
  5. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  6. package/lib/api/impl/beacon/state/utils.js +17 -9
  7. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  8. package/lib/api/impl/validator/index.js +7 -5
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  11. package/lib/chain/produceBlock/produceBlockBody.js +0 -1
  12. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  13. package/lib/execution/engine/http.d.ts.map +1 -1
  14. package/lib/execution/engine/http.js +0 -1
  15. package/lib/execution/engine/http.js.map +1 -1
  16. package/lib/execution/engine/payloadIdCache.d.ts +1 -2
  17. package/lib/execution/engine/payloadIdCache.d.ts.map +1 -1
  18. package/lib/execution/engine/payloadIdCache.js +2 -2
  19. package/lib/execution/engine/payloadIdCache.js.map +1 -1
  20. package/lib/network/processor/index.d.ts.map +1 -1
  21. package/lib/network/processor/index.js +26 -25
  22. package/lib/network/processor/index.js.map +1 -1
  23. package/package.json +29 -29
  24. package/src/api/impl/beacon/state/index.ts +10 -1
  25. package/src/api/impl/beacon/state/utils.ts +19 -9
  26. package/src/api/impl/validator/index.ts +7 -7
  27. package/src/chain/produceBlock/produceBlockBody.ts +0 -1
  28. package/src/execution/engine/http.ts +0 -1
  29. package/src/execution/engine/payloadIdCache.ts +5 -6
  30. package/src/network/processor/index.ts +22 -23
  31. package/src/network/processor/types.ts +1 -1
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/ChainSafe/lodestar/issues"
13
13
  },
14
- "version": "1.51.0-dev.a1662c7de7",
14
+ "version": "1.51.0-dev.a1ecd6c34f",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -116,28 +116,28 @@
116
116
  "@fastify/cors": "^10.0.1",
117
117
  "@fastify/swagger": "^9.0.0",
118
118
  "@fastify/swagger-ui": "^5.0.1",
119
- "@libp2p/bootstrap": "^12.0.30",
120
- "@libp2p/crypto": "^5.1.23",
121
- "@libp2p/gossipsub": "^17.1.2",
122
- "@libp2p/identify": "^4.1.13",
123
- "@libp2p/interface": "^3.3.0",
124
- "@libp2p/mdns": "^12.0.30",
125
- "@libp2p/mplex": "^12.0.30",
126
- "@libp2p/peer-id": "^6.0.15",
127
- "@libp2p/prometheus-metrics": "^5.0.28",
128
- "@libp2p/tcp": "^11.0.27",
129
- "@libp2p/utils": "^7.4.0",
130
- "@lodestar/api": "^1.51.0-dev.a1662c7de7",
131
- "@lodestar/config": "^1.51.0-dev.a1662c7de7",
132
- "@lodestar/db": "^1.51.0-dev.a1662c7de7",
133
- "@lodestar/fork-choice": "^1.51.0-dev.a1662c7de7",
134
- "@lodestar/logger": "^1.51.0-dev.a1662c7de7",
135
- "@lodestar/params": "^1.51.0-dev.a1662c7de7",
136
- "@lodestar/reqresp": "^1.51.0-dev.a1662c7de7",
137
- "@lodestar/state-transition": "^1.51.0-dev.a1662c7de7",
138
- "@lodestar/types": "^1.51.0-dev.a1662c7de7",
139
- "@lodestar/utils": "^1.51.0-dev.a1662c7de7",
140
- "@lodestar/validator": "^1.51.0-dev.a1662c7de7",
119
+ "@libp2p/bootstrap": "^12.0.34",
120
+ "@libp2p/crypto": "^5.1.25",
121
+ "@libp2p/gossipsub": "^17.1.4",
122
+ "@libp2p/identify": "^4.1.16",
123
+ "@libp2p/interface": "^3.4.0",
124
+ "@libp2p/mdns": "^12.0.34",
125
+ "@libp2p/mplex": "^12.0.34",
126
+ "@libp2p/peer-id": "^6.0.17",
127
+ "@libp2p/prometheus-metrics": "^5.0.31",
128
+ "@libp2p/tcp": "^11.0.30",
129
+ "@libp2p/utils": "^7.4.3",
130
+ "@lodestar/api": "^1.51.0-dev.a1ecd6c34f",
131
+ "@lodestar/config": "^1.51.0-dev.a1ecd6c34f",
132
+ "@lodestar/db": "^1.51.0-dev.a1ecd6c34f",
133
+ "@lodestar/fork-choice": "^1.51.0-dev.a1ecd6c34f",
134
+ "@lodestar/logger": "^1.51.0-dev.a1ecd6c34f",
135
+ "@lodestar/params": "^1.51.0-dev.a1ecd6c34f",
136
+ "@lodestar/reqresp": "^1.51.0-dev.a1ecd6c34f",
137
+ "@lodestar/state-transition": "^1.51.0-dev.a1ecd6c34f",
138
+ "@lodestar/types": "^1.51.0-dev.a1ecd6c34f",
139
+ "@lodestar/utils": "^1.51.0-dev.a1ecd6c34f",
140
+ "@lodestar/validator": "^1.51.0-dev.a1ecd6c34f",
141
141
  "@multiformats/multiaddr": "^13.0.1",
142
142
  "datastore-core": "^12.0.1",
143
143
  "datastore-level": "^13.0.1",
@@ -145,7 +145,7 @@
145
145
  "fastify": "^5.8.1",
146
146
  "interface-datastore": "^10.0.1",
147
147
  "jwt-simple": "0.5.6",
148
- "libp2p": "3.3.9",
148
+ "libp2p": "3.4.0",
149
149
  "multiformats": "^13.4.2",
150
150
  "prom-client": "^15.1.0",
151
151
  "qs": "^6.14.2",
@@ -157,10 +157,10 @@
157
157
  },
158
158
  "devDependencies": {
159
159
  "@ethereumjs/rlp": "^4.0.1",
160
- "@libp2p/interface-internal": "^3.1.12",
161
- "@libp2p/logger": "^6.2.13",
162
- "@lodestar/spec-test-util": "^1.51.0-dev.a1662c7de7",
163
- "@lodestar/test-utils": "^1.51.0-dev.a1662c7de7",
160
+ "@libp2p/interface-internal": "^3.2.0",
161
+ "@libp2p/logger": "^6.2.15",
162
+ "@lodestar/spec-test-util": "^1.51.0-dev.a1ecd6c34f",
163
+ "@lodestar/test-utils": "^1.51.0-dev.a1ecd6c34f",
164
164
  "@types/js-yaml": "^4.0.5",
165
165
  "@types/qs": "^6.9.7",
166
166
  "@types/tmp": "^0.2.3",
@@ -175,5 +175,5 @@
175
175
  "beacon",
176
176
  "blockchain"
177
177
  ],
178
- "gitHead": "4049cf7c7999c6cabf363e7243520a3ba3cb691e"
178
+ "gitHead": "42cc5a38cbffc5d4d9c25418c3189f58571e7e9c"
179
179
  }
@@ -17,6 +17,7 @@ import {
17
17
  isStatePostGloas,
18
18
  } from "@lodestar/state-transition";
19
19
  import {
20
+ BuilderIndex,
20
21
  ValidatorIndex,
21
22
  getBuilderStatus,
22
23
  getValidatorStatus,
@@ -29,6 +30,7 @@ import {ApiModules} from "../../types.js";
29
30
  import {assertUniqueItems} from "../../utils.js";
30
31
  import {
31
32
  filterStateValidatorsByStatus,
33
+ getBuilderIndexByPubkey,
32
34
  getStateBuilderIndex,
33
35
  getStateResponseWithRegen,
34
36
  getStateValidatorIndex,
@@ -170,8 +172,15 @@ export function getBeaconStateApi({
170
172
  if (builderIds.length) {
171
173
  assertUniqueItems(builderIds, "Duplicate builder IDs provided");
172
174
 
175
+ // There is no builder pubkey cache, scan the registry at most once per request
176
+ let builderIndexByPubkey: Map<string, BuilderIndex> | null = null;
177
+ const getBuilderIndexByPubkeyMap = () => {
178
+ builderIndexByPubkey ??= getBuilderIndexByPubkey(state);
179
+ return builderIndexByPubkey;
180
+ };
181
+
173
182
  for (const id of builderIds) {
174
- const resp = getStateBuilderIndex(id, state);
183
+ const resp = getStateBuilderIndex(id, state, getBuilderIndexByPubkeyMap);
175
184
  if (resp.valid) {
176
185
  const builderIndex = resp.builderIndex;
177
186
  const builder = state.getBuilder(builderIndex);
@@ -13,7 +13,7 @@ import {
13
13
  getValidatorStatus,
14
14
  phase0,
15
15
  } from "@lodestar/types";
16
- import {byteArrayEquals, fromHex} from "@lodestar/utils";
16
+ import {fromHex, toHex} from "@lodestar/utils";
17
17
  import {IBeaconChain} from "../../../../chain/index.js";
18
18
  import {GENESIS_EPOCH} from "../../../../constants/index.js";
19
19
  import {IBeaconSync} from "../../../../sync/index.js";
@@ -118,9 +118,22 @@ type StateBuilderIndexResponse =
118
118
  | {valid: true; builderIndex: BuilderIndex}
119
119
  | {valid: false; code: number; reason: string};
120
120
 
121
+ export function getBuilderIndexByPubkey(state: IBeaconStateViewGloas): Map<string, BuilderIndex> {
122
+ const builderIndexByPubkey = new Map<string, BuilderIndex>();
123
+ const buildersLength = state.getBuildersLength();
124
+ for (let builderIndex = 0; builderIndex < buildersLength; builderIndex++) {
125
+ const pubkeyHex = toHex(state.getBuilder(builderIndex).pubkey);
126
+ if (!builderIndexByPubkey.has(pubkeyHex)) {
127
+ builderIndexByPubkey.set(pubkeyHex, builderIndex);
128
+ }
129
+ }
130
+ return builderIndexByPubkey;
131
+ }
132
+
121
133
  export function getStateBuilderIndex(
122
134
  id: routes.beacon.BuilderId | BLSPubkey,
123
- state: IBeaconStateViewGloas
135
+ state: IBeaconStateViewGloas,
136
+ getBuilderIndexByPubkeyMap: () => Map<string, BuilderIndex>
124
137
  ): StateBuilderIndexResponse {
125
138
  if (typeof id === "string") {
126
139
  // mutate `id` and fallthrough to below
@@ -148,14 +161,11 @@ export function getStateBuilderIndex(
148
161
  }
149
162
 
150
163
  // typeof id === Uint8Array
151
- // There is no builder pubkey cache, linear scan over the registry
152
- const buildersLength = state.getBuildersLength();
153
- for (let builderIndex = 0; builderIndex < buildersLength; builderIndex++) {
154
- if (byteArrayEquals(state.getBuilder(builderIndex).pubkey, id)) {
155
- return {valid: true, builderIndex};
156
- }
164
+ const builderIndex = getBuilderIndexByPubkeyMap().get(toHex(id));
165
+ if (builderIndex === undefined) {
166
+ return {valid: false, code: 404, reason: "Builder pubkey not found in state"};
157
167
  }
158
- return {valid: false, code: 404, reason: "Builder pubkey not found in state"};
168
+ return {valid: true, builderIndex};
159
169
  }
160
170
 
161
171
  export function filterStateValidatorsByStatus(
@@ -254,13 +254,13 @@ export function getValidatorApi(
254
254
  genesisBlockRoot = state.computeAnchorCheckpoint().checkpoint.root;
255
255
  } else if (state.slot < SLOTS_PER_HISTORICAL_ROOT) {
256
256
  genesisBlockRoot = state.getBlockRootAtSlot(GENESIS_SLOT);
257
- }
258
-
259
- const blockRes = await chain.getCanonicalBlockAtSlot(GENESIS_SLOT);
260
- if (blockRes) {
261
- genesisBlockRoot = config
262
- .getForkTypes(blockRes.block.message.slot)
263
- .SignedBeaconBlock.hashTreeRoot(blockRes.block);
257
+ } else {
258
+ const blockRes = await chain.getCanonicalBlockAtSlot(GENESIS_SLOT);
259
+ if (blockRes) {
260
+ genesisBlockRoot = config
261
+ .getForkTypes(blockRes.block.message.slot)
262
+ .BeaconBlock.hashTreeRoot(blockRes.block.message);
263
+ }
264
264
  }
265
265
  }
266
266
 
@@ -781,7 +781,6 @@ export async function prepareExecutionPayload(
781
781
 
782
782
  const payloadIdCached = chain.executionEngine.payloadIdCache.get({
783
783
  headBlockHash: toRootHex(parentBlockHash),
784
- finalizedBlockHash,
785
784
  timestamp: numToQuantity(timestamp),
786
785
  prevRandao: toHex(prevRandao),
787
786
  suggestedFeeRecipient,
@@ -367,7 +367,6 @@ export class ExecutionEngineHttp implements IExecutionEngine {
367
367
  this.payloadIdCache.add(
368
368
  {
369
369
  headBlockHash,
370
- finalizedBlockHash,
371
370
  ...payloadAttributesRpc,
372
371
  inclusionListTransactions: payloadAttributes.inclusionListTransactions,
373
372
  },
@@ -19,10 +19,9 @@ export type WithdrawalV1 = {
19
19
  amount: QUANTITY;
20
20
  };
21
21
 
22
- type FcuAttributes = {headBlockHash: DATA; finalizedBlockHash: DATA} & Omit<
23
- PayloadAttributesRpc,
24
- "withdrawals" | "inclusionListTransactions"
25
- > & {inclusionListTransactions?: bellatrix.Transactions};
22
+ type FcuAttributes = {headBlockHash: DATA} & Omit<PayloadAttributesRpc, "withdrawals" | "inclusionListTransactions"> & {
23
+ inclusionListTransactions?: bellatrix.Transactions;
24
+ };
26
25
 
27
26
  export class PayloadIdCache {
28
27
  private readonly payloadIdByFcuAttributes = new Map<
@@ -30,8 +29,8 @@ export class PayloadIdCache {
30
29
  {payloadId: PayloadId; fullKey: string; inclusionListTransactions?: bellatrix.Transactions}
31
30
  >();
32
31
 
33
- getFullKey({headBlockHash, finalizedBlockHash, timestamp, prevRandao, suggestedFeeRecipient}: FcuAttributes): string {
34
- return `${headBlockHash}-${finalizedBlockHash}-${timestamp}-${prevRandao}-${suggestedFeeRecipient}`;
32
+ getFullKey({headBlockHash, timestamp, prevRandao, suggestedFeeRecipient}: FcuAttributes): string {
33
+ return `${headBlockHash}-${timestamp}-${prevRandao}-${suggestedFeeRecipient}`;
35
34
  }
36
35
  getKey({timestamp}: Pick<FcuAttributes, "timestamp">): string {
37
36
  return timestamp;
@@ -867,19 +867,20 @@ export class NetworkProcessor {
867
867
  const nowSec = Date.now() / 1000;
868
868
  const minSlot = clockSlot - MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE;
869
869
 
870
- for (const [slot, searchedRoots] of this.searchedRootsBySlot) {
870
+ for (const [slot, bufferedRoots] of this.bufferedRootsBySlot) {
871
871
  if (slot > minSlot) continue;
872
872
 
873
- // expire messages awaiting an unknown block for these roots. Only roots this slot did a BLOCK search
874
- // for (blockPeerIds set) have block waits keyed here; skipping the rest avoids deleting a payload wait
875
- // that a newer slot created for the same root (block/envelope searches share searchedRootsBySlot).
876
- for (const [rootHex, entry] of searchedRoots) {
877
- if (entry.blockPeerIds === undefined) continue;
873
+ for (const rootHex of bufferedRoots) {
878
874
  const messagesByTopic = this.awaitingMessagesByBlockRoot.get(rootHex);
879
875
  if (messagesByTopic === undefined) continue;
880
- let removed = 0;
876
+ let remaining = 0;
881
877
  for (const messages of messagesByTopic.values()) {
882
878
  for (const message of messages) {
879
+ // the same root may be buffered again from a newer slot, keep those messages.
880
+ // msgSlot is always set before buffering, a missing one expires rather than leaks
881
+ if ((message.msgSlot ?? minSlot) > minSlot) continue;
882
+ messages.delete(message);
883
+ this.awaitingBlockMessageCount--;
883
884
  const topicType = message.topic.type;
884
885
  this.metrics?.awaitingBlockGossipMessages.reject.inc({
885
886
  topic: topicType,
@@ -891,22 +892,22 @@ export class NetworkProcessor {
891
892
  );
892
893
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
893
894
  }
894
- removed += messages.size;
895
- }
896
- if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
897
- this.awaitingBlockMessageCount -= removed;
895
+ remaining += messages.size;
898
896
  }
897
+ if (remaining === 0) this.awaitingMessagesByBlockRoot.delete(rootHex);
899
898
  }
900
899
 
901
- // expire messages awaiting an unknown payload envelope for these roots. Symmetric to the block pass:
902
- // only roots this slot did an ENVELOPE search for (envelopePeerIds set) have payload waits keyed here.
903
- for (const [rootHex, entry] of searchedRoots) {
904
- if (entry.envelopePeerIds === undefined) continue;
900
+ for (const rootHex of bufferedRoots) {
905
901
  const messagesByTopic = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
906
902
  if (messagesByTopic === undefined) continue;
907
- let removed = 0;
903
+ let remaining = 0;
908
904
  for (const messages of messagesByTopic.values()) {
909
905
  for (const message of messages) {
906
+ // the same root may be buffered again from a newer slot, keep those messages.
907
+ // msgSlot is always set before buffering, a missing one expires rather than leaks
908
+ if ((message.msgSlot ?? minSlot) > minSlot) continue;
909
+ messages.delete(message);
910
+ this.awaitingPayloadMessageCount--;
910
911
  const topicType = message.topic.type;
911
912
  this.metrics?.awaitingPayloadGossipMessages.reject.inc({
912
913
  topic: topicType,
@@ -918,18 +919,16 @@ export class NetworkProcessor {
918
919
  );
919
920
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
920
921
  }
921
- removed += messages.size;
922
- }
923
- if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
924
- this.awaitingPayloadMessageCount -= removed;
922
+ remaining += messages.size;
925
923
  }
924
+ if (remaining === 0) this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
926
925
  }
927
926
 
928
- this.searchedRootsBySlot.delete(slot);
927
+ this.bufferedRootsBySlot.delete(slot);
929
928
  }
930
929
 
931
- for (const slot of this.bufferedRootsBySlot.keys()) {
932
- if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
930
+ for (const slot of this.searchedRootsBySlot.keys()) {
931
+ if (slot <= minSlot) this.searchedRootsBySlot.delete(slot);
933
932
  }
934
933
 
935
934
  for (const [peer, slot] of this.fatalPeers) {
@@ -11,7 +11,7 @@ export type GossipAttestationsWork = {
11
11
  export type PendingGossipsubMessage = {
12
12
  topic: GossipTopic;
13
13
  msg: Message;
14
- // only available for beacon_attestation and aggregate_and_proof
14
+ // set for every topic with a slot, held messages expire by it
15
15
  msgSlot?: Slot;
16
16
  msgId: string;
17
17
  propagationSource: PeerIdStr;