@aztec/epoch-cache 3.0.3-rc.3 → 4.0.0-devnet.1-patch.0

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.
@@ -13,25 +13,29 @@ export type EpochCommitteeInfo = {
13
13
  committee: EthAddress[] | undefined;
14
14
  seed: bigint;
15
15
  epoch: EpochNumber;
16
+ /** True if the epoch is within an open escape hatch window. */
17
+ isEscapeHatchOpen: boolean;
16
18
  };
17
19
  export type SlotTag = 'now' | 'next' | SlotNumber;
18
20
  export interface EpochCacheInterface {
19
21
  getCommittee(slot: SlotTag | undefined): Promise<EpochCommitteeInfo>;
20
- getEpochAndSlotNow(): EpochAndSlot;
22
+ getEpochAndSlotNow(): EpochAndSlot & {
23
+ nowMs: bigint;
24
+ };
21
25
  getEpochAndSlotInNextL1Slot(): EpochAndSlot & {
22
26
  now: bigint;
23
27
  };
24
28
  getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
25
29
  computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
26
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
27
- currentProposer: EthAddress | undefined;
28
- nextProposer: EthAddress | undefined;
30
+ getCurrentAndNextSlot(): {
29
31
  currentSlot: SlotNumber;
30
32
  nextSlot: SlotNumber;
31
- }>;
33
+ };
34
+ getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined>;
32
35
  getRegisteredValidators(): Promise<EthAddress[]>;
33
36
  isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
34
37
  filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
38
+ getL1Constants(): L1RollupConstants;
35
39
  }
36
40
  /**
37
41
  * Epoch cache
@@ -66,7 +70,7 @@ export declare class EpochCache implements EpochCacheInterface {
66
70
  }): Promise<EpochCache>;
67
71
  getL1Constants(): L1RollupConstants;
68
72
  getEpochAndSlotNow(): EpochAndSlot & {
69
- now: bigint;
73
+ nowMs: bigint;
70
74
  };
71
75
  nowInSeconds(): bigint;
72
76
  private getEpochAndSlotAtSlot;
@@ -75,6 +79,20 @@ export declare class EpochCache implements EpochCacheInterface {
75
79
  };
76
80
  private getEpochAndSlotAtTimestamp;
77
81
  getCommitteeForEpoch(epoch: EpochNumber): Promise<EpochCommitteeInfo>;
82
+ /**
83
+ * Returns whether the escape hatch is open for the given epoch.
84
+ *
85
+ * Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
86
+ * the epoch committee info (which includes the escape hatch flag) and return it.
87
+ */
88
+ isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean>;
89
+ /**
90
+ * Returns whether the escape hatch is open for the epoch containing the given slot.
91
+ *
92
+ * This is a lightweight helper intended for callers that already have a slot number and only
93
+ * need the escape hatch flag (without pulling full committee info).
94
+ */
95
+ isEscapeHatchOpenAtSlot(slot?: SlotTag): Promise<boolean>;
78
96
  /**
79
97
  * Get the current validator set
80
98
  * @param nextSlot - If true, get the validator set for the next slot.
@@ -88,18 +106,11 @@ export declare class EpochCache implements EpochCacheInterface {
88
106
  */
89
107
  getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
90
108
  computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
