@aztec/epoch-cache 1.0.0-nightly.20250616 → 1.0.0-nightly.20250618

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.
@@ -9,7 +9,7 @@ type EpochAndSlot = {
9
9
  ts: bigint;
10
10
  };
11
11
  export type EpochCommitteeInfo = {
12
- committee: EthAddress[];
12
+ committee: EthAddress[] | undefined;
13
13
  seed: bigint;
14
14
  epoch: bigint;
15
15
  };
@@ -42,7 +42,7 @@ export declare class EpochCache implements EpochCacheInterface {
42
42
  private readonly config;
43
43
  private cache;
44
44
  private readonly log;
45
- constructor(rollup: RollupContract, initialEpoch?: bigint, initialValidators?: EthAddress[], initialSampleSeed?: bigint, l1constants?: L1RollupConstants, dateProvider?: DateProvider, config?: {
45
+ constructor(rollup: RollupContract, initialEpoch?: bigint, initialValidators?: EthAddress[] | undefined, initialSampleSeed?: bigint, l1constants?: L1RollupConstants, dateProvider?: DateProvider, config?: {
46
46
  cacheSize: number;
47
47
  });
48
48
  static create(rollupAddress: EthAddress, config?: EpochCacheConfig, deps?: {
@@ -84,7 +84,18 @@ export declare class EpochCache implements EpochCacheInterface {
84
84
  currentProposer: EthAddress | undefined;
85
85
  nextProposer: EthAddress | undefined;
86
86
  }>;
87
+ /**
88
+ * Get the proposer attester address in the next slot
89
+ * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
90
+ * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
91
+ */
87
92
  getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined>;
93
+ /**
94
+ * Get the proposer attester address at a given epoch and slot
95
+ * @param when - The epoch and slot to get the proposer attester address at
96
+ * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
97
+ * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
98
+ */
88
99
  private getProposerAttesterAddressAt;
89
100
  /**
90
101
  * Check if a validator is in the current epoch's committee
@@ -1 +1 @@
1
- {"version":3,"file":"epoch_cache.d.ts","sourceRoot":"","sources":["../src/epoch_cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"epoch_cache.d.ts","sourceRoot":"","sources":["../src/epoch_cache.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { RollupContract, createEthereumChain } from '@aztec/ethereum';
1
+ import { NoCommitteeError, RollupContract, createEthereumChain } from '@aztec/ethereum';
2
2
  import { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { DateProvider } from '@aztec/foundation/timer';
@@ -20,7 +20,7 @@ import { getEpochCacheConfigEnvVars } from './config.js';
20
20
  config;
21
21
  cache;
22
22
  log;
23
- constructor(rollup, initialEpoch = 0n, initialValidators = [], initialSampleSeed = 0n, l1constants = EmptyL1RollupConstants, dateProvider = new DateProvider(), config = {
23
+ constructor(rollup, initialEpoch = 0n, initialValidators = undefined, initialSampleSeed = 0n, l1constants = EmptyL1RollupConstants, dateProvider = new DateProvider(), config = {
24
24
  cacheSize: 12
25
25
  }){
26
26
  this.rollup = rollup;
@@ -34,7 +34,7 @@ import { getEpochCacheConfigEnvVars } from './config.js';
34
34
  committee: initialValidators,
35
35
  seed: initialSampleSeed
36
36
  });
37
- this.log.debug(`Initialized EpochCache with ${initialValidators.length} validators`, {
37
+ this.log.debug(`Initialized EpochCache with ${initialValidators?.length ?? 'no'} validators`, {
38
38
  l1constants,
39
39
  initialValidators,
40
40
  initialSampleSeed,
@@ -65,7 +65,7 @@ import { getEpochCacheConfigEnvVars } from './config.js';
65
65
  epochDuration: config.aztecEpochDuration,
66
66
  ethereumSlotDuration: config.ethereumSlotDuration
67
67
  };
68
- return new EpochCache(rollup, epochNumber, initialValidators.map((v)=>EthAddress.fromString(v)), sampleSeed, l1RollupConstants, deps.dateProvider);
68
+ return new EpochCache(rollup, epochNumber, initialValidators?.map((v)=>EthAddress.fromString(v)), sampleSeed, l1RollupConstants, deps.dateProvider);
69
69
  }
70
70
  getL1Constants() {
71
71
  return this.l1constants;
@@ -122,8 +122,8 @@ import { getEpochCacheConfigEnvVars } from './config.js';
122
122
  epoch,
123
123
  ts
124
124
  });
125
- // If the committee size is 0, then do not cache
126
- if (epochData.committee.length == 0) {
125
+ // If the committee size is 0 or undefined, then do not cache
126
+ if (!epochData.committee || epochData.committee.length === 0) {
127
127
  return epochData;
128
128
  }
129
129
  this.cache.set(epoch, epochData);
@@ -146,7 +146,7 @@ import { getEpochCacheConfigEnvVars } from './config.js';
146
146
  this.rollup.getCommitteeAt(ts),
147
147
  this.rollup.getSampleSeedAt(ts)
148
148
  ]);
149
- const committee = committeeHex.map((v)=>EthAddress.fromString(v));
149
+ const committee = committeeHex?.map((v)=>EthAddress.fromString(v));
150
150
  return {
151
151
  committee,
152
152
  seed,
@@ -197,13 +197,27 @@ import { getEpochCacheConfigEnvVars } from './config.js';
197
197
  nextSlot: next.slot
198
198
  };
199
199
  }
200
- getProposerAttesterAddressInNextSlot() {
200
+ /**
201
+ * Get the proposer attester address in the next slot
202
+ * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
203
+ * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
204
+ */ getProposerAttesterAddressInNextSlot() {
201
205
  const epochAndSlot = this.getEpochAndSlotInNextL1Slot();
202
206
  return this.getProposerAttesterAddressAt(epochAndSlot);
203
207
  }
204
- async getProposerAttesterAddressAt(when) {
208
+ /**
209
+ * Get the proposer attester address at a given epoch and slot
210
+ * @param when - The epoch and slot to get the proposer attester address at
211
+ * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
212
+ * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
213
+ */ async getProposerAttesterAddressAt(when) {
205
214
  const { epoch, slot } = when;
206
- const { seed, committee } = await this.getCommittee(slot);
215
+ const { committee, seed } = await this.getCommittee(slot);
216
+ if (!committee) {
217
+ throw new NoCommitteeError();
218
+ } else if (committee.length === 0) {
219
+ return undefined;
220
+ }
207
221
  const proposerIndex = this.computeProposerIndex(slot, epoch, seed, BigInt(committee.length));
208
222
  return committee[Number(proposerIndex)];
209
223
  }
@@ -211,10 +225,16 @@ import { getEpochCacheConfigEnvVars } from './config.js';
211
225
  * Check if a validator is in the current epoch's committee
212
226
  */ async isInCommittee(validator) {
213
227
  const { committee } = await this.getCommittee();
228
+ if (!committee) {
229
+ return false;
230
+ }
214
231
  return committee.some((v)=>v.equals(validator));
215
232
  }
216
233
  async filterInCommittee(validators) {
217
234
  const { committee } = await this.getCommittee();
235
+ if (!committee) {
236
+ return [];
237
+ }
218
238
  const committeeSet = new Set(committee.map((v)=>v.toString()));
219
239
  return validators.filter((v)=>committeeSet.has(v.toString()));
220
240
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/epoch-cache",
3
- "version": "1.0.0-nightly.20250616",
3
+ "version": "1.0.0-nightly.20250618",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -26,10 +26,10 @@
26
26
  "../package.common.json"
27
27
  ],
28
28
  "dependencies": {
29
- "@aztec/ethereum": "1.0.0-nightly.20250616",
30
- "@aztec/foundation": "1.0.0-nightly.20250616",
31
- "@aztec/l1-artifacts": "1.0.0-nightly.20250616",
32
- "@aztec/stdlib": "1.0.0-nightly.20250616",
29
+ "@aztec/ethereum": "1.0.0-nightly.20250618",
30
+ "@aztec/foundation": "1.0.0-nightly.20250618",
31
+ "@aztec/l1-artifacts": "1.0.0-nightly.20250618",
32
+ "@aztec/stdlib": "1.0.0-nightly.20250618",
33
33
  "@viem/anvil": "^0.0.10",
34
34
  "dotenv": "^16.0.3",
35
35
  "get-port": "^7.1.0",
@@ -39,10 +39,10 @@
39
39
  "zod": "^3.23.8"
40
40
  },
41
41
  "devDependencies": {
42
- "@jest/globals": "^29.5.0",
42
+ "@jest/globals": "^30.0.0",
43
43
  "@types/jest": "^29.5.0",
44
44
  "@types/node": "^22.15.17",
45
- "jest": "^29.5.0",
45
+ "jest": "^30.0.0",
46
46
  "ts-node": "^10.9.1",
47
47
  "typescript": "^5.3.3"
48
48
  },
@@ -84,7 +84,10 @@
84
84
  "setupFiles": [
85
85
  "../../foundation/src/jest/setup.mjs"
86
86
  ],
87
- "testEnvironment": "../../foundation/src/jest/env.mjs"
87
+ "testEnvironment": "../../foundation/src/jest/env.mjs",
88
+ "setupFilesAfterEnv": [
89
+ "../../foundation/src/jest/setupAfterEnv.mjs"
90
+ ]
88
91
  },
89
92
  "engines": {
90
93
  "node": ">=20.10"
@@ -1,4 +1,4 @@
1
- import { RollupContract, createEthereumChain } from '@aztec/ethereum';
1
+ import { NoCommitteeError, RollupContract, createEthereumChain } from '@aztec/ethereum';
2
2
  import { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { DateProvider } from '@aztec/foundation/timer';
@@ -24,7 +24,7 @@ type EpochAndSlot = {
24
24
  };
25
25
 
26
26
  export type EpochCommitteeInfo = {
27
- committee: EthAddress[];
27
+ committee: EthAddress[] | undefined;
28
28
  seed: bigint;
29
29
  epoch: bigint;
30
30
  };
@@ -59,14 +59,14 @@ export class EpochCache implements EpochCacheInterface {
59
59
  constructor(
60
60
  private rollup: RollupContract,
61
61
  initialEpoch: bigint = 0n,
62
- initialValidators: EthAddress[] = [],
62
+ initialValidators: EthAddress[] | undefined = undefined,
63
63
  initialSampleSeed: bigint = 0n,
64
64
  private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
65
65
  private readonly dateProvider: DateProvider = new DateProvider(),
66
66
  private readonly config = { cacheSize: 12 },
67
67
  ) {
68
68
  this.cache.set(initialEpoch, { epoch: initialEpoch, committee: initialValidators, seed: initialSampleSeed });
69
- this.log.debug(`Initialized EpochCache with ${initialValidators.length} validators`, {
69
+ this.log.debug(`Initialized EpochCache with ${initialValidators?.length ?? 'no'} validators`, {
70
70
  l1constants,
71
71
  initialValidators,
72
72
  initialSampleSeed,
@@ -109,7 +109,7 @@ export class EpochCache implements EpochCacheInterface {
109
109
  return new EpochCache(
110
110
  rollup,
111
111
  epochNumber,
112
- initialValidators.map(v => EthAddress.fromString(v)),
112
+ initialValidators?.map(v => EthAddress.fromString(v)),
113
113
  sampleSeed,
114
114
  l1RollupConstants,
115
115
  deps.dateProvider,
@@ -168,8 +168,8 @@ export class EpochCache implements EpochCacheInterface {
168
168
  }
169
169
 
170
170
  const epochData = await this.computeCommittee({ epoch, ts });
171
- // If the committee size is 0, then do not cache
172
- if (epochData.committee.length == 0) {
171
+ // If the committee size is 0 or undefined, then do not cache
172
+ if (!epochData.committee || epochData.committee.length === 0) {
173
173
  return epochData;
174
174
  }
175
175
  this.cache.set(epoch, epochData);
@@ -195,7 +195,7 @@ export class EpochCache implements EpochCacheInterface {
195
195
  private async computeCommittee(when: { epoch: bigint; ts: bigint }): Promise<EpochCommitteeInfo> {
196
196
  const { ts, epoch } = when;
197
197
  const [committeeHex, seed] = await Promise.all([this.rollup.getCommitteeAt(ts), this.rollup.getSampleSeedAt(ts)]);
198
- const committee = committeeHex.map((v: `0x${string}`) => EthAddress.fromString(v));
198
+ const committee = committeeHex?.map((v: `0x${string}`) => EthAddress.fromString(v));
199
199
  return { committee, seed, epoch };
200
200
  }
201
201
 
@@ -244,15 +244,31 @@ export class EpochCache implements EpochCacheInterface {
244
244
  };
245
245
  }
246
246
 
247
+ /**
248
+ * Get the proposer attester address in the next slot
249
+ * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
250
+ * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
251
+ */
247
252
  getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined> {
248
253
  const epochAndSlot = this.getEpochAndSlotInNextL1Slot();
249
254
 
250
255
  return this.getProposerAttesterAddressAt(epochAndSlot);
251
256
  }
252
257
 
258
+ /**
259
+ * Get the proposer attester address at a given epoch and slot
260
+ * @param when - The epoch and slot to get the proposer attester address at
261
+ * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
262
+ * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
263
+ */
253
264
  private async getProposerAttesterAddressAt(when: EpochAndSlot) {
254
265
  const { epoch, slot } = when;
255
- const { seed, committee } = await this.getCommittee(slot);
266
+ const { committee, seed } = await this.getCommittee(slot);
267
+ if (!committee) {
268
+ throw new NoCommitteeError();
269
+ } else if (committee.length === 0) {
270
+ return undefined;
271
+ }
256
272
 
257
273
  const proposerIndex = this.computeProposerIndex(slot, epoch, seed, BigInt(committee.length));
258
274
  return committee[Number(proposerIndex)];
@@ -263,11 +279,17 @@ export class EpochCache implements EpochCacheInterface {
263
279
  */
264
280
  async isInCommittee(validator: EthAddress): Promise<boolean> {
265
281
  const { committee } = await this.getCommittee();
282
+ if (!committee) {
283
+ return false;
284
+ }
266
285
  return committee.some(v => v.equals(validator));
267
286
  }
268
287
 
269
288
  async filterInCommittee(validators: EthAddress[]): Promise<EthAddress[]> {
270
289
  const { committee } = await this.getCommittee();
290
+ if (!committee) {
291
+ return [];
292
+ }
271
293
  const committeeSet = new Set(committee.map(v => v.toString()));
272
294
  return validators.filter(v => committeeSet.has(v.toString()));
273
295
  }