@lodestar/beacon-node 1.50.0-dev.072bf98853 → 1.50.0-dev.08c4bed201

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 (62) hide show
  1. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/state/index.js +6 -4
  3. package/lib/api/impl/beacon/state/index.js.map +1 -1
  4. package/lib/chain/chain.d.ts.map +1 -1
  5. package/lib/chain/chain.js +2 -0
  6. package/lib/chain/chain.js.map +1 -1
  7. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  8. package/lib/chain/opPools/opPool.js +1 -23
  9. package/lib/chain/opPools/opPool.js.map +1 -1
  10. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -0
  11. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  12. package/lib/chain/opPools/proposerPreferencesPool.js +13 -1
  13. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  14. package/lib/chain/opPools/utils.d.ts +11 -0
  15. package/lib/chain/opPools/utils.d.ts.map +1 -1
  16. package/lib/chain/opPools/utils.js +22 -0
  17. package/lib/chain/opPools/utils.js.map +1 -1
  18. package/lib/db/beacon.d.ts +2 -1
  19. package/lib/db/beacon.d.ts.map +1 -1
  20. package/lib/db/beacon.js +3 -1
  21. package/lib/db/beacon.js.map +1 -1
  22. package/lib/db/buckets.d.ts +2 -1
  23. package/lib/db/buckets.d.ts.map +1 -1
  24. package/lib/db/buckets.js +1 -0
  25. package/lib/db/buckets.js.map +1 -1
  26. package/lib/db/interface.d.ts +2 -1
  27. package/lib/db/interface.d.ts.map +1 -1
  28. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  29. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  30. package/lib/db/repositories/index.d.ts +1 -0
  31. package/lib/db/repositories/index.d.ts.map +1 -1
  32. package/lib/db/repositories/index.js +1 -0
  33. package/lib/db/repositories/index.js.map +1 -1
  34. package/lib/db/repositories/proposerPreferences.d.ts +14 -0
  35. package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
  36. package/lib/db/repositories/proposerPreferences.js +21 -0
  37. package/lib/db/repositories/proposerPreferences.js.map +1 -0
  38. package/lib/network/core/events.d.ts +5 -1
  39. package/lib/network/core/events.d.ts.map +1 -1
  40. package/lib/network/core/events.js +8 -0
  41. package/lib/network/core/events.js.map +1 -1
  42. package/lib/util/asyncIterableToEvents.d.ts +10 -2
  43. package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
  44. package/lib/util/asyncIterableToEvents.js +29 -3
  45. package/lib/util/asyncIterableToEvents.js.map +1 -1
  46. package/lib/util/metadata.d.ts.map +1 -1
  47. package/lib/util/metadata.js +2 -1
  48. package/lib/util/metadata.js.map +1 -1
  49. package/package.json +15 -15
  50. package/src/api/impl/beacon/state/index.ts +13 -4
  51. package/src/chain/chain.ts +2 -0
  52. package/src/chain/opPools/opPool.ts +1 -31
  53. package/src/chain/opPools/proposerPreferencesPool.ts +20 -1
  54. package/src/chain/opPools/utils.ts +31 -0
  55. package/src/db/beacon.ts +3 -0
  56. package/src/db/buckets.ts +1 -0
  57. package/src/db/interface.ts +2 -0
  58. package/src/db/repositories/index.ts +1 -0
  59. package/src/db/repositories/proposerPreferences.ts +23 -0
  60. package/src/network/core/events.ts +16 -1
  61. package/src/util/asyncIterableToEvents.ts +44 -5
  62. package/src/util/metadata.ts +2 -1
@@ -55,7 +55,10 @@ export class AsyncIterableBridgeCaller {
55
55
  },
56
56
  async return() {
57
57
  // This will be reached if the consumer called 'break' or 'return' early in the loop.
58
- self.pending.delete(id);
58
+ // Without the cancel the handler keeps producing items that cross the thread boundary just to be dropped
59
+ if (self.pending.delete(id)) {
60
+ self.events.emitRequestCancel({ id });
61
+ }
59
62
  return { value: undefined, done: true };
60
63
  },
61
64
  };