91
- /**
92
- * Returns the current and next proposer's attester address
93
- *
94
- * We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
95
- * which can be the next slot. If this is the case, then it will send proposals early.
96
- */
97
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
109
+ /** Returns the current and next L2 slot numbers. */
110
+ getCurrentAndNextSlot(): {
98
111
  currentSlot: SlotNumber;
99
112
  nextSlot: SlotNumber;
100
- currentProposer: EthAddress | undefined;
101
- nextProposer: EthAddress | undefined;
102
- }>;
113
+ };
103
114
  /**
104
115
  * Get the proposer attester address in the given L2 slot
105
116
  * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
@@ -120,4 +131,4 @@ export declare class EpochCache implements EpochCacheInterface {
120
131
  filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
121
132
  getRegisteredValidators(): Promise<EthAddress[]>;
122
133
  }
123
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfY2FjaGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQW9CLGNBQWMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDMUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTNELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEVBQ0wsS0FBSyxpQkFBaUIsRUFPdkIsTUFBTSw2QkFBNkIsQ0FBQztBQUlyQyxPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFFaEYsTUFBTSxNQUFNLFlBQVksR0FBRztJQUN6QixLQUFLLEVBQUUsV0FBVyxDQUFDO0lBQ25CLElBQUksRUFBRSxVQUFVLENBQUM7SUFDakIsRUFBRSxFQUFFLE1BQU0sQ0FBQztDQUNaLENBQUM7QUFFRixNQUFNLE1BQU0sa0JBQWtCLEdBQUc7SUFDL0IsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLFNBQVMsQ0FBQztJQUNwQyxJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsS0FBSyxFQUFFLFdBQVcsQ0FBQztDQUNwQixDQUFDO0FBRUYsTUFBTSxNQUFNLE9BQU8sR0FBRyxLQUFLLEdBQUcsTUFBTSxHQUFHLFVBQVUsQ0FBQztBQUVsRCxNQUFNLFdBQVcsbUJBQW1CO0lBQ2xDLFlBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxHQUFHLFNBQVMsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUNyRSxrQkFBa0IsSUFBSSxZQUFZLENBQUM7SUFDbkMsMkJBQTJCLElBQUksWUFBWSxHQUFHO1FBQUUsR0FBRyxFQUFFLE1BQU0sQ0FBQTtLQUFFLENBQUM7SUFDOUQsd0JBQXdCLENBQUMs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
134
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfY2FjaGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQW9CLGNBQWMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDMUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTNELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEVBQ0wsS0FBSyxpQkFBaUIsRUFPdkIsTUFBTSw2QkFBNkIsQ0FBQztBQUlyQyxPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFFaEYsTUFBTSxNQUFNLFlBQVksR0FBRztJQUN6QixLQUFLLEVBQUUsV0FBVyxDQUFDO0lBQ25CLElBQUksRUFBRSxVQUFVLENBQUM7SUFDakIsRUFBRSxFQUFFLE1BQU0sQ0FBQztDQUNaLENBQUM7QUFFRixNQUFNLE1BQU0sa0JBQWtCLEdBQUc7SUFDL0IsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLFNBQVMsQ0FBQztJQUNwQyxJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsS0FBSyxFQUFFLFdBQVcsQ0FBQztJQUNuQiwrREFBK0Q7SUFDL0QsaUJBQWlCLEVBQUUsT0FBTyxDQUFDO0NBQzVCLENBQUM7QUFFRixNQUFNLE1BQU0sT0FBTyxHQUFHLEtBQUssR0FBRyxNQUFNLEdBQUcsVUFBVSxDQUFDO0FBRWxELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsWUFBWSxDQUFDLElBQUksRUFBRSxPQUFPLEdBQUcsU0FBUyxHQUFHLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBQ3JFLGtCQUFrQixJQUFJLFlBQVksR0FBRztRQUFFLEtBQUssRUFBRSxNQUFNLENBQUE7S0FBRSxDQUFDO0lBQ3ZE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
@@ -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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"}
@@ -50,19 +50,22 @@ import { getEpochCacheConfigEnvVars } from './config.js';
50
50
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
51
51
  const publicClient = createPublicClient({
52
52
  chain: chain.chainInfo,
53
- transport: fallback(config.l1RpcUrls.map((url)=>http(url))),
53
+ transport: fallback(config.l1RpcUrls.map((url)=>http(url, {
54
+ batch: false
55
+ }))),
54
56
  pollingInterval: config.viemPollingIntervalMS
55
57
  });
56
58
  rollup = new RollupContract(publicClient, rollupOrAddress.toString());
57
59
  }
58
- const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration, lagInEpochsForValidatorSet, lagInEpochsForRandao] = await Promise.all([
60
+ const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration, lagInEpochsForValidatorSet, lagInEpochsForRandao, targetCommitteeSize] = await Promise.all([
59
61
  rollup.getL1StartBlock(),
60
62
  rollup.getL1GenesisTime(),
61
63
  rollup.getProofSubmissionEpochs(),
62
64
  rollup.getSlotDuration(),
63
65
  rollup.getEpochDuration(),
64
66
  rollup.getLagInEpochsForValidatorSet(),
65
- rollup.getLagInEpochsForRandao()
67
+ rollup.getLagInEpochsForRandao(),
68
+ rollup.getTargetCommitteeSize()
66
69
  ]);
67
70
  const l1RollupConstants = {
68
71
  l1StartBlock,
@@ -72,7 +75,8 @@ import { getEpochCacheConfigEnvVars } from './config.js';
72
75
  epochDuration: Number(epochDuration),
73
76
  ethereumSlotDuration: config.ethereumSlotDuration,
74
77
  lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
75
- lagInEpochsForRandao: Number(lagInEpochsForRandao)
78
+ lagInEpochsForRandao: Number(lagInEpochsForRandao),
79
+ targetCommitteeSize: Number(targetCommitteeSize)
76
80
  };
77
81
  return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
78
82
  }
@@ -80,10 +84,11 @@ import { getEpochCacheConfigEnvVars } from './config.js';
80
84
  return this.l1constants;
81
85
  }
82
86
  getEpochAndSlotNow() {
83
- const now = this.nowInSeconds();
87
+ const nowMs = BigInt(this.dateProvider.now());
88
+ const nowSeconds = nowMs / 1000n;
84
89
  return {
85
- ...this.getEpochAndSlotAtTimestamp(now),
86
- now
90
+ ...this.getEpochAndSlotAtTimestamp(nowSeconds),
91
+ nowMs
87
92
  };
88
93
  }
89
94
  nowInSeconds() {
@@ -119,6 +124,28 @@ import { getEpochCacheConfigEnvVars } from './config.js';
119
124
  return this.getCommittee(startSlot);
120
125
  }
121
126
  /**
127
+ * Returns whether the escape hatch is open for the given epoch.
128
+ *
129
+ * Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
130
+ * the epoch committee info (which includes the escape hatch flag) and return it.
131
+ */ async isEscapeHatchOpen(epoch) {
132
+ const cached = this.cache.get(epoch);
133
+ if (cached) {
134
+ return cached.isEscapeHatchOpen;
135
+ }
136
+ const info = await this.getCommitteeForEpoch(epoch);
137
+ return info.isEscapeHatchOpen;
138
+ }
139
+ /**
140
+ * Returns whether the escape hatch is open for the epoch containing the given slot.
141
+ *
142
+ * This is a lightweight helper intended for callers that already have a slot number and only
143
+ * need the escape hatch flag (without pulling full committee info).
144
+ */ async isEscapeHatchOpenAtSlot(slot = 'now') {
145
+ const epoch = slot === 'now' ? this.getEpochAndSlotNow().epoch : slot === 'next' ? this.getEpochAndSlotInNextL1Slot().epoch : getEpochAtSlot(slot, this.l1constants);
146
+ return await this.isEscapeHatchOpen(epoch);
147
+ }
148
+ /**
122
149
  * Get the current validator set
123
150
  * @param nextSlot - If true, get the validator set for the next slot.
124
151
  * @returns The current validator set.
@@ -151,23 +178,24 @@ import { getEpochCacheConfigEnvVars } from './config.js';
151
178
  }
152
179
  async computeCommittee(when) {
153
180
  const { ts, epoch } = when;
154
- const [committeeHex, seed, l1Timestamp] = await Promise.all([
181
+ const [committee, seedBuffer, l1Timestamp, isEscapeHatchOpen] = await Promise.all([
155
182
  this.rollup.getCommitteeAt(ts),
156
183
  this.rollup.getSampleSeedAt(ts),
157
184
  this.rollup.client.getBlock({
158
185
  includeTransactions: false
159
- }).then((b)=>b.timestamp)
186
+ }).then((b)=>b.timestamp),
187
+ this.rollup.isEscapeHatchOpen(epoch)
160
188
  ]);
161
189
  const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
162
190
  const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
163
191
  if (ts - sub > l1Timestamp) {
164
192
  throw new Error(`Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`);
165
193
  }
166
- const committee = committeeHex?.map((v)=>EthAddress.fromString(v));
167
194
  return {
168
195
  committee,
169
- seed,
170
- epoch
196
+ seed: seedBuffer.toBigInt(),
197
+ epoch,
198
+ isEscapeHatchOpen
171
199
  };
172
200
  }
173
201
  /**
@@ -199,17 +227,10 @@ import { getEpochCacheConfigEnvVars } from './config.js';
199
227
  }
200
228
  return BigInt(keccak256(this.getProposerIndexEncoding(epoch, slot, seed))) % size;
201
229
  }
202
- /**
203
- * Returns the current and next proposer's attester address
204
- *
205
- * We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
206
- * which can be the next slot. If this is the case, then it will send proposals early.
207
- */ async getProposerAttesterAddressInCurrentOrNextSlot() {
230
+ /** Returns the current and next L2 slot numbers. */ getCurrentAndNextSlot() {
208
231
  const current = this.getEpochAndSlotNow();
209
232
  const next = this.getEpochAndSlotInNextL1Slot();
210
233
  return {
211
- currentProposer: await this.getProposerAttesterAddressAt(current),
212
- nextProposer: await this.getProposerAttesterAddressAt(next),
213
234
  currentSlot: current.slot,
214
235
  nextSlot: next.slot
215
236
  };
@@ -273,9 +294,9 @@ import { getEpochCacheConfigEnvVars } from './config.js';
273
294
  const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
274
295
  if (validatorRefreshTime < this.dateProvider.now()) {
275
296
  const currentSet = await this.rollup.getAttesters();
276
- this.allValidators = new Set(currentSet);
297
+ this.allValidators = new Set(currentSet.map((v)=>v.toString()));
277
298
  this.lastValidatorRefresh = this.dateProvider.now();
278
299
  }
279
- return Array.from(this.allValidators.keys().map((v)=>EthAddress.fromString(v)));
300
+ return Array.from(this.allValidators.keys()).map((v)=>EthAddress.fromString(v));
280
301
  }
281
302
  }
@@ -0,0 +1,2 @@
1
+ export * from './test_epoch_cache.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsdUJBQXVCLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './test_epoch_cache.js';
@@ -0,0 +1,76 @@
1
+ import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
4
+ import type { EpochAndSlot, EpochCacheInterface, EpochCommitteeInfo, SlotTag } from '../epoch_cache.js';
5
+ /**
6
+ * A test implementation of EpochCacheInterface that allows manual configuration
7
+ * of committee, proposer, slot, and escape hatch state for use in tests.
8
+ *
9
+ * Unlike the real EpochCache, this class doesn't require any RPC connections
10
+ * or mock setup. Simply use the setter methods to configure the test state.
11
+ */
12
+ export declare class TestEpochCache implements EpochCacheInterface {
13
+ private committee;
14
+ private proposerAddress;
15
+ private currentSlot;
16
+ private escapeHatchOpen;
17
+ private seed;
18
+ private registeredValidators;
19
+ private l1Constants;
20
+ constructor(l1Constants?: Partial<L1RollupConstants>);
21
+ /**
22
+ * Sets the committee members. Used in validation and attestation flows.
23
+ * @param committee - Array of committee member addresses.
24
+ */
25
+ setCommittee(committee: EthAddress[]): this;
26
+ /**
27
+ * Sets the proposer address returned by getProposerAttesterAddressInSlot.
28
+ * @param proposer - The address of the current proposer.
29
+ */
30
+ setProposer(proposer: EthAddress | undefined): this;
31
+ /**
32
+ * Sets the current slot number.
33
+ * @param slot - The slot number to set.
34
+ */
35
+ setCurrentSlot(slot: SlotNumber): this;
36
+ /**
37
+ * Sets whether the escape hatch is open.
38
+ * @param open - True if escape hatch should be open.
39
+ */
40
+ setEscapeHatchOpen(open: boolean): this;
41
+ /**
42
+ * Sets the randomness seed used for proposer selection.
43
+ * @param seed - The seed value.
44
+ */
45
+ setSeed(seed: bigint): this;
46
+ /**
47
+ * Sets the list of registered validators (all validators, not just committee).
48
+ * @param validators - Array of validator addresses.
49
+ */
50
+ setRegisteredValidators(validators: EthAddress[]): this;
51
+ /**
52
+ * Sets the L1 constants used for epoch/slot calculations.
53
+ * @param constants - Partial constants to override defaults.
54
+ */
55
+ setL1Constants(constants: Partial<L1RollupConstants>): this;
56
+ getL1Constants(): L1RollupConstants;
57
+ getCommittee(_slot?: SlotTag): Promise<EpochCommitteeInfo>;
58
+ getEpochAndSlotNow(): EpochAndSlot & {
59
+ nowMs: bigint;
60
+ };
61
+ getEpochAndSlotInNextL1Slot(): EpochAndSlot & {
62
+ now: bigint;
63
+ };
64
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
65
+ computeProposerIndex(slot: SlotNumber, _epoch: EpochNumber, _seed: bigint, size: bigint): bigint;
66
+ getCurrentAndNextSlot(): {
67
+ currentSlot: SlotNumber;
68
+ nextSlot: SlotNumber;
69
+ };
70
+ getProposerAttesterAddressInSlot(_slot: SlotNumber): Promise<EthAddress | undefined>;
71
+ getRegisteredValidators(): Promise<EthAddress[]>;
72
+ isInCommittee(_slot: SlotTag, validator: EthAddress): Promise<boolean>;
73
+ filterInCommittee(_slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
74
+ isEscapeHatchOpenAtSlot(_slot?: SlotTag): Promise<boolean>;
75
+ }
76
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdF9lcG9jaF9jYWNoZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3QvdGVzdF9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUMzRCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBR3JFLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxrQkFBa0IsRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQWF4Rzs7Ozs7O0dBTUc7QUFDSCxxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBQ3hELE9BQU8sQ0FBQyxTQUFTLENBQW9CO0lBQ3JDLE9BQU8sQ0FBQyxlQUFlLENBQXlCO0lBQ2hELE9BQU8sQ0FBQyxXQUFXLENBQTZCO0lBQ2hELE9BQU8sQ0FBQyxlQUFlLENBQWtCO0lBQ3pDLE9BQU8sQ0FBQyxJQUFJLENBQWM7SUFDMUIsT0FBTyxDQUFDLG9CQUFvQixDQUFvQjtJQUNoRCxPQUFPLENBQUMsV0FBVyxDQUFvQjtJQUV2QyxZQUFZLFdBQVcsR0FBRSxPQUFPLENBQUMsaUJBQWlCLENBQU0sRUFFdkQ7SUFFRDs7O09BR0c7SUFDSCxZQUFZLENBQUMsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FHMUM7SUFFRDs7O09BR0c7SUFDSCxXQUFXLENBQUMsUUFBUSxFQUFFLFVBQVUsR0FBRyxTQUFTLEdBQUcsSUFBSSxDQUdsRDtJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxJQUFJLEVBQUUsVUFBVSxHQUFHLElBQUksQ0FHckM7SUFFRDs7O09BR0c7SUFDSCxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsT0FBTyxHQUFHLElBQUksQ0FHdEM7SUFFRDs7O09BR0c7SUFDSCxPQUFPLENBQUMsSUFBSSxFQUFFLE1BQU0sR0FBRyxJQUFJLENBRzFCO0lBRUQ7OztPQUdHO0lBQ0gsdUJBQXVCLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FHdEQ7SUFFRDs7O09BR0c7SUFDSCxjQUFjLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLElBQUksQ0FHMUQ7SUFFRCxjQUFjLElBQUksaUJBQWlCLENBRWxDO0lBRUQsWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FRekQ7SUFFRCxrQkFBa0IsSUFBSSxZQUFZLEdBQUc7UUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FJckQ7SUFFRCwyQkFBMkIsSUFBSSxZQUFZLEdBQUc7UUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FPNUQ7SUFFRCx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxLQUFLLE1BQU0sRUFBRSxDQUcxRjtJQUVELG9CQUFvQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsTUFBTSxDQUsvRjtJQUVELHFCQUFxQixJQUFJO1FBQUUsV0FBVyxFQUFFLFVBQVUsQ0FBQztRQUFDLFFBQVEsRUFBRSxVQUFVLENBQUE7S0FBRSxDQUt6RTtJQUVELGdDQUFnQyxDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUMsQ0FFbkY7SUFFRCx1QkFBdUIsSUFBSSxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FFL0M7SUFFRCxhQUFhLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FFckU7SUFFRCxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsR0FBRyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FHakY7SUFFRCx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUV6RDtDQUNGIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_epoch_cache.d.ts","sourceRoot":"","sources":["../../src/test/test_epoch_cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAaxG;;;;;;GAMG;AACH,qBAAa,cAAe,YAAW,mBAAmB;IACxD,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,oBAAoB,CAAoB;IAChD,OAAO,CAAC,WAAW,CAAoB;IAEvC,YAAY,WAAW,GAAE,OAAO,CAAC,iBAAiB,CAAM,EAEvD;IAED;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,IAAI,CAG1C;IAED;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,CAGlD;IAED;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAGrC;IAED;;;OAGG;IACH,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAGtC;IAED;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG1B;IAED;;;OAGG;IACH,uBAAuB,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAGtD;IAED;;;OAGG;IACH,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAG1D;IAED,cAAc,IAAI,iBAAiB,CAElC;IAED,YAAY,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAQzD;IAED,kBAAkB,IAAI,YAAY,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAIrD;IAED,2BAA2B,IAAI,YAAY,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAO5D;IAED,wBAAwB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,CAG1F;IAED,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/F;IAED,qBAAqB,IAAI;QAAE,WAAW,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,UAAU,CAAA;KAAE,CAKzE;IAED,gCAAgC,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAEnF;IAED,uBAAuB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAE/C;IAED,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAErE;IAED,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAGjF;IAED,uBAAuB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAEzD;CACF"}
@@ -0,0 +1,151 @@
1
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
+ import { getEpochAtSlot, getSlotAtTimestamp, getTimestampRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
3
+ /** Default L1 constants for testing. */ const DEFAULT_L1_CONSTANTS = {
4
+ l1StartBlock: 0n,
5
+ l1GenesisTime: 0n,
6
+ slotDuration: 24,
7
+ epochDuration: 16,
8
+ ethereumSlotDuration: 12,
9
+ proofSubmissionEpochs: 2,
10
+ targetCommitteeSize: 48
11
+ };
12
+ /**
13
+ * A test implementation of EpochCacheInterface that allows manual configuration
14
+ * of committee, proposer, slot, and escape hatch state for use in tests.
15
+ *
16
+ * Unlike the real EpochCache, this class doesn't require any RPC connections
17
+ * or mock setup. Simply use the setter methods to configure the test state.
18
+ */ export class TestEpochCache {
19
+ committee = [];
20
+ proposerAddress;
21
+ currentSlot = SlotNumber(0);
22
+ escapeHatchOpen = false;
23
+ seed = 0n;
24
+ registeredValidators = [];
25
+ l1Constants;
26
+ constructor(l1Constants = {}){
27
+ this.l1Constants = {
28
+ ...DEFAULT_L1_CONSTANTS,
29
+ ...l1Constants
30
+ };
31
+ }
32
+ /**
33
+ * Sets the committee members. Used in validation and attestation flows.
34
+ * @param committee - Array of committee member addresses.
35
+ */ setCommittee(committee) {
36
+ this.committee = committee;
37
+ return this;
38
+ }
39
+ /**
40
+ * Sets the proposer address returned by getProposerAttesterAddressInSlot.
41
+ * @param proposer - The address of the current proposer.
42
+ */ setProposer(proposer) {
43
+ this.proposerAddress = proposer;
44
+ return this;
45
+ }
46
+ /**
47
+ * Sets the current slot number.
48
+ * @param slot - The slot number to set.
49
+ */ setCurrentSlot(slot) {
50
+ this.currentSlot = slot;
51
+ return this;
52
+ }
53
+ /**
54
+ * Sets whether the escape hatch is open.
55
+ * @param open - True if escape hatch should be open.
56
+ */ setEscapeHatchOpen(open) {
57
+ this.escapeHatchOpen = open;
58
+ return this;
59
+ }
60
+ /**
61
+ * Sets the randomness seed used for proposer selection.
62
+ * @param seed - The seed value.
63
+ */ setSeed(seed) {
64
+ this.seed = seed;
65
+ return this;
66
+ }
67
+ /**
68
+ * Sets the list of registered validators (all validators, not just committee).
69
+ * @param validators - Array of validator addresses.
70
+ */ setRegisteredValidators(validators) {
71
+ this.registeredValidators = validators;
72
+ return this;
73
+ }
74
+ /**
75
+ * Sets the L1 constants used for epoch/slot calculations.
76
+ * @param constants - Partial constants to override defaults.
77
+ */ setL1Constants(constants) {
78
+ this.l1Constants = {
79
+ ...this.l1Constants,
80
+ ...constants
81
+ };
82
+ return this;
83
+ }
84
+ getL1Constants() {
85
+ return this.l1Constants;
86
+ }
87
+ getCommittee(_slot) {
88
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
89
+ return Promise.resolve({
90
+ committee: this.committee,
91
+ epoch,
92
+ seed: this.seed,
93
+ isEscapeHatchOpen: this.escapeHatchOpen
94
+ });
95
+ }
96
+ getEpochAndSlotNow() {
97
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
98
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
99
+ return {
100
+ epoch,
101
+ slot: this.currentSlot,
102
+ ts,
103
+ nowMs: ts * 1000n
104
+ };
105
+ }
106
+ getEpochAndSlotInNextL1Slot() {
107
+ const now = getTimestampRangeForEpoch(getEpochAtSlot(this.currentSlot, this.l1Constants), this.l1Constants)[0];
108
+ const nextSlotTs = now + BigInt(this.l1Constants.ethereumSlotDuration);
109
+ const nextSlot = getSlotAtTimestamp(nextSlotTs, this.l1Constants);
110
+ const epoch = getEpochAtSlot(nextSlot, this.l1Constants);
111
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
112
+ return {
113
+ epoch,
114
+ slot: nextSlot,
115
+ ts,
116
+ now
117
+ };
118
+ }
119
+ getProposerIndexEncoding(epoch, slot, seed) {
120
+ // Simple encoding for testing purposes
121
+ return `0x${epoch.toString(16).padStart(64, '0')}${slot.toString(16).padStart(64, '0')}${seed.toString(16).padStart(64, '0')}`;
122
+ }
123
+ computeProposerIndex(slot, _epoch, _seed, size) {
124
+ if (size === 0n) {
125
+ return 0n;
126
+ }
127
+ return BigInt(slot) % size;
128
+ }
129
+ getCurrentAndNextSlot() {
130
+ return {
131
+ currentSlot: this.currentSlot,
132
+ nextSlot: SlotNumber(this.currentSlot + 1)
133
+ };
134
+ }
135
+ getProposerAttesterAddressInSlot(_slot) {
136
+ return Promise.resolve(this.proposerAddress);
137
+ }
138
+ getRegisteredValidators() {
139
+ return Promise.resolve(this.registeredValidators);
140
+ }
141
+ isInCommittee(_slot, validator) {
142
+ return Promise.resolve(this.committee.some((v)=>v.equals(validator)));
143
+ }
144
+ filterInCommittee(_slot, validators) {
145
+ const committeeSet = new Set(this.committee.map((v)=>v.toString()));
146
+ return Promise.resolve(validators.filter((v)=>committeeSet.has(v.toString())));
147
+ }
148
+ isEscapeHatchOpenAtSlot(_slot) {
149
+ return Promise.resolve(this.escapeHatchOpen);
150
+ }
151
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/epoch-cache",
3
- "version": "3.0.3-rc.3",
3
+ "version": "4.0.0-devnet.1-patch.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -18,7 +18,7 @@
18
18
  "build": "yarn clean && ../scripts/tsc.sh",
19
19
  "build:dev": "../scripts/tsc.sh --watch",
20
20
  "clean": "rm -rf ./dest .tsbuildinfo",
21
- "start:dev": "concurrently -k \"tsgo -b -w\" \"nodemon --watch dest --exec yarn start\"",
21
+ "start:dev": "concurrently -k \"../scripts/tsc.sh --watch\" \"nodemon --watch dest --exec yarn start\"",
22
22
  "start": "node ./dest/index.js",
23
23
  "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
24
24
  },
@@ -26,10 +26,10 @@
26
26
  "../package.common.json"
27
27
  ],
28
28
  "dependencies": {
29
- "@aztec/ethereum": "3.0.3-rc.3",
30
- "@aztec/foundation": "3.0.3-rc.3",
31
- "@aztec/l1-artifacts": "3.0.3-rc.3",
32
- "@aztec/stdlib": "3.0.3-rc.3",
29
+ "@aztec/ethereum": "4.0.0-devnet.1-patch.0",
30
+ "@aztec/foundation": "4.0.0-devnet.1-patch.0",
31
+ "@aztec/l1-artifacts": "4.0.0-devnet.1-patch.0",
32
+ "@aztec/stdlib": "4.0.0-devnet.1-patch.0",
33
33
  "@viem/anvil": "^0.0.10",
34
34
  "dotenv": "^16.0.3",
35
35
  "get-port": "^7.1.0",
@@ -42,7 +42,7 @@
42
42
  "@jest/globals": "^30.0.0",
43
43
  "@types/jest": "^30.0.0",
44
44
  "@types/node": "^22.15.17",
45
- "@typescript/native-preview": "7.0.0-dev.20251126.1",
45
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
46
46
  "jest": "^30.0.0",
47
47
  "ts-node": "^10.9.1",
48
48
  "typescript": "^5.3.3"
@@ -28,25 +28,24 @@ export type EpochCommitteeInfo = {
28
28
  committee: EthAddress[] | undefined;
29
29
  seed: bigint;
30
30
  epoch: EpochNumber;
31
+ /** True if the epoch is within an open escape hatch window. */
32
+ isEscapeHatchOpen: boolean;
31
33
  };
32
34
 
33
35
  export type SlotTag = 'now' | 'next' | SlotNumber;
34
36
 
35
37
  export interface EpochCacheInterface {
36
38
  getCommittee(slot: SlotTag | undefined): Promise<EpochCommitteeInfo>;
37
- getEpochAndSlotNow(): EpochAndSlot;
39
+ getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint };
38
40
  getEpochAndSlotInNextL1Slot(): EpochAndSlot & { now: bigint };
39
41
  getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
40
42
  computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
41
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
42
- currentProposer: EthAddress | undefined;
43
- nextProposer: EthAddress | undefined;
44
- currentSlot: SlotNumber;
45
- nextSlot: SlotNumber;
46
- }>;
43
+ getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber };
44
+ getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined>;
47
45
  getRegisteredValidators(): Promise<EthAddress[]>;