@@ -92,18 +95,29 @@ export class AsyncIterableBridgeCaller {
92
95
  export class AsyncIterableBridgeHandler {
93
96
  events;
94
97
  handler;
98
+ iterators = new Map();
95
99
  constructor(events, handler) {
96
100
  this.events = events;
97
101
  this.handler = handler;
98
102
  events.onRequest(this.onRequest.bind(this));
103
+ events.onRequestCancel(this.onRequestCancel.bind(this));
104
+ }
105
+ get iteratingCount() {
106
+ return this.iterators.size;
99
107
  }
100
108
  async onRequest(data) {
101
109
  try {
102
- for await (const item of this.handler(data.callArgs)) {
110
+ const iterator = this.handler(data.callArgs)[Symbol.asyncIterator]();
111
+ this.iterators.set(data.id, iterator);
112
+ while (true) {
113
+ const result = await iterator.next();
114
+ if (result.done) {
115
+ break;
116
+ }
103
117
  this.events.emitResponse({
104
118
  type: IteratorEventType.next,
105
119
  id: data.id,
106
- item,
120
+ item: result.value,
107
121
  });
108
122
  }
109
123
  this.events.emitResponse({
@@ -118,6 +132,18 @@ export class AsyncIterableBridgeHandler {
118
132
  error: toThreadBoundaryError(e),
119
133
  });
120
134
  }
135
+ finally {
136
+ this.iterators.delete(data.id);
137
+ }
138
+ }
139
+ onRequestCancel(data) {
140
+ const iterator = this.iterators.get(data.id);
141
+ if (!iterator?.return) {
142
+ return;
143
+ }
144
+ // An async generator queues return() behind the next() that is in flight, so at most one more
145
+ // item is produced before its finally blocks run. Any error surfaces through that next() call.
146
+ void iterator.return().catch(() => { });
121
147
  }
122
148
  }
123
149
  //# sourceMappingURL=asyncIterableToEvents.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"asyncIterableToEvents.js","sourceRoot":"","sources":["../../src/util/asyncIterableToEvents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"asyncIterableToEvents.js","sourceRoot":"","sources":["../../src/util/asyncIterableToEvents.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/util/metadata.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEhE,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAC,GAAG,aAAa,CAOlG;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAEpD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAE3D"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/util/metadata.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEhE,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAC,GAAG,aAAa,CAQlG;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAEpD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAE3D"}
@@ -5,7 +5,8 @@ export function getLodestarClientVersion(info) {
5
5
  code: ClientCode.LS,
6
6
  name: "Lodestar",
7
7
  version: info?.version ?? "",
8
- commit: info?.commit?.slice(0, 8) ?? "",
8
+ // engine_getClientVersionV1 defines commit as 4 bytes, so builds without git data must not send an empty one
9
+ commit: info?.commit ? info.commit.slice(0, 8) : "00000000",
9
10
  };
10
11
  }
11
12
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/util/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,UAAU,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAC,UAAU,EAAgB,MAAM,uBAAuB,CAAC;AAEhE,MAAM,UAAU,wBAAwB,CAAC,IAA0C;IACjF,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,EAAE;QACnB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE;QAC5B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;KACxC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAoB;IACjD,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/util/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,UAAU,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAC,UAAU,EAAgB,MAAM,uBAAuB,CAAC;AAEhE,MAAM,UAAU,wBAAwB,CAAC,IAA0C;IACjF,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,EAAE;QACnB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE;QAC5B,6GAA6G;QAC7G,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;KAC5D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAoB;IACjD,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC"}
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.50.0-dev.072bf98853",
14
+ "version": "1.50.0-dev.08c4bed201",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -127,17 +127,17 @@
127
127
  "@libp2p/prometheus-metrics": "^5.0.28",
128
128
  "@libp2p/tcp": "^11.0.27",
129
129
  "@libp2p/utils": "^7.4.0",
130
- "@lodestar/api": "^1.50.0-dev.072bf98853",
131
- "@lodestar/config": "^1.50.0-dev.072bf98853",
132
- "@lodestar/db": "^1.50.0-dev.072bf98853",
133
- "@lodestar/fork-choice": "^1.50.0-dev.072bf98853",
134
- "@lodestar/logger": "^1.50.0-dev.072bf98853",
135
- "@lodestar/params": "^1.50.0-dev.072bf98853",
136
- "@lodestar/reqresp": "^1.50.0-dev.072bf98853",
137
- "@lodestar/state-transition": "^1.50.0-dev.072bf98853",
138
- "@lodestar/types": "^1.50.0-dev.072bf98853",
139
- "@lodestar/utils": "^1.50.0-dev.072bf98853",
140
- "@lodestar/validator": "^1.50.0-dev.072bf98853",
130
+ "@lodestar/api": "^1.50.0-dev.08c4bed201",
131
+ "@lodestar/config": "^1.50.0-dev.08c4bed201",
132
+ "@lodestar/db": "^1.50.0-dev.08c4bed201",
133
+ "@lodestar/fork-choice": "^1.50.0-dev.08c4bed201",
134
+ "@lodestar/logger": "^1.50.0-dev.08c4bed201",
135
+ "@lodestar/params": "^1.50.0-dev.08c4bed201",
136
+ "@lodestar/reqresp": "^1.50.0-dev.08c4bed201",
137
+ "@lodestar/state-transition": "^1.50.0-dev.08c4bed201",
138
+ "@lodestar/types": "^1.50.0-dev.08c4bed201",
139
+ "@lodestar/utils": "^1.50.0-dev.08c4bed201",
140
+ "@lodestar/validator": "^1.50.0-dev.08c4bed201",
141
141
  "@multiformats/multiaddr": "^13.0.1",
142
142
  "datastore-core": "^12.0.1",
143
143
  "datastore-level": "^13.0.1",
@@ -158,8 +158,8 @@
158
158
  "devDependencies": {
159
159
  "@libp2p/interface-internal": "^3.1.12",
160
160
  "@libp2p/logger": "^6.2.13",
161
- "@lodestar/spec-test-util": "^1.50.0-dev.072bf98853",
162
- "@lodestar/test-utils": "^1.50.0-dev.072bf98853",
161
+ "@lodestar/spec-test-util": "^1.50.0-dev.08c4bed201",
162
+ "@lodestar/test-utils": "^1.50.0-dev.08c4bed201",
163
163
  "@types/js-yaml": "^4.0.5",
164
164
  "@types/qs": "^6.9.7",
165
165
  "@types/tmp": "^0.2.3",
@@ -174,5 +174,5 @@
174
174
  "beacon",
175
175
  "blockchain"
176
176
  ],
177
- "gitHead": "f61f24d517de6a960aa1e7f4eda106f018b9bce9"
177
+ "gitHead": "70891683ed33187aca902723b5515d6a164ada13"
178
178
  }
@@ -16,7 +16,14 @@ import {
16
16
  isStatePostFulu,
17
17
  isStatePostGloas,
18
18
  } from "@lodestar/state-transition";
19
- import {ValidatorIndex, getBuilderStatus, getValidatorStatus, mapToGeneralStatus, ssz} from "@lodestar/types";
19
+ import {
20
+ ValidatorIndex,
21
+ getBuilderStatus,
22
+ getValidatorStatus,
23
+ mapToGeneralStatus,
24
+ ssz,
25
+ sszTypesFor,
26
+ } from "@lodestar/types";
20
27
  import {ApiError} from "../../errors.js";
21
28
  import {ApiModules} from "../../types.js";
22
29
  import {assertUniqueItems} from "../../utils.js";
@@ -413,7 +420,9 @@ export function getBeaconStateApi({
413
420
  const pendingDeposits = state.pendingDeposits;
414
421
 
415
422
  return {
416
- data: context?.returnBytes ? ssz.electra.PendingDeposits.serialize(pendingDeposits) : pendingDeposits,
423
+ data: context?.returnBytes
424
+ ? sszTypesFor(state.forkName).PendingDeposits.serialize(pendingDeposits)
425
+ : pendingDeposits,
417
426
  meta: {executionOptimistic, finalized, version: fork},
418
427
  };
419
428
  },
@@ -430,7 +439,7 @@ export function getBeaconStateApi({
430
439
 
431
440
  return {
432
441
  data: context?.returnBytes
433
- ? ssz.electra.PendingPartialWithdrawals.serialize(pendingPartialWithdrawals)
442
+ ? sszTypesFor(state.forkName).PendingPartialWithdrawals.serialize(pendingPartialWithdrawals)
434
443
  : pendingPartialWithdrawals,
435
444
  meta: {executionOptimistic, finalized, version: fork},
436
445
  };
@@ -448,7 +457,7 @@ export function getBeaconStateApi({
448
457
 
449
458
  return {
450
459
  data: context?.returnBytes
451
- ? ssz.electra.PendingConsolidations.serialize(pendingConsolidations)
460
+ ? sszTypesFor(state.forkName).PendingConsolidations.serialize(pendingConsolidations)
452
461
  : pendingConsolidations,
453
462
  meta: {executionOptimistic, finalized, version: fork},
454
463
  };
@@ -620,12 +620,14 @@ export class BeaconChain implements IBeaconChain {
620
620
  async loadFromDisk(): Promise<void> {
621
621
  await this.regen.init();
622
622
  await this.opPool.fromPersisted(this.db, this.getHeadState(), this.bls, this.clock.currentSlot);
623
+ await this.proposerPreferencesPool.fromPersisted(this.db, this.clock.currentSlot);
623
624
  }
624
625
 
625
626
  /** Persist in-memory data to the DB. Call at least once before stopping the process */
626
627
  async persistToDisk(): Promise<void> {
627
628
  await this.archiveStore.persistToDisk();
628
629
  await this.opPool.toPersisted(this.db);
630
+ await this.proposerPreferencesPool.toPersisted(this.db);
629
631
  }
630
632
 
631
633
  getHeadState(): IBeaconStateView {
@@ -1,5 +1,4 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
- import {DbBatch, Id, Repository} from "@lodestar/db";
3
2
  import {
4
3
  BLS_WITHDRAWAL_PREFIX,
5
4
  ForkName,
@@ -42,6 +41,7 @@ import {
42
41
  getAttesterSlashingSignatureDomains,
43
42
  getProposerSlashingSignatureDomain,
44
43
  isValidBlsToExecutionChangeForBlockInclusion,
44
+ persistDiff,
45
45
  } from "./utils.js";
46
46
 
47
47
  type HexRoot = string;
@@ -500,33 +500,3 @@ function isVoluntaryExitSignatureIncludable(stateFork: ForkSeq, voluntaryExitFor
500
500
  function isSlashableAtEpoch(validator: phase0.Validator, epoch: Epoch): boolean {
501
501
  return !validator.slashed && validator.activationEpoch <= epoch && epoch < validator.withdrawableEpoch;
502
502
  }
503
-
504
- /**
505
- * Persist target items `items` in `dbRepo` doing minimum put and delete writes.
506
- * Reads all keys in repository to compute the diff between current persisted data and target data.
507
- */
508
- async function persistDiff<K extends Id, V>(
509
- dbRepo: Repository<K, V>,
510
- items: {key: K; value: V}[],
511
- serializeKey: (key: K) => number | string,
512
- opts: {updateExisting?: boolean} = {}
513
- ): Promise<void> {
514
- const persistedKeys = await dbRepo.keys();
515
- const batch: DbBatch<K, V> = [];
516
-
517
- const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
518
- for (const item of items) {
519
- if (opts.updateExisting || !persistedKeysSerialized.has(serializeKey(item.key))) {
520
- batch.push({type: "put", key: item.key, value: item.value});
521
- }
522
- }
523
-
524
- const targetKeysSerialized = new Set(items.map((item) => serializeKey(item.key)));
525
- for (const persistedKey of persistedKeys) {
526
- if (!targetKeysSerialized.has(serializeKey(persistedKey))) {
527
- batch.push({type: "del", key: persistedKey});
528
- }
529
- }
530
-
531
- if (batch.length > 0) await dbRepo.batch(batch);
532
- }
@@ -1,5 +1,7 @@
1
1
  import {RootHex, Slot, ValidatorIndex, gloas} from "@lodestar/types";
2
- import {toRootHex} from "@lodestar/utils";
2
+ import {toHex, toRootHex} from "@lodestar/utils";
3
+ import {IBeaconDb} from "../../db/index.js";
4
+ import {persistDiff} from "./utils.js";
3
5
 
4
6
  /**
5
7
  * Pool of validated `SignedProposerPreferences` indexed by `(slot, dependent_root)`.
@@ -64,4 +66,21 @@ export class ProposerPreferencesPool {
64
66
  if (slot < currentSlot) this.bySlot.delete(slot);
65
67
  }
66
68
  }
69
+
70
+ /** Restore the preferences of upcoming slots, they were validated before they were persisted */
71
+ async fromPersisted(db: IBeaconDb, currentSlot: Slot): Promise<void> {
72
+ for (const signed of await db.proposerPreferences.values()) {
73
+ if (signed.message.proposalSlot >= currentSlot) {
74
+ this.add(signed);
75
+ }
76
+ }
77
+ }
78
+
79
+ async toPersisted(db: IBeaconDb): Promise<void> {
80
+ await persistDiff(
81
+ db.proposerPreferences,
82
+ this.getAll().map((signed) => ({key: db.proposerPreferences.getId(signed), value: signed})),
83
+ toHex
84
+ );
85
+ }
67
86
  }
@@ -1,5 +1,6 @@
1
1
  import {Signature} from "@chainsafe/lodestar-z/blst";
2
2
  import {BeaconConfig} from "@lodestar/config";
3
+ import {DbBatch, Id, Repository} from "@lodestar/db";
3
4
  import {BLS_WITHDRAWAL_PREFIX, DOMAIN_BEACON_ATTESTER, DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
4
5
  import {IBeaconStateView, computeStartSlotAtEpoch} from "@lodestar/state-transition";
5
6
  import {AttesterSlashing, Domain, Slot, capella, phase0} from "@lodestar/types";
@@ -91,3 +92,33 @@ export function isValidBlsToExecutionChangeForBlockInclusion(
91
92
  export function isElectraAggregate(aggregate: AggregateFast): aggregate is AggregateFastElectra {
92
93
  return (aggregate as AggregateFastElectra).committeeBits !== undefined;
93
94
  }
95
+
96
+ /**
97
+ * Persist target items `items` in `dbRepo` doing minimum put and delete writes.
98
+ * Reads all keys in repository to compute the diff between current persisted data and target data.
99
+ */
100
+ export async function persistDiff<K extends Id, V>(
101
+ dbRepo: Repository<K, V>,
102
+ items: {key: K; value: V}[],
103
+ serializeKey: (key: K) => number | string,
104
+ opts: {updateExisting?: boolean} = {}
105
+ ): Promise<void> {
106
+ const persistedKeys = await dbRepo.keys();
107
+ const batch: DbBatch<K, V> = [];
108
+
109
+ const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
110
+ for (const item of items) {
111
+ if (opts.updateExisting || !persistedKeysSerialized.has(serializeKey(item.key))) {
112
+ batch.push({type: "put", key: item.key, value: item.value});
113
+ }
114
+ }
115
+
116
+ const targetKeysSerialized = new Set(items.map((item) => serializeKey(item.key)));
117
+ for (const persistedKey of persistedKeys) {
118
+ if (!targetKeysSerialized.has(serializeKey(persistedKey))) {
119
+ batch.push({type: "del", key: persistedKey});
120
+ }
121
+ }
122
+
123
+ if (batch.length > 0) await dbRepo.batch(batch);
124
+ }
package/src/db/beacon.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  DataColumnSidecarRepository,
23
23
  ExecutionPayloadEnvelopeArchiveRepository,
24
24
  ExecutionPayloadEnvelopeRepository,
25
+ ProposerPreferencesRepository,
25
26
  ProposerSlashingRepository,
26
27
  StateArchiveRepository,
27
28
  SyncCommitteeRepository,
@@ -53,6 +54,7 @@ export class BeaconDb implements IBeaconDb {
53
54
 
54
55
  voluntaryExit: VoluntaryExitRepository;
55
56
  proposerSlashing: ProposerSlashingRepository;
57
+ proposerPreferences: ProposerPreferencesRepository;
56
58
  attesterSlashing: AttesterSlashingRepository;
57
59
  blsToExecutionChange: BLSToExecutionChangeRepository;
58
60
 
@@ -89,6 +91,7 @@ export class BeaconDb implements IBeaconDb {
89
91
  this.voluntaryExit = new VoluntaryExitRepository(config, db);
90
92
  this.blsToExecutionChange = new BLSToExecutionChangeRepository(config, db);
91
93
  this.proposerSlashing = new ProposerSlashingRepository(config, db);
94
+ this.proposerPreferences = new ProposerPreferencesRepository(config, db);
92
95
  this.attesterSlashing = new AttesterSlashingRepository(config, db);
93
96
 
94
97
  // lightclient
package/src/db/buckets.ts CHANGED
@@ -73,6 +73,7 @@ export enum Bucket {
73
73
  gloas_executionPayloadEnvelope = 59, // GLOAS BeaconBlockRoot -> SignedExecutionPayloadEnvelope
74
74
  gloas_executionPayloadEnvelopeArchive = 60, // GLOAS Slot -> SignedExecutionPayloadEnvelope, or 0x00 + SignedExecutionPayloadHeaderEnvelope
75
75
  earliestAvailableSlot = 61, // singleton -> Slot
76
+ gloas_proposerPreferences = 62, // Slot || DependentRoot -> SignedProposerPreferences
76
77
  }
77
78
 
78
79
  export function getBucketNameByValue<T extends Bucket>(enumValue: T): keyof typeof Bucket {
@@ -17,6 +17,7 @@ import {
17
17
  DataColumnSidecarRepository,
18
18
  ExecutionPayloadEnvelopeArchiveRepository,
19
19
  ExecutionPayloadEnvelopeRepository,
20
+ ProposerPreferencesRepository,
20
21
  ProposerSlashingRepository,
21
22
  StateArchiveRepository,
22
23
  SyncCommitteeRepository,
@@ -52,6 +53,7 @@ export interface IBeaconDb {
52
53
  // op pool
53
54
  voluntaryExit: VoluntaryExitRepository;
54
55
  proposerSlashing: ProposerSlashingRepository;
56
+ proposerPreferences: ProposerPreferencesRepository;
55
57
  attesterSlashing: AttesterSlashingRepository;
56
58
  blsToExecutionChange: BLSToExecutionChangeRepository;
57
59
 
@@ -18,6 +18,7 @@ export {BestLightClientUpdateRepository} from "./lightclientBestUpdate.js";
18
18
  export {CheckpointHeaderRepository} from "./lightclientCheckpointHeader.js";
19
19
  export {SyncCommitteeRepository} from "./lightclientSyncCommittee.js";
20
20
  export {SyncCommitteeWitnessRepository} from "./lightclientSyncCommitteeWitness.js";
21
+ export {ProposerPreferencesRepository} from "./proposerPreferences.js";
21
22
  export {ProposerSlashingRepository} from "./proposerSlashing.js";
22
23
  export {StateArchiveRepository} from "./stateArchive.js";
23
24
  export {VoluntaryExitRepository} from "./voluntaryExit.js";
@@ -0,0 +1,23 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {Db, Repository} from "@lodestar/db";
3
+ import {gloas, ssz} from "@lodestar/types";
4
+ import {intToBytes} from "@lodestar/utils";
5
+ import {Bucket, getBucketNameByValue} from "../buckets.js";
6
+
7
+ /**
8
+ * SignedProposerPreferences indexed by proposal slot and dependent root
9
+ *
10
+ * Added via gossip or api
11
+ * Removed once the proposal slot has passed
12
+ */
13
+ export class ProposerPreferencesRepository extends Repository<Uint8Array, gloas.SignedProposerPreferences> {
14
+ constructor(config: ChainForkConfig, db: Db) {
15
+ const bucket = Bucket.gloas_proposerPreferences;
16
+ super(config, db, bucket, ssz.gloas.SignedProposerPreferences, getBucketNameByValue(bucket));
17
+ }
18
+
19
+ getId(value: gloas.SignedProposerPreferences): Uint8Array {
20
+ const {proposalSlot, dependentRoot} = value.message;
21
+ return Buffer.concat([intToBytes(proposalSlot, 8, "be"), dependentRoot]);
22
+ }
23
+ }
@@ -1,21 +1,30 @@
1
1
  import EventEmitter from "node:events";
2
2
  import {ResponseIncoming, ResponseOutgoing} from "@lodestar/reqresp";
3
- import {AsyncIterableEventBus, IteratorEvent, RequestEvent} from "../../util/asyncIterableToEvents.js";
3
+ import {
4
+ AsyncIterableEventBus,
5
+ IteratorEvent,
6
+ RequestCancelEvent,
7
+ RequestEvent,
8
+ } from "../../util/asyncIterableToEvents.js";
4
9
  import {StrictEventEmitterSingleArg} from "../../util/strictEvents.js";
5
10
  import {EventDirection} from "../events.js";
6
11
  import {IncomingRequestArgs, OutgoingRequestArgs} from "../reqresp/types.js";
7
12
 
8
13
  export enum ReqRespBridgeEvent {
9
14
  outgoingRequest = "reqresp.outgoingRequest",
15
+ outgoingRequestCancel = "reqresp.outgoingRequestCancel",
10
16
  outgoingResponse = "reqresp.outgoingResponse",
11
17
  incomingRequest = "reqresp.incomingRequest",
18
+ incomingRequestCancel = "reqresp.incomingRequestCancel",
12
19
  incomingResponse = "reqresp.incomingResponse",
13
20
  }
14
21
 
15
22
  export type ReqRespBridgeEventData = {
16
23
  [ReqRespBridgeEvent.outgoingRequest]: RequestEvent<OutgoingRequestArgs>;
24
+ [ReqRespBridgeEvent.outgoingRequestCancel]: RequestCancelEvent;
17
25
  [ReqRespBridgeEvent.outgoingResponse]: IteratorEvent<ResponseOutgoing>;
18
26
  [ReqRespBridgeEvent.incomingRequest]: RequestEvent<IncomingRequestArgs>;
27
+ [ReqRespBridgeEvent.incomingRequestCancel]: RequestCancelEvent;
19
28
  [ReqRespBridgeEvent.incomingResponse]: IteratorEvent<ResponseIncoming>;
20
29
  };
21
30
 
@@ -26,8 +35,10 @@ export class ReqRespBridgeEventBus extends (EventEmitter as {new (): IReqRespBri
26
35
  // NOTE: If the same event is on this two arrays it can create an infinite cycle
27
36
  export const reqRespBridgeEventDirection: Record<ReqRespBridgeEvent, EventDirection> = {
28
37
  [ReqRespBridgeEvent.outgoingRequest]: EventDirection.mainToWorker,
38
+ [ReqRespBridgeEvent.outgoingRequestCancel]: EventDirection.mainToWorker,
29
39
  [ReqRespBridgeEvent.outgoingResponse]: EventDirection.mainToWorker,
30
40
  [ReqRespBridgeEvent.incomingRequest]: EventDirection.workerToMain,
41
+ [ReqRespBridgeEvent.incomingRequestCancel]: EventDirection.workerToMain,
31
42
  [ReqRespBridgeEvent.incomingResponse]: EventDirection.workerToMain,
32
43
  };
33
44
 
@@ -36,8 +47,10 @@ export function getReqRespBridgeReqEvents(
36
47
  ): AsyncIterableEventBus<OutgoingRequestArgs, ResponseIncoming> {
37
48
  return {
38
49
  emitRequest: (data) => events.emit(ReqRespBridgeEvent.outgoingRequest, data),
50
+ emitRequestCancel: (data) => events.emit(ReqRespBridgeEvent.outgoingRequestCancel, data),
39
51
  emitResponse: (data) => events.emit(ReqRespBridgeEvent.incomingResponse, data),
40
52
  onRequest: (cb) => events.on(ReqRespBridgeEvent.outgoingRequest, cb),
53
+ onRequestCancel: (cb) => events.on(ReqRespBridgeEvent.outgoingRequestCancel, cb),
41
54
  onResponse: (cb) => events.on(ReqRespBridgeEvent.incomingResponse, cb),
42
55
  };
43
56
  }
@@ -47,8 +60,10 @@ export function getReqRespBridgeRespEvents(
47
60
  ): AsyncIterableEventBus<IncomingRequestArgs, ResponseOutgoing> {
48
61
  return {
49
62
  emitRequest: (data) => events.emit(ReqRespBridgeEvent.incomingRequest, data),
63
+ emitRequestCancel: (data) => events.emit(ReqRespBridgeEvent.incomingRequestCancel, data),
50
64
  emitResponse: (data) => events.emit(ReqRespBridgeEvent.outgoingResponse, data),
51
65
  onRequest: (cb) => events.on(ReqRespBridgeEvent.incomingRequest, cb),
66
+ onRequestCancel: (cb) => events.on(ReqRespBridgeEvent.incomingRequestCancel, cb),
52
67
  onResponse: (cb) => events.on(ReqRespBridgeEvent.outgoingResponse, cb),
53
68
  };
54
69
  }
@@ -6,6 +6,10 @@ export type RequestEvent<T> = {
6
6
  id: number;
7
7
  };
8
8
 
9
+ export type RequestCancelEvent = {
10
+ id: number;
11
+ };
12
+
9
13
  export enum IteratorEventType {
10
14
  next = "iterator.next",
11
15
  done = "iterator.done",
@@ -19,8 +23,10 @@ export type IteratorEvent<V> =
19
23
 
20
24
  export type AsyncIterableEventBus<Args, Item> = {
21
25
  emitRequest(data: RequestEvent<Args>): void;
26
+ emitRequestCancel(data: RequestCancelEvent): void;
22
27
  emitResponse(data: IteratorEvent<Item>): void;
23
28
  onRequest(handler: (data: RequestEvent<Args>) => void): void;
29
+ onRequestCancel(handler: (data: RequestCancelEvent) => void): void;
24
30
  onResponse(handler: (data: IteratorEvent<Item>) => void): void;
25
31
  };
26
32
 
@@ -37,7 +43,9 @@ export class AsyncIterableBridgeCaller<Args, Item> {
37
43
  // TODO: Consider expiring the requests after no reply for long enough, t
38
44
  private readonly pending = new Map<number, PendingItem<Item>>();
39
45
 
40
- constructor(private readonly events: Pick<AsyncIterableEventBus<Args, Item>, "onResponse" | "emitRequest">) {
46
+ constructor(
47
+ private readonly events: Pick<AsyncIterableEventBus<Args, Item>, "onResponse" | "emitRequest" | "emitRequestCancel">
48
+ ) {
41
49
  events.onResponse(this.onResponse.bind(this));
42
50
  }
43
51
 
@@ -89,7 +97,10 @@ export class AsyncIterableBridgeCaller<Args, Item> {
89
97
 
90
98
  async return() {
91
99
  // This will be reached if the consumer called 'break' or 'return' early in the loop.
92
- self.pending.delete(id);
100
+ // Without the cancel the handler keeps producing items that cross the thread boundary just to be dropped
101
+ if (self.pending.delete(id)) {
102
+ self.events.emitRequestCancel({id});
103
+ }
93
104
  return {value: undefined, done: true};
94
105
  },
95
106
  };
@@ -132,20 +143,35 @@ export class AsyncIterableBridgeCaller<Args, Item> {
132
143
  }
133
144
 
134
145
  export class AsyncIterableBridgeHandler<Args, Item> {
146
+ private readonly iterators = new Map<number, AsyncIterator<Item>>();
147
+
135
148
  constructor(
136
- private readonly events: Pick<AsyncIterableEventBus<Args, Item>, "onRequest" | "emitResponse">,
149
+ private readonly events: Pick<AsyncIterableEventBus<Args, Item>, "onRequest" | "onRequestCancel" | "emitResponse">,
137
150
  private readonly handler: (args: Args) => AsyncIterable<Item>
138
151
  ) {
139
152
  events.onRequest(this.onRequest.bind(this));
153
+ events.onRequestCancel(this.onRequestCancel.bind(this));
154
+ }
155
+
156
+ get iteratingCount(): number {
157
+ return this.iterators.size;
140
158
  }
141
159
 
142
160
  private async onRequest(data: RequestEvent<Args>): Promise<void> {
143
161
  try {
144
- for await (const item of this.handler(data.callArgs)) {
162
+ const iterator = this.handler(data.callArgs)[Symbol.asyncIterator]();
163
+ this.iterators.set(data.id, iterator);
164
+
165
+ while (true) {
166
+ const result = await iterator.next();
167
+ if (result.done) {
168
+ break;
169
+ }
170
+
145
171
  this.events.emitResponse({
146
172
  type: IteratorEventType.next,
147
173
  id: data.id,
148
- item,
174
+ item: result.value,
149
175
  });
150
176
  }
151
177
 
@@ -159,6 +185,19 @@ export class AsyncIterableBridgeHandler<Args, Item> {
159
185
  id: data.id,
160
186
  error: toThreadBoundaryError(e as Error),
161
187
  });
188
+ } finally {
189
+ this.iterators.delete(data.id);
190
+ }
191
+ }
192
+
193
+ private onRequestCancel(data: RequestCancelEvent): void {
194
+ const iterator = this.iterators.get(data.id);
195
+ if (!iterator?.return) {
196
+ return;
162
197
  }
198
+
199
+ // An async generator queues return() behind the next() that is in flight, so at most one more
200
+ // item is produced before its finally blocks run. Any error surfaces through that next() call.
201
+ void iterator.return().catch(() => {});
163
202
  }
164
203
  }
@@ -6,7 +6,8 @@ export function getLodestarClientVersion(info?: {version?: string; commit?: stri
6
6
  code: ClientCode.LS,
7
7
  name: "Lodestar",
8
8
  version: info?.version ?? "",
9
- commit: info?.commit?.slice(0, 8) ?? "",
9
+ // engine_getClientVersionV1 defines commit as 4 bytes, so builds without git data must not send an empty one
10
+ commit: info?.commit ? info.commit.slice(0, 8) : "00000000",
10
11
  };
11
12
  }
12
13