48
46
  isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
49
47
  filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
48
+ getL1Constants(): L1RollupConstants;
50
49
  }
51
50
 
52
51
  /**
@@ -94,7 +93,7 @@ export class EpochCache implements EpochCacheInterface {
94
93
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
95
94
  const publicClient = createPublicClient({
96
95
  chain: chain.chainInfo,
97
- transport: fallback(config.l1RpcUrls.map(url => http(url))),
96
+ transport: fallback(config.l1RpcUrls.map(url => http(url, { batch: false }))),
98
97
  pollingInterval: config.viemPollingIntervalMS,
99
98
  });
100
99
  rollup = new RollupContract(publicClient, rollupOrAddress.toString());
@@ -108,6 +107,7 @@ export class EpochCache implements EpochCacheInterface {
108
107
  epochDuration,
109
108
  lagInEpochsForValidatorSet,
110
109
  lagInEpochsForRandao,
110
+ targetCommitteeSize,
111
111
  ] = await Promise.all([
112
112
  rollup.getL1StartBlock(),
113
113
  rollup.getL1GenesisTime(),
@@ -116,6 +116,7 @@ export class EpochCache implements EpochCacheInterface {
116
116
  rollup.getEpochDuration(),
117
117
  rollup.getLagInEpochsForValidatorSet(),
118
118
  rollup.getLagInEpochsForRandao(),
119
+ rollup.getTargetCommitteeSize(),
119
120
  ] as const);
120
121
 
121
122
  const l1RollupConstants = {
@@ -127,6 +128,7 @@ export class EpochCache implements EpochCacheInterface {
127
128
  ethereumSlotDuration: config.ethereumSlotDuration,
128
129
  lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
129
130
  lagInEpochsForRandao: Number(lagInEpochsForRandao),
131
+ targetCommitteeSize: Number(targetCommitteeSize),
130
132
  };
131
133
 
132
134
  return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
@@ -136,9 +138,10 @@ export class EpochCache implements EpochCacheInterface {
136
138
  return this.l1constants;
137
139
  }
138
140
 
139
- public getEpochAndSlotNow(): EpochAndSlot & { now: bigint } {
140
- const now = this.nowInSeconds();
141
- return { ...this.getEpochAndSlotAtTimestamp(now), now };
141
+ public getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
142
+ const nowMs = BigInt(this.dateProvider.now());
143
+ const nowSeconds = nowMs / 1000n;
144
+ return { ...this.getEpochAndSlotAtTimestamp(nowSeconds), nowMs };
142
145
  }
143
146
 
144
147
  public nowInSeconds(): bigint {
@@ -171,6 +174,38 @@ export class EpochCache implements EpochCacheInterface {
171
174
  return this.getCommittee(startSlot);
172
175
  }
173
176
 
177
+ /**
178
+ * Returns whether the escape hatch is open for the given epoch.
179
+ *
180
+ * Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
181
+ * the epoch committee info (which includes the escape hatch flag) and return it.
182
+ */
183
+ public async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
184
+ const cached = this.cache.get(epoch);
185
+ if (cached) {
186
+ return cached.isEscapeHatchOpen;
187
+ }
188
+ const info = await this.getCommitteeForEpoch(epoch);
189
+ return info.isEscapeHatchOpen;
190
+ }
191
+
192
+ /**
193
+ * Returns whether the escape hatch is open for the epoch containing the given slot.
194
+ *
195
+ * This is a lightweight helper intended for callers that already have a slot number and only
196
+ * need the escape hatch flag (without pulling full committee info).
197
+ */
198
+ public async isEscapeHatchOpenAtSlot(slot: SlotTag = 'now'): Promise<boolean> {
199
+ const epoch =
200
+ slot === 'now'
201
+ ? this.getEpochAndSlotNow().epoch
202
+ : slot === 'next'
203
+ ? this.getEpochAndSlotInNextL1Slot().epoch
204
+ : getEpochAtSlot(slot, this.l1constants);
205
+
206
+ return await this.isEscapeHatchOpen(epoch);
207
+ }
208
+
174
209
  /**
175
210
  * Get the current validator set
176
211
  * @param nextSlot - If true, get the validator set for the next slot.
@@ -210,10 +245,11 @@ export class EpochCache implements EpochCacheInterface {
210
245
 
211
246
  private async computeCommittee(when: { epoch: EpochNumber; ts: bigint }): Promise<EpochCommitteeInfo> {
212
247
  const { ts, epoch } = when;
213
- const [committeeHex, seed, l1Timestamp] = await Promise.all([
248
+ const [committee, seedBuffer, l1Timestamp, isEscapeHatchOpen] = await Promise.all([
214
249
  this.rollup.getCommitteeAt(ts),
215
250
  this.rollup.getSampleSeedAt(ts),
216
251
  this.rollup.client.getBlock({ includeTransactions: false }).then(b => b.timestamp),
252
+ this.rollup.isEscapeHatchOpen(epoch),
217
253
  ]);
218
254
  const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
219
255
  const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
@@ -222,8 +258,7 @@ export class EpochCache implements EpochCacheInterface {
222
258
  `Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`,
223
259
  );
224
260
  }
225
- const committee = committeeHex?.map((v: `0x${string}`) => EthAddress.fromString(v));
226
- return { committee, seed, epoch };
261
+ return { committee, seed: seedBuffer.toBigInt(), epoch, isEscapeHatchOpen };
227
262
  }
228
263
 
229
264
  /**
@@ -248,24 +283,12 @@ export class EpochCache implements EpochCacheInterface {
248
283
  return BigInt(keccak256(this.getProposerIndexEncoding(epoch, slot, seed))) % size;
249
284
  }
250
285
 
251
- /**
252
- * Returns the current and next proposer's attester address
253
- *
254
- * We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
255
- * which can be the next slot. If this is the case, then it will send proposals early.
256
- */
257
- public async getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
258
- currentSlot: SlotNumber;
259
- nextSlot: SlotNumber;
260
- currentProposer: EthAddress | undefined;
261
- nextProposer: EthAddress | undefined;
262
- }> {
286
+ /** Returns the current and next L2 slot numbers. */
287
+ public getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
263
288
  const current = this.getEpochAndSlotNow();
264
289
  const next = this.getEpochAndSlotInNextL1Slot();
265
290
 
266
291
  return {
267
- currentProposer: await this.getProposerAttesterAddressAt(current),
268
- nextProposer: await this.getProposerAttesterAddressAt(next),
269
292
  currentSlot: current.slot,
270
293
  nextSlot: next.slot,
271
294
  };
@@ -351,9 +374,9 @@ export class EpochCache implements EpochCacheInterface {
351
374
  const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
352
375
  if (validatorRefreshTime < this.dateProvider.now()) {
353
376
  const currentSet = await this.rollup.getAttesters();
354
- this.allValidators = new Set(currentSet);
377
+ this.allValidators = new Set(currentSet.map(v => v.toString()));
355
378
  this.lastValidatorRefresh = this.dateProvider.now();
356
379
  }
357
- return Array.from(this.allValidators.keys().map(v => EthAddress.fromString(v)));
380
+ return Array.from(this.allValidators.keys()).map(v => EthAddress.fromString(v));
358
381
  }
359
382
  }
@@ -0,0 +1 @@
1
+ export * from './test_epoch_cache.js';
@@ -0,0 +1,170 @@
1
+ import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
4
+ import { getEpochAtSlot, getSlotAtTimestamp, getTimestampRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
5
+
6
+ import type { EpochAndSlot, EpochCacheInterface, EpochCommitteeInfo, SlotTag } from '../epoch_cache.js';
7
+
8
+ /** Default L1 constants for testing. */
9
+ const DEFAULT_L1_CONSTANTS: L1RollupConstants = {
10
+ l1StartBlock: 0n,
11
+ l1GenesisTime: 0n,
12
+ slotDuration: 24,
13
+ epochDuration: 16,
14
+ ethereumSlotDuration: 12,
15
+ proofSubmissionEpochs: 2,
16
+ targetCommitteeSize: 48,
17
+ };
18
+
19
+ /**
20
+ * A test implementation of EpochCacheInterface that allows manual configuration
21
+ * of committee, proposer, slot, and escape hatch state for use in tests.
22
+ *
23
+ * Unlike the real EpochCache, this class doesn't require any RPC connections
24
+ * or mock setup. Simply use the setter methods to configure the test state.
25
+ */
26
+ export class TestEpochCache implements EpochCacheInterface {
27
+ private committee: EthAddress[] = [];
28
+ private proposerAddress: EthAddress | undefined;
29
+ private currentSlot: SlotNumber = SlotNumber(0);
30
+ private escapeHatchOpen: boolean = false;
31
+ private seed: bigint = 0n;
32
+ private registeredValidators: EthAddress[] = [];
33
+ private l1Constants: L1RollupConstants;
34
+
35
+ constructor(l1Constants: Partial<L1RollupConstants> = {}) {
36
+ this.l1Constants = { ...DEFAULT_L1_CONSTANTS, ...l1Constants };
37
+ }
38
+
39
+ /**
40
+ * Sets the committee members. Used in validation and attestation flows.
41
+ * @param committee - Array of committee member addresses.
42
+ */
43
+ setCommittee(committee: EthAddress[]): this {
44
+ this.committee = committee;
45
+ return this;
46
+ }
47
+
48
+ /**
49
+ * Sets the proposer address returned by getProposerAttesterAddressInSlot.
50
+ * @param proposer - The address of the current proposer.
51
+ */
52
+ setProposer(proposer: EthAddress | undefined): this {
53
+ this.proposerAddress = proposer;
54
+ return this;
55
+ }
56
+
57
+ /**
58
+ * Sets the current slot number.
59
+ * @param slot - The slot number to set.
60
+ */
61
+ setCurrentSlot(slot: SlotNumber): this {
62
+ this.currentSlot = slot;
63
+ return this;
64
+ }
65
+
66
+ /**
67
+ * Sets whether the escape hatch is open.
68
+ * @param open - True if escape hatch should be open.
69
+ */
70
+ setEscapeHatchOpen(open: boolean): this {
71
+ this.escapeHatchOpen = open;
72
+ return this;
73
+ }
74
+
75
+ /**
76
+ * Sets the randomness seed used for proposer selection.
77
+ * @param seed - The seed value.
78
+ */
79
+ setSeed(seed: bigint): this {
80
+ this.seed = seed;
81
+ return this;
82
+ }
83
+
84
+ /**
85
+ * Sets the list of registered validators (all validators, not just committee).
86
+ * @param validators - Array of validator addresses.
87
+ */
88
+ setRegisteredValidators(validators: EthAddress[]): this {
89
+ this.registeredValidators = validators;
90
+ return this;
91
+ }
92
+
93
+ /**
94
+ * Sets the L1 constants used for epoch/slot calculations.
95
+ * @param constants - Partial constants to override defaults.
96
+ */
97
+ setL1Constants(constants: Partial<L1RollupConstants>): this {
98
+ this.l1Constants = { ...this.l1Constants, ...constants };
99
+ return this;
100
+ }
101
+
102
+ getL1Constants(): L1RollupConstants {
103
+ return this.l1Constants;
104
+ }
105
+
106
+ getCommittee(_slot?: SlotTag): Promise<EpochCommitteeInfo> {
107
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
108
+ return Promise.resolve({
109
+ committee: this.committee,
110
+ epoch,
111
+ seed: this.seed,
112
+ isEscapeHatchOpen: this.escapeHatchOpen,
113
+ });
114
+ }
115
+
116
+ getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
117
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
118
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
119
+ return { epoch, slot: this.currentSlot, ts, nowMs: ts * 1000n };
120
+ }
121
+
122
+ getEpochAndSlotInNextL1Slot(): EpochAndSlot & { now: bigint } {
123
+ const now = getTimestampRangeForEpoch(getEpochAtSlot(this.currentSlot, this.l1Constants), this.l1Constants)[0];
124
+ const nextSlotTs = now + BigInt(this.l1Constants.ethereumSlotDuration);
125
+ const nextSlot = getSlotAtTimestamp(nextSlotTs, this.l1Constants);
126
+ const epoch = getEpochAtSlot(nextSlot, this.l1Constants);
127
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
128
+ return { epoch, slot: nextSlot, ts, now };
129
+ }
130
+
131
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}` {
132
+ // Simple encoding for testing purposes
133
+ return `0x${epoch.toString(16).padStart(64, '0')}${slot.toString(16).padStart(64, '0')}${seed.toString(16).padStart(64, '0')}`;
134
+ }
135
+
136
+ computeProposerIndex(slot: SlotNumber, _epoch: EpochNumber, _seed: bigint, size: bigint): bigint {
137
+ if (size === 0n) {
138
+ return 0n;
139
+ }
140
+ return BigInt(slot) % size;
141
+ }
142
+
143
+ getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
144
+ return {
145
+ currentSlot: this.currentSlot,
146
+ nextSlot: SlotNumber(this.currentSlot + 1),
147
+ };
148
+ }
149
+
150
+ getProposerAttesterAddressInSlot(_slot: SlotNumber): Promise<EthAddress | undefined> {
151
+ return Promise.resolve(this.proposerAddress);
152
+ }
153
+
154
+ getRegisteredValidators(): Promise<EthAddress[]> {
155
+ return Promise.resolve(this.registeredValidators);
156
+ }
157
+
158
+ isInCommittee(_slot: SlotTag, validator: EthAddress): Promise<boolean> {
159
+ return Promise.resolve(this.committee.some(v => v.equals(validator)));
160
+ }
161
+
162
+ filterInCommittee(_slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]> {
163
+ const committeeSet = new Set(this.committee.map(v => v.toString()));
164
+ return Promise.resolve(validators.filter(v => committeeSet.has(v.toString())));
165
+ }
166
+
167
+ isEscapeHatchOpenAtSlot(_slot?: SlotTag): Promise<boolean> {
168
+ return Promise.resolve(this.escapeHatchOpen);
169
+ }
170
+ }