@aztec/epoch-cache 0.0.1-commit.b655e406 → 0.0.1-commit.c2595eba

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.
package/dest/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type L1ContractsConfig, type L1ReaderConfig } from '@aztec/ethereum';
1
+ import { type L1ContractsConfig } from '@aztec/ethereum/config';
2
+ import { type L1ReaderConfig } from '@aztec/ethereum/l1-reader';
2
3
  export type EpochCacheConfig = Pick<L1ReaderConfig & L1ContractsConfig, 'l1RpcUrls' | 'l1ChainId' | 'viemPollingIntervalMS' | 'ethereumSlotDuration'>;
3
4
  export declare function getEpochCacheConfigEnvVars(): EpochCacheConfig;
4
- //# sourceMappingURL=config.d.ts.map
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGlCQUFpQixFQUErQixNQUFNLHdCQUF3QixDQUFDO0FBQzdGLE9BQU8sRUFBRSxLQUFLLGNBQWMsRUFBNEIsTUFBTSwyQkFBMkIsQ0FBQztBQUUxRixNQUFNLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUNqQyxjQUFjLEdBQUcsaUJBQWlCLEVBQ2xDLFdBQVcsR0FBRyxXQUFXLEdBQUcsdUJBQXVCLEdBQUcsc0JBQXNCLENBQzdFLENBQUM7QUFFRix3QkFBZ0IsMEJBQTBCLElBQUksZ0JBQWdCLENBRTdEIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,cAAc,GAAG,iBAAiB,EAClC,WAAW,GAAG,WAAW,GAAG,uBAAuB,GAAG,sBAAsB,CAC7E,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,gBAAgB,CAE7D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAA+B,MAAM,wBAAwB,CAAC;AAC7F,OAAO,EAAE,KAAK,cAAc,EAA4B,MAAM,2BAA2B,CAAC;AAE1F,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,cAAc,GAAG,iBAAiB,EAClC,WAAW,GAAG,WAAW,GAAG,uBAAuB,GAAG,sBAAsB,CAC7E,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,gBAAgB,CAE7D"}
package/dest/config.js CHANGED
@@ -1,4 +1,5 @@
1
- import { getL1ContractsConfigEnvVars, getL1ReaderConfigFromEnv } from '@aztec/ethereum';
1
+ import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
2
+ import { getL1ReaderConfigFromEnv } from '@aztec/ethereum/l1-reader';
2
3
  export function getEpochCacheConfigEnvVars() {
3
4
  return {
4
5
  ...getL1ReaderConfigFromEnv(),
@@ -1,33 +1,37 @@
1
- import { RollupContract } from '@aztec/ethereum';
1
+ import { RollupContract } from '@aztec/ethereum/contracts';
2
+ import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
3
  import { EthAddress } from '@aztec/foundation/eth-address';
3
4
  import { DateProvider } from '@aztec/foundation/timer';
4
5
  import { type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
5
6
  import { type EpochCacheConfig } from './config.js';
6
7
  export type EpochAndSlot = {
7
- epoch: bigint;
8
- slot: bigint;
8
+ epoch: EpochNumber;
9
+ slot: SlotNumber;
9
10
  ts: bigint;
10
11
  };
11
12
  export type EpochCommitteeInfo = {
12
13
  committee: EthAddress[] | undefined;
13
14
  seed: bigint;
14
- epoch: bigint;
15
+ epoch: EpochNumber;
16
+ /** True if the epoch is within an open escape hatch window. */
17
+ isEscapeHatchOpen: boolean;
15
18
  };
16
- export type SlotTag = 'now' | 'next' | bigint;
19
+ export type SlotTag = 'now' | 'next' | SlotNumber;
17
20
  export interface EpochCacheInterface {
18
21
  getCommittee(slot: SlotTag | undefined): Promise<EpochCommitteeInfo>;
19
- getEpochAndSlotNow(): EpochAndSlot;
22
+ getEpochAndSlotNow(): EpochAndSlot & {
23
+ nowMs: bigint;
24
+ };
20
25
  getEpochAndSlotInNextL1Slot(): EpochAndSlot & {
21
26
  now: bigint;
22
27
  };
23
- getProposerIndexEncoding(epoch: bigint, slot: bigint, seed: bigint): `0x${string}`;
24
- computeProposerIndex(slot: bigint, epoch: bigint, seed: bigint, size: bigint): bigint;
25
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
26
- currentProposer: EthAddress | undefined;
27
- nextProposer: EthAddress | undefined;
28
- currentSlot: bigint;
29
- nextSlot: bigint;
30
- }>;
28
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
29
+ computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
30
+ getCurrentAndNextSlot(): {
31
+ currentSlot: SlotNumber;
32
+ nextSlot: SlotNumber;
33
+ };
34
+ getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined>;
31
35
  getRegisteredValidators(): Promise<EthAddress[]>;
32
36
  isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
33
37
  filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
@@ -49,11 +53,14 @@ export declare class EpochCache implements EpochCacheInterface {
49
53
  cacheSize: number;
50
54
  validatorRefreshIntervalSeconds: number;
51
55
  };
52
- protected cache: Map<bigint, EpochCommitteeInfo>;
56
+ protected cache: Map<EpochNumber, EpochCommitteeInfo>;
53
57
  private allValidators;
54
58
  private lastValidatorRefresh;
55
59
  private readonly log;
56
- constructor(rollup: RollupContract, l1constants?: L1RollupConstants, dateProvider?: DateProvider, config?: {
60
+ constructor(rollup: RollupContract, l1constants: L1RollupConstants & {
61
+ lagInEpochsForValidatorSet: number;
62
+ lagInEpochsForRandao: number;
63
+ }, dateProvider?: DateProvider, config?: {
57
64
  cacheSize: number;
58
65
  validatorRefreshIntervalSeconds: number;
59
66
  });
@@ -62,7 +69,7 @@ export declare class EpochCache implements EpochCacheInterface {
62
69
  }): Promise<EpochCache>;
63
70
  getL1Constants(): L1RollupConstants;
64
71
  getEpochAndSlotNow(): EpochAndSlot & {
65
- now: bigint;
72
+ nowMs: bigint;
66
73
  };
67
74
  nowInSeconds(): bigint;
68
75
  private getEpochAndSlotAtSlot;
@@ -70,7 +77,21 @@ export declare class EpochCache implements EpochCacheInterface {
70
77
  now: bigint;
71
78
  };
72
79
  private getEpochAndSlotAtTimestamp;
73
- getCommitteeForEpoch(epoch: bigint): Promise<EpochCommitteeInfo>;
80
+ getCommitteeForEpoch(epoch: EpochNumber): Promise<EpochCommitteeInfo>;
81
+ /**
82
+ * Returns whether the escape hatch is open for the given epoch.
83
+ *
84
+ * Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
85
+ * the epoch committee info (which includes the escape hatch flag) and return it.
86
+ */
87
+ isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean>;
88
+ /**
89
+ * Returns whether the escape hatch is open for the epoch containing the given slot.
90
+ *
91
+ * This is a lightweight helper intended for callers that already have a slot number and only
92
+ * need the escape hatch flag (without pulling full committee info).
93
+ */
94
+ isEscapeHatchOpenAtSlot(slot?: SlotTag): Promise<boolean>;
74
95
  /**
75
96
  * Get the current validator set
76
97
  * @param nextSlot - If true, get the validator set for the next slot.
@@ -82,44 +103,31 @@ export declare class EpochCache implements EpochCacheInterface {
82
103
  /**
83
104
  * Get the ABI encoding of the proposer index - see ValidatorSelectionLib.sol computeProposerIndex
84
105
  */
85
- getProposerIndexEncoding(epoch: bigint, slot: bigint, seed: bigint): `0x${string}`;
86
- computeProposerIndex(slot: bigint, epoch: bigint, seed: bigint, size: bigint): bigint;
87
- /**
88
- * Returns the current and next proposer's attester address
89
- *
90
- * We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
91
- * which can be the next slot. If this is the case, then it will send proposals early.
92
- */
93
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
94
- currentSlot: bigint;
95
- nextSlot: bigint;
96
- currentProposer: EthAddress | undefined;
97
- nextProposer: EthAddress | undefined;
98
- }>;
106
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
107
+ computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
108
+ /** Returns the current and next L2 slot numbers. */
109
+ getCurrentAndNextSlot(): {
110
+ currentSlot: SlotNumber;
111
+ nextSlot: SlotNumber;
112
+ };
99
113
  /**
100
114
  * Get the proposer attester address in the given L2 slot
101
115
  * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
102
116
  * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
103
117
  */
104
- getProposerAttesterAddressInSlot(slot: bigint): Promise<EthAddress | undefined>;
118
+ getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined>;
105
119
  /**
106
120
  * Get the proposer attester address in the next slot
107
121
  * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
108
122
  * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
109
123
  */
110
124
  getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined>;
111
- /**
112
- * Get the proposer attester address at a given epoch and slot
113
- * @param when - The epoch and slot to get the proposer attester address at
114
- * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
115
- * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
116
- */
117
125
  private getProposerAttesterAddressAt;
118
- getProposerFromEpochCommittee(epochCommitteeInfo: EpochCommitteeInfo, slot: bigint): EthAddress | undefined;
126
+ getProposerFromEpochCommittee(epochCommitteeInfo: EpochCommitteeInfo, slot: SlotNumber): EthAddress | undefined;
119
127
  /** Check if a validator is in the given slot's committee */
120
128
  isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
121
129
  /** From the set of given addresses, return all that are on the committee for the given slot */
122
130
  filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
123
131
  getRegisteredValidators(): Promise<EthAddress[]>;
124
132
  }
125
- //# sourceMappingURL=epoch_cache.d.ts.map
133
+ //# 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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"}
@@ -1,8 +1,9 @@
1
- import { NoCommitteeError, RollupContract, createEthereumChain } from '@aztec/ethereum';
1
+ import { createEthereumChain } from '@aztec/ethereum/chain';
2
+ import { NoCommitteeError, RollupContract } from '@aztec/ethereum/contracts';
2
3
  import { EthAddress } from '@aztec/foundation/eth-address';
3
4
  import { createLogger } from '@aztec/foundation/log';
4
5
  import { DateProvider } from '@aztec/foundation/timer';
5
- import { EmptyL1RollupConstants, getEpochAtSlot, getEpochNumberAtTimestamp, getSlotAtTimestamp, getSlotRangeForEpoch, getTimestampForSlot, getTimestampRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
6
+ import { getEpochAtSlot, getEpochNumberAtTimestamp, getSlotAtTimestamp, getSlotRangeForEpoch, getTimestampForSlot, getTimestampRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
6
7
  import { createPublicClient, encodeAbiParameters, fallback, http, keccak256 } from 'viem';
7
8
  import { getEpochCacheConfigEnvVars } from './config.js';
8
9
  /**
@@ -18,11 +19,12 @@ import { getEpochCacheConfigEnvVars } from './config.js';
18
19
  l1constants;
19
20
  dateProvider;
20
21
  config;
22
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
21
23
  cache;
22
24
  allValidators;
23
25
  lastValidatorRefresh;
24
26
  log;
25
- constructor(rollup, l1constants = EmptyL1RollupConstants, dateProvider = new DateProvider(), config = {
27
+ constructor(rollup, l1constants, dateProvider = new DateProvider(), config = {
26
28
  cacheSize: 12,
27
29
  validatorRefreshIntervalSeconds: 60
28
30
  }){
@@ -48,17 +50,21 @@ import { getEpochCacheConfigEnvVars } from './config.js';
48
50
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
49
51
  const publicClient = createPublicClient({
50
52
  chain: chain.chainInfo,
51
- transport: fallback(config.l1RpcUrls.map((url)=>http(url))),
53
+ transport: fallback(config.l1RpcUrls.map((url)=>http(url, {
54
+ batch: false
55
+ }))),
52
56
  pollingInterval: config.viemPollingIntervalMS
53
57
  });
54
58
  rollup = new RollupContract(publicClient, rollupOrAddress.toString());
55
59
  }
56
- const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration] = await Promise.all([
60
+ const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration, lagInEpochsForValidatorSet, lagInEpochsForRandao] = await Promise.all([
57
61
  rollup.getL1StartBlock(),
58
62
  rollup.getL1GenesisTime(),
59
63
  rollup.getProofSubmissionEpochs(),
60
64
  rollup.getSlotDuration(),
61
- rollup.getEpochDuration()
65
+ rollup.getEpochDuration(),
66
+ rollup.getLagInEpochsForValidatorSet(),
67
+ rollup.getLagInEpochsForRandao()
62
68
  ]);
63
69
  const l1RollupConstants = {
64
70
  l1StartBlock,
@@ -66,7 +72,9 @@ import { getEpochCacheConfigEnvVars } from './config.js';
66
72
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
67
73
  slotDuration: Number(slotDuration),
68
74
  epochDuration: Number(epochDuration),
69
- ethereumSlotDuration: config.ethereumSlotDuration
75
+ ethereumSlotDuration: config.ethereumSlotDuration,
76
+ lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
77
+ lagInEpochsForRandao: Number(lagInEpochsForRandao)
70
78
  };
71
79
  return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
72
80
  }
@@ -74,10 +82,11 @@ import { getEpochCacheConfigEnvVars } from './config.js';
74
82
  return this.l1constants;
75
83
  }
76
84
  getEpochAndSlotNow() {
77
- const now = this.nowInSeconds();
85
+ const nowMs = BigInt(this.dateProvider.now());
86
+ const nowSeconds = nowMs / 1000n;
78
87
  return {
79
- ...this.getEpochAndSlotAtTimestamp(now),
80
- now
88
+ ...this.getEpochAndSlotAtTimestamp(nowSeconds),
89
+ nowMs
81
90
  };
82
91
  }
83
92
  nowInSeconds() {
@@ -113,6 +122,28 @@ import { getEpochCacheConfigEnvVars } from './config.js';
113
122
  return this.getCommittee(startSlot);
114
123
  }
115
124
  /**
125
+ * Returns whether the escape hatch is open for the given epoch.
126
+ *
127
+ * Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
128
+ * the epoch committee info (which includes the escape hatch flag) and return it.
129
+ */ async isEscapeHatchOpen(epoch) {
130
+ const cached = this.cache.get(epoch);
131
+ if (cached) {
132
+ return cached.isEscapeHatchOpen;
133
+ }
134
+ const info = await this.getCommitteeForEpoch(epoch);
135
+ return info.isEscapeHatchOpen;
136
+ }
137
+ /**
138
+ * Returns whether the escape hatch is open for the epoch containing the given slot.
139
+ *
140
+ * This is a lightweight helper intended for callers that already have a slot number and only
141
+ * need the escape hatch flag (without pulling full committee info).
142
+ */ async isEscapeHatchOpenAtSlot(slot = 'now') {
143
+ const epoch = slot === 'now' ? this.getEpochAndSlotNow().epoch : slot === 'next' ? this.getEpochAndSlotInNextL1Slot().epoch : getEpochAtSlot(slot, this.l1constants);
144
+ return await this.isEscapeHatchOpen(epoch);
145
+ }
146
+ /**
116
147
  * Get the current validator set
117
148
  * @param nextSlot - If true, get the validator set for the next slot.
118
149
  * @returns The current validator set.
@@ -145,15 +176,24 @@ import { getEpochCacheConfigEnvVars } from './config.js';
145
176
  }
146
177
  async computeCommittee(when) {
147
178
  const { ts, epoch } = when;
148
- const [committeeHex, seed] = await Promise.all([
179
+ const [committee, seedBuffer, l1Timestamp, isEscapeHatchOpen] = await Promise.all([
149
180
  this.rollup.getCommitteeAt(ts),
150
- this.rollup.getSampleSeedAt(ts)
181
+ this.rollup.getSampleSeedAt(ts),
182
+ this.rollup.client.getBlock({
183
+ includeTransactions: false
184
+ }).then((b)=>b.timestamp),
185
+ this.rollup.isEscapeHatchOpen(epoch)
151
186
  ]);
152
- const committee = committeeHex?.map((v)=>EthAddress.fromString(v));
187
+ const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
188
+ const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
189
+ if (ts - sub > l1Timestamp) {
190
+ throw new Error(`Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`);
191
+ }
153
192
  return {
154
193
  committee,
155
- seed,
156
- epoch
194
+ seed: seedBuffer.toBigInt(),
195
+ epoch,
196
+ isEscapeHatchOpen
157
197
  };
158
198
  }
159
199
  /**
@@ -173,8 +213,8 @@ import { getEpochCacheConfigEnvVars } from './config.js';
173
213
  name: 'seed'
174
214
  }
175
215
  ], [
176
- epoch,
177
- slot,
216
+ BigInt(epoch),
217
+ BigInt(slot),
178
218
  seed
179
219
  ]);
180
220
  }
@@ -185,17 +225,10 @@ import { getEpochCacheConfigEnvVars } from './config.js';
185
225
  }
186
226
  return BigInt(keccak256(this.getProposerIndexEncoding(epoch, slot, seed))) % size;
187
227
  }
188
- /**
189
- * Returns the current and next proposer's attester address
190
- *
191
- * We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
192
- * which can be the next slot. If this is the case, then it will send proposals early.
193
- */ async getProposerAttesterAddressInCurrentOrNextSlot() {
228
+ /** Returns the current and next L2 slot numbers. */ getCurrentAndNextSlot() {
194
229
  const current = this.getEpochAndSlotNow();
195
230
  const next = this.getEpochAndSlotInNextL1Slot();
196
231
  return {
197
- currentProposer: await this.getProposerAttesterAddressAt(current),
198
- nextProposer: await this.getProposerAttesterAddressAt(next),
199
232
  currentSlot: current.slot,
200
233
  nextSlot: next.slot
201
234
  };
@@ -259,9 +292,9 @@ import { getEpochCacheConfigEnvVars } from './config.js';
259
292
  const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
260
293
  if (validatorRefreshTime < this.dateProvider.now()) {
261
294
  const currentSet = await this.rollup.getAttesters();
262
- this.allValidators = new Set(currentSet);
295
+ this.allValidators = new Set(currentSet.map((v)=>v.toString()));
263
296
  this.lastValidatorRefresh = this.dateProvider.now();
264
297
  }
265
- return Array.from(this.allValidators.keys().map((v)=>EthAddress.fromString(v)));
298
+ return Array.from(this.allValidators.keys()).map((v)=>EthAddress.fromString(v));
266
299
  }
267
300
  }
package/dest/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from './epoch_cache.js';
2
2
  export * from './config.js';
3
- //# sourceMappingURL=index.d.ts.map
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsYUFBYSxDQUFDIn0=
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdF9lcG9jaF9jYWNoZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3QvdGVzdF9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUMzRCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBR3JFLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxrQkFBa0IsRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQVl4Rzs7Ozs7O0dBTUc7QUFDSCxxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBQ3hELE9BQU8sQ0FBQyxTQUFTLENBQW9CO0lBQ3JDLE9BQU8sQ0FBQyxlQUFlLENBQXlCO0lBQ2hELE9BQU8sQ0FBQyxXQUFXLENBQTZCO0lBQ2hELE9BQU8sQ0FBQyxlQUFlLENBQWtCO0lBQ3pDLE9BQU8sQ0FBQyxJQUFJLENBQWM7SUFDMUIsT0FBTyxDQUFDLG9CQUFvQixDQUFvQjtJQUNoRCxPQUFPLENBQUMsV0FBVyxDQUFvQjtJQUV2QyxZQUFZLFdBQVcsR0FBRSxPQUFPLENBQUMsaUJBQWlCLENBQU0sRUFFdkQ7SUFFRDs7O09BR0c7SUFDSCxZQUFZLENBQUMsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FHMUM7SUFFRDs7O09BR0c7SUFDSCxXQUFXLENBQUMsUUFBUSxFQUFFLFVBQVUsR0FBRyxTQUFTLEdBQUcsSUFBSSxDQUdsRDtJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxJQUFJLEVBQUUsVUFBVSxHQUFHLElBQUksQ0FHckM7SUFFRDs7O09BR0c7SUFDSCxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsT0FBTyxHQUFHLElBQUksQ0FHdEM7SUFFRDs7O09BR0c7SUFDSCxPQUFPLENBQUMsSUFBSSxFQUFFLE1BQU0sR0FBRyxJQUFJLENBRzFCO0lBRUQ7OztPQUdHO0lBQ0gsdUJBQXVCLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FHdEQ7SUFFRDs7O09BR0c7SUFDSCxjQUFjLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLElBQUksQ0FHMUQ7SUFFRCxjQUFjLElBQUksaUJBQWlCLENBRWxDO0lBRUQsWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FRekQ7SUFFRCxrQkFBa0IsSUFBSSxZQUFZLEdBQUc7UUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FJckQ7SUFFRCwyQkFBMkIsSUFBSSxZQUFZLEdBQUc7UUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FPNUQ7SUFFRCx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxLQUFLLE1BQU0sRUFBRSxDQUcxRjtJQUVELG9CQUFvQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsTUFBTSxDQUsvRjtJQUVELHFCQUFxQixJQUFJO1FBQUUsV0FBVyxFQUFFLFVBQVUsQ0FBQztRQUFDLFFBQVEsRUFBRSxVQUFVLENBQUE7S0FBRSxDQUt6RTtJQUVELGdDQUFnQyxDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUMsQ0FFbkY7SUFFRCx1QkFBdUIsSUFBSSxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FFL0M7SUFFRCxhQUFhLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FFckU7SUFFRCxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsR0FBRyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FHakY7SUFFRCx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUV6RDtDQUNGIn0=
@@ -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;AAYxG;;;;;;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,150 @@
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
+ };
11
+ /**
12
+ * A test implementation of EpochCacheInterface that allows manual configuration
13
+ * of committee, proposer, slot, and escape hatch state for use in tests.
14
+ *
15
+ * Unlike the real EpochCache, this class doesn't require any RPC connections
16
+ * or mock setup. Simply use the setter methods to configure the test state.
17
+ */ export class TestEpochCache {
18
+ committee = [];
19
+ proposerAddress;
20
+ currentSlot = SlotNumber(0);
21
+ escapeHatchOpen = false;
22
+ seed = 0n;
23
+ registeredValidators = [];
24
+ l1Constants;
25
+ constructor(l1Constants = {}){
26
+ this.l1Constants = {
27
+ ...DEFAULT_L1_CONSTANTS,
28
+ ...l1Constants
29
+ };
30
+ }
31
+ /**
32
+ * Sets the committee members. Used in validation and attestation flows.
33
+ * @param committee - Array of committee member addresses.
34
+ */ setCommittee(committee) {
35
+ this.committee = committee;
36
+ return this;
37
+ }
38
+ /**
39
+ * Sets the proposer address returned by getProposerAttesterAddressInSlot.
40
+ * @param proposer - The address of the current proposer.
41
+ */ setProposer(proposer) {
42
+ this.proposerAddress = proposer;
43
+ return this;
44
+ }
45
+ /**
46
+ * Sets the current slot number.
47
+ * @param slot - The slot number to set.
48
+ */ setCurrentSlot(slot) {
49
+ this.currentSlot = slot;
50
+ return this;
51
+ }
52
+ /**
53
+ * Sets whether the escape hatch is open.
54
+ * @param open - True if escape hatch should be open.
55
+ */ setEscapeHatchOpen(open) {
56
+ this.escapeHatchOpen = open;
57
+ return this;
58
+ }
59
+ /**
60
+ * Sets the randomness seed used for proposer selection.
61
+ * @param seed - The seed value.
62
+ */ setSeed(seed) {
63
+ this.seed = seed;
64
+ return this;
65
+ }
66
+ /**
67
+ * Sets the list of registered validators (all validators, not just committee).
68
+ * @param validators - Array of validator addresses.
69
+ */ setRegisteredValidators(validators) {
70
+ this.registeredValidators = validators;
71
+ return this;
72
+ }
73
+ /**
74
+ * Sets the L1 constants used for epoch/slot calculations.
75
+ * @param constants - Partial constants to override defaults.
76
+ */ setL1Constants(constants) {
77
+ this.l1Constants = {
78
+ ...this.l1Constants,
79
+ ...constants
80
+ };
81
+ return this;
82
+ }
83
+ getL1Constants() {
84
+ return this.l1Constants;
85
+ }
86
+ getCommittee(_slot) {
87
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
88
+ return Promise.resolve({
89
+ committee: this.committee,
90
+ epoch,
91
+ seed: this.seed,
92
+ isEscapeHatchOpen: this.escapeHatchOpen
93
+ });
94
+ }
95
+ getEpochAndSlotNow() {
96
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
97
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
98
+ return {
99
+ epoch,
100
+ slot: this.currentSlot,
101
+ ts,
102
+ nowMs: ts * 1000n
103
+ };
104
+ }
105
+ getEpochAndSlotInNextL1Slot() {
106
+ const now = getTimestampRangeForEpoch(getEpochAtSlot(this.currentSlot, this.l1Constants), this.l1Constants)[0];
107
+ const nextSlotTs = now + BigInt(this.l1Constants.ethereumSlotDuration);
108
+ const nextSlot = getSlotAtTimestamp(nextSlotTs, this.l1Constants);
109
+ const epoch = getEpochAtSlot(nextSlot, this.l1Constants);
110
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
111
+ return {
112
+ epoch,
113
+ slot: nextSlot,
114
+ ts,
115
+ now
116
+ };
117
+ }
118
+ getProposerIndexEncoding(epoch, slot, seed) {
119
+ // Simple encoding for testing purposes
120
+ return `0x${epoch.toString(16).padStart(64, '0')}${slot.toString(16).padStart(64, '0')}${seed.toString(16).padStart(64, '0')}`;
121
+ }
122
+ computeProposerIndex(slot, _epoch, _seed, size) {
123
+ if (size === 0n) {
124
+ return 0n;
125
+ }
126
+ return BigInt(slot) % size;
127
+ }
128
+ getCurrentAndNextSlot() {
129
+ return {
130
+ currentSlot: this.currentSlot,
131
+ nextSlot: SlotNumber(this.currentSlot + 1)
132
+ };
133
+ }
134
+ getProposerAttesterAddressInSlot(_slot) {
135
+ return Promise.resolve(this.proposerAddress);
136
+ }
137
+ getRegisteredValidators() {
138
+ return Promise.resolve(this.registeredValidators);
139
+ }
140
+ isInCommittee(_slot, validator) {
141
+ return Promise.resolve(this.committee.some((v)=>v.equals(validator)));
142
+ }
143
+ filterInCommittee(_slot, validators) {
144
+ const committeeSet = new Set(this.committee.map((v)=>v.toString()));
145
+ return Promise.resolve(validators.filter((v)=>committeeSet.has(v.toString())));
146
+ }
147
+ isEscapeHatchOpenAtSlot(_slot) {
148
+ return Promise.resolve(this.escapeHatchOpen);
149
+ }
150
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/epoch-cache",
3
- "version": "0.0.1-commit.b655e406",
3
+ "version": "0.0.1-commit.c2595eba",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -15,10 +15,10 @@
15
15
  "tsconfig": "./tsconfig.json"
16
16
  },
17
17
  "scripts": {
18
- "build": "yarn clean && tsc -b",
19
- "build:dev": "tsc -b --watch",
18
+ "build": "yarn clean && ../scripts/tsc.sh",
19
+ "build:dev": "../scripts/tsc.sh --watch",
20
20
  "clean": "rm -rf ./dest .tsbuildinfo",
21
- "start:dev": "tsc-watch -p tsconfig.json --onSuccess '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,22 +26,23 @@
26
26
  "../package.common.json"
27
27
  ],
28
28
  "dependencies": {
29
- "@aztec/ethereum": "0.0.1-commit.b655e406",
30
- "@aztec/foundation": "0.0.1-commit.b655e406",
31
- "@aztec/l1-artifacts": "0.0.1-commit.b655e406",
32
- "@aztec/stdlib": "0.0.1-commit.b655e406",
29
+ "@aztec/ethereum": "0.0.1-commit.c2595eba",
30
+ "@aztec/foundation": "0.0.1-commit.c2595eba",
31
+ "@aztec/l1-artifacts": "0.0.1-commit.c2595eba",
32
+ "@aztec/stdlib": "0.0.1-commit.c2595eba",
33
33
  "@viem/anvil": "^0.0.10",
34
34
  "dotenv": "^16.0.3",
35
35
  "get-port": "^7.1.0",
36
36
  "jest-mock-extended": "^4.0.0",
37
37
  "tslib": "^2.4.0",
38
- "viem": "npm:@spalladino/viem@2.38.2-eip7594.0",
38
+ "viem": "npm:@aztec/viem@2.38.2",
39
39
  "zod": "^3.23.8"
40
40
  },
41
41
  "devDependencies": {
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.20260113.1",
45
46
  "jest": "^30.0.0",
46
47
  "ts-node": "^10.9.1",
47
48
  "typescript": "^5.3.3"
package/src/config.ts CHANGED
@@ -1,9 +1,5 @@
1
- import {
2
- type L1ContractsConfig,
3
- type L1ReaderConfig,
4
- getL1ContractsConfigEnvVars,
5
- getL1ReaderConfigFromEnv,
6
- } from '@aztec/ethereum';
1
+ import { type L1ContractsConfig, getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
2
+ import { type L1ReaderConfig, getL1ReaderConfigFromEnv } from '@aztec/ethereum/l1-reader';
7
3
 
8
4
  export type EpochCacheConfig = Pick<
9
5
  L1ReaderConfig & L1ContractsConfig,
@@ -1,9 +1,10 @@
1
- import { NoCommitteeError, RollupContract, createEthereumChain } from '@aztec/ethereum';
1
+ import { createEthereumChain } from '@aztec/ethereum/chain';
2
+ import { NoCommitteeError, RollupContract } from '@aztec/ethereum/contracts';
3
+ import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
4
  import { EthAddress } from '@aztec/foundation/eth-address';
3
5
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
6
  import { DateProvider } from '@aztec/foundation/timer';
5
7
  import {
6
- EmptyL1RollupConstants,
7
8
  type L1RollupConstants,
8
9
  getEpochAtSlot,
9
10
  getEpochNumberAtTimestamp,
@@ -18,31 +19,29 @@ import { createPublicClient, encodeAbiParameters, fallback, http, keccak256 } fr
18
19
  import { type EpochCacheConfig, getEpochCacheConfigEnvVars } from './config.js';
19
20
 
20
21
  export type EpochAndSlot = {
21
- epoch: bigint;
22
- slot: bigint;
22
+ epoch: EpochNumber;
23
+ slot: SlotNumber;
23
24
  ts: bigint;
24
25
  };
25
26
 
26
27
  export type EpochCommitteeInfo = {
27
28
  committee: EthAddress[] | undefined;
28
29
  seed: bigint;
29
- epoch: bigint;
30
+ epoch: EpochNumber;
31
+ /** True if the epoch is within an open escape hatch window. */
32
+ isEscapeHatchOpen: boolean;
30
33
  };
31
34
 
32
- export type SlotTag = 'now' | 'next' | bigint;
35
+ export type SlotTag = 'now' | 'next' | SlotNumber;
33
36
 
34
37
  export interface EpochCacheInterface {
35
38
  getCommittee(slot: SlotTag | undefined): Promise<EpochCommitteeInfo>;
36
- getEpochAndSlotNow(): EpochAndSlot;
39
+ getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint };
37
40
  getEpochAndSlotInNextL1Slot(): EpochAndSlot & { now: bigint };
38
- getProposerIndexEncoding(epoch: bigint, slot: bigint, seed: bigint): `0x${string}`;
39
- computeProposerIndex(slot: bigint, epoch: bigint, seed: bigint, size: bigint): bigint;
40
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
41
- currentProposer: EthAddress | undefined;
42
- nextProposer: EthAddress | undefined;
43
- currentSlot: bigint;
44
- nextSlot: bigint;
45
- }>;
41
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
42
+ computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
43
+ getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber };
44
+ getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined>;
46
45
  getRegisteredValidators(): Promise<EthAddress[]>;
47
46
  isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
48
47
  filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
@@ -58,14 +57,18 @@ export interface EpochCacheInterface {
58
57
  * Note: This class is very dependent on the system clock being in sync.
59
58
  */
60
59
  export class EpochCache implements EpochCacheInterface {
61
- protected cache: Map<bigint, EpochCommitteeInfo> = new Map();
60
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
61
+ protected cache: Map<EpochNumber, EpochCommitteeInfo> = new Map();
62
62
  private allValidators: Set<string> = new Set();
63
63
  private lastValidatorRefresh = 0;
64
64
  private readonly log: Logger = createLogger('epoch-cache');
65
65
 
66
66
  constructor(
67
67
  private rollup: RollupContract,
68
- private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
68
+ private readonly l1constants: L1RollupConstants & {
69
+ lagInEpochsForValidatorSet: number;
70
+ lagInEpochsForRandao: number;
71
+ },
69
72
  private readonly dateProvider: DateProvider = new DateProvider(),
70
73
  protected readonly config = { cacheSize: 12, validatorRefreshIntervalSeconds: 60 },
71
74
  ) {
@@ -89,27 +92,39 @@ export class EpochCache implements EpochCacheInterface {
89
92
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
90
93
  const publicClient = createPublicClient({
91
94
  chain: chain.chainInfo,
92
- transport: fallback(config.l1RpcUrls.map(url => http(url))),
95
+ transport: fallback(config.l1RpcUrls.map(url => http(url, { batch: false }))),
93
96
  pollingInterval: config.viemPollingIntervalMS,
94
97
  });
95
98
  rollup = new RollupContract(publicClient, rollupOrAddress.toString());
96
99
  }
97
100
 
98
- const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration] = await Promise.all([
101
+ const [
102
+ l1StartBlock,
103
+ l1GenesisTime,
104
+ proofSubmissionEpochs,
105
+ slotDuration,
106
+ epochDuration,
107
+ lagInEpochsForValidatorSet,
108
+ lagInEpochsForRandao,
109
+ ] = await Promise.all([
99
110
  rollup.getL1StartBlock(),
100
111
  rollup.getL1GenesisTime(),
101
112
  rollup.getProofSubmissionEpochs(),
102
113
  rollup.getSlotDuration(),
103
114
  rollup.getEpochDuration(),
115
+ rollup.getLagInEpochsForValidatorSet(),
116
+ rollup.getLagInEpochsForRandao(),
104
117
  ] as const);
105
118
 
106
- const l1RollupConstants: L1RollupConstants = {
119
+ const l1RollupConstants = {
107
120
  l1StartBlock,
108
121
  l1GenesisTime,
109
122
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
110
123
  slotDuration: Number(slotDuration),
111
124
  epochDuration: Number(epochDuration),
112
125
  ethereumSlotDuration: config.ethereumSlotDuration,
126
+ lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
127
+ lagInEpochsForRandao: Number(lagInEpochsForRandao),
113
128
  };
114
129
 
115
130
  return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
@@ -119,16 +134,17 @@ export class EpochCache implements EpochCacheInterface {
119
134
  return this.l1constants;
120
135
  }
121
136
 
122
- public getEpochAndSlotNow(): EpochAndSlot & { now: bigint } {
123
- const now = this.nowInSeconds();
124
- return { ...this.getEpochAndSlotAtTimestamp(now), now };
137
+ public getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
138
+ const nowMs = BigInt(this.dateProvider.now());
139
+ const nowSeconds = nowMs / 1000n;
140
+ return { ...this.getEpochAndSlotAtTimestamp(nowSeconds), nowMs };
125
141
  }
126
142
 
127
143
  public nowInSeconds(): bigint {
128
144
  return BigInt(Math.floor(this.dateProvider.now() / 1000));
129
145
  }
130
146
 
131
- private getEpochAndSlotAtSlot(slot: bigint): EpochAndSlot {
147
+ private getEpochAndSlotAtSlot(slot: SlotNumber): EpochAndSlot {
132
148
  const epoch = getEpochAtSlot(slot, this.l1constants);
133
149
  const ts = getTimestampRangeForEpoch(epoch, this.l1constants)[0];
134
150
  return { epoch, ts, slot };
@@ -149,11 +165,43 @@ export class EpochCache implements EpochCacheInterface {
149
165
  };
150
166
  }
151
167
 
152
- public getCommitteeForEpoch(epoch: bigint): Promise<EpochCommitteeInfo> {
168
+ public getCommitteeForEpoch(epoch: EpochNumber): Promise<EpochCommitteeInfo> {
153
169
  const [startSlot] = getSlotRangeForEpoch(epoch, this.l1constants);
154
170
  return this.getCommittee(startSlot);
155
171
  }
156
172
 
173
+ /**
174
+ * Returns whether the escape hatch is open for the given epoch.
175
+ *
176
+ * Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
177
+ * the epoch committee info (which includes the escape hatch flag) and return it.
178
+ */
179
+ public async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
180
+ const cached = this.cache.get(epoch);
181
+ if (cached) {
182
+ return cached.isEscapeHatchOpen;
183
+ }
184
+ const info = await this.getCommitteeForEpoch(epoch);
185
+ return info.isEscapeHatchOpen;
186
+ }
187
+
188
+ /**
189
+ * Returns whether the escape hatch is open for the epoch containing the given slot.
190
+ *
191
+ * This is a lightweight helper intended for callers that already have a slot number and only
192
+ * need the escape hatch flag (without pulling full committee info).
193
+ */
194
+ public async isEscapeHatchOpenAtSlot(slot: SlotTag = 'now'): Promise<boolean> {
195
+ const epoch =
196
+ slot === 'now'
197
+ ? this.getEpochAndSlotNow().epoch
198
+ : slot === 'next'
199
+ ? this.getEpochAndSlotInNextL1Slot().epoch
200
+ : getEpochAtSlot(slot, this.l1constants);
201
+
202
+ return await this.isEscapeHatchOpen(epoch);
203
+ }
204
+
157
205
  /**
158
206
  * Get the current validator set
159
207
  * @param nextSlot - If true, get the validator set for the next slot.
@@ -191,28 +239,39 @@ export class EpochCache implements EpochCacheInterface {
191
239
  }
192
240
  }
193
241
 
194
- private async computeCommittee(when: { epoch: bigint; ts: bigint }): Promise<EpochCommitteeInfo> {
242
+ private async computeCommittee(when: { epoch: EpochNumber; ts: bigint }): Promise<EpochCommitteeInfo> {
195
243
  const { ts, epoch } = when;
196
- const [committeeHex, seed] = await Promise.all([this.rollup.getCommitteeAt(ts), this.rollup.getSampleSeedAt(ts)]);
197
- const committee = committeeHex?.map((v: `0x${string}`) => EthAddress.fromString(v));
198
- return { committee, seed, epoch };
244
+ const [committee, seedBuffer, l1Timestamp, isEscapeHatchOpen] = await Promise.all([
245
+ this.rollup.getCommitteeAt(ts),
246
+ this.rollup.getSampleSeedAt(ts),
247
+ this.rollup.client.getBlock({ includeTransactions: false }).then(b => b.timestamp),
248
+ this.rollup.isEscapeHatchOpen(epoch),
249
+ ]);
250
+ const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
251
+ const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
252
+ if (ts - sub > l1Timestamp) {
253
+ throw new Error(
254
+ `Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`,
255
+ );
256
+ }
257
+ return { committee, seed: seedBuffer.toBigInt(), epoch, isEscapeHatchOpen };
199
258
  }
200
259
 
201
260
  /**
202
261
  * Get the ABI encoding of the proposer index - see ValidatorSelectionLib.sol computeProposerIndex
203
262
  */
204
- getProposerIndexEncoding(epoch: bigint, slot: bigint, seed: bigint): `0x${string}` {
263
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}` {
205
264
  return encodeAbiParameters(
206
265
  [
207
266
  { type: 'uint256', name: 'epoch' },
208
267
  { type: 'uint256', name: 'slot' },
209
268
  { type: 'uint256', name: 'seed' },
210
269
  ],
211
- [epoch, slot, seed],
270
+ [BigInt(epoch), BigInt(slot), seed],
212
271
  );
213
272
  }
214
273
 
215
- public computeProposerIndex(slot: bigint, epoch: bigint, seed: bigint, size: bigint): bigint {
274
+ public computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint {
216
275
  // if committe size is 0, then mod 1 is 0
217
276
  if (size === 0n) {
218
277
  return 0n;
@@ -220,24 +279,12 @@ export class EpochCache implements EpochCacheInterface {
220
279
  return BigInt(keccak256(this.getProposerIndexEncoding(epoch, slot, seed))) % size;
221
280
  }
222
281
 
223
- /**
224
- * Returns the current and next proposer's attester address
225
- *
226
- * We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
227
- * which can be the next slot. If this is the case, then it will send proposals early.
228
- */
229
- public async getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
230
- currentSlot: bigint;
231
- nextSlot: bigint;
232
- currentProposer: EthAddress | undefined;
233
- nextProposer: EthAddress | undefined;
234
- }> {
282
+ /** Returns the current and next L2 slot numbers. */
283
+ public getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
235
284
  const current = this.getEpochAndSlotNow();
236
285
  const next = this.getEpochAndSlotInNextL1Slot();
237
286
 
238
287
  return {
239
- currentProposer: await this.getProposerAttesterAddressAt(current),
240
- nextProposer: await this.getProposerAttesterAddressAt(next),
241
288
  currentSlot: current.slot,
242
289
  nextSlot: next.slot,
243
290
  };
@@ -248,7 +295,7 @@ export class EpochCache implements EpochCacheInterface {
248
295
  * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
249
296
  * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
250
297
  */
251
- public getProposerAttesterAddressInSlot(slot: bigint): Promise<EthAddress | undefined> {
298
+ public getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined> {
252
299
  const epochAndSlot = this.getEpochAndSlotAtSlot(slot);
253
300
  return this.getProposerAttesterAddressAt(epochAndSlot);
254
301
  }
@@ -282,7 +329,10 @@ export class EpochCache implements EpochCacheInterface {
282
329
  return committee[Number(proposerIndex)];
283
330
  }
284
331
 
285
- public getProposerFromEpochCommittee(epochCommitteeInfo: EpochCommitteeInfo, slot: bigint): EthAddress | undefined {
332
+ public getProposerFromEpochCommittee(
333
+ epochCommitteeInfo: EpochCommitteeInfo,
334
+ slot: SlotNumber,
335
+ ): EthAddress | undefined {
286
336
  if (!epochCommitteeInfo.committee || epochCommitteeInfo.committee.length === 0) {
287
337
  return undefined;
288
338
  }
@@ -320,9 +370,9 @@ export class EpochCache implements EpochCacheInterface {
320
370
  const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
321
371
  if (validatorRefreshTime < this.dateProvider.now()) {
322
372
  const currentSet = await this.rollup.getAttesters();
323
- this.allValidators = new Set(currentSet);
373
+ this.allValidators = new Set(currentSet.map(v => v.toString()));
324
374
  this.lastValidatorRefresh = this.dateProvider.now();
325
375
  }
326
- return Array.from(this.allValidators.keys().map(v => EthAddress.fromString(v)));
376
+ return Array.from(this.allValidators.keys()).map(v => EthAddress.fromString(v));
327
377
  }
328
378
  }
@@ -0,0 +1 @@
1
+ export * from './test_epoch_cache.js';
@@ -0,0 +1,169 @@
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
+ };
17
+
18
+ /**
19
+ * A test implementation of EpochCacheInterface that allows manual configuration
20
+ * of committee, proposer, slot, and escape hatch state for use in tests.
21
+ *
22
+ * Unlike the real EpochCache, this class doesn't require any RPC connections
23
+ * or mock setup. Simply use the setter methods to configure the test state.
24
+ */
25
+ export class TestEpochCache implements EpochCacheInterface {
26
+ private committee: EthAddress[] = [];
27
+ private proposerAddress: EthAddress | undefined;
28
+ private currentSlot: SlotNumber = SlotNumber(0);
29
+ private escapeHatchOpen: boolean = false;
30
+ private seed: bigint = 0n;
31
+ private registeredValidators: EthAddress[] = [];
32
+ private l1Constants: L1RollupConstants;
33
+
34
+ constructor(l1Constants: Partial<L1RollupConstants> = {}) {
35
+ this.l1Constants = { ...DEFAULT_L1_CONSTANTS, ...l1Constants };
36
+ }
37
+
38
+ /**
39
+ * Sets the committee members. Used in validation and attestation flows.
40
+ * @param committee - Array of committee member addresses.
41
+ */
42
+ setCommittee(committee: EthAddress[]): this {
43
+ this.committee = committee;
44
+ return this;
45
+ }
46
+
47
+ /**
48
+ * Sets the proposer address returned by getProposerAttesterAddressInSlot.
49
+ * @param proposer - The address of the current proposer.
50
+ */
51
+ setProposer(proposer: EthAddress | undefined): this {
52
+ this.proposerAddress = proposer;
53
+ return this;
54
+ }
55
+
56
+ /**
57
+ * Sets the current slot number.
58
+ * @param slot - The slot number to set.
59
+ */
60
+ setCurrentSlot(slot: SlotNumber): this {
61
+ this.currentSlot = slot;
62
+ return this;
63
+ }
64
+
65
+ /**
66
+ * Sets whether the escape hatch is open.
67
+ * @param open - True if escape hatch should be open.
68
+ */
69
+ setEscapeHatchOpen(open: boolean): this {
70
+ this.escapeHatchOpen = open;
71
+ return this;
72
+ }
73
+
74
+ /**
75
+ * Sets the randomness seed used for proposer selection.
76
+ * @param seed - The seed value.
77
+ */
78
+ setSeed(seed: bigint): this {
79
+ this.seed = seed;
80
+ return this;
81
+ }
82
+
83
+ /**
84
+ * Sets the list of registered validators (all validators, not just committee).
85
+ * @param validators - Array of validator addresses.
86
+ */
87
+ setRegisteredValidators(validators: EthAddress[]): this {
88
+ this.registeredValidators = validators;
89
+ return this;
90
+ }
91
+
92
+ /**
93
+ * Sets the L1 constants used for epoch/slot calculations.
94
+ * @param constants - Partial constants to override defaults.
95
+ */
96
+ setL1Constants(constants: Partial<L1RollupConstants>): this {
97
+ this.l1Constants = { ...this.l1Constants, ...constants };
98
+ return this;
99
+ }
100
+
101
+ getL1Constants(): L1RollupConstants {
102
+ return this.l1Constants;
103
+ }
104
+
105
+ getCommittee(_slot?: SlotTag): Promise<EpochCommitteeInfo> {
106
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
107
+ return Promise.resolve({
108
+ committee: this.committee,
109
+ epoch,
110
+ seed: this.seed,
111
+ isEscapeHatchOpen: this.escapeHatchOpen,
112
+ });
113
+ }
114
+
115
+ getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
116
+ const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
117
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
118
+ return { epoch, slot: this.currentSlot, ts, nowMs: ts * 1000n };
119
+ }
120
+
121
+ getEpochAndSlotInNextL1Slot(): EpochAndSlot & { now: bigint } {
122
+ const now = getTimestampRangeForEpoch(getEpochAtSlot(this.currentSlot, this.l1Constants), this.l1Constants)[0];
123
+ const nextSlotTs = now + BigInt(this.l1Constants.ethereumSlotDuration);
124
+ const nextSlot = getSlotAtTimestamp(nextSlotTs, this.l1Constants);
125
+ const epoch = getEpochAtSlot(nextSlot, this.l1Constants);
126
+ const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
127
+ return { epoch, slot: nextSlot, ts, now };
128
+ }
129
+
130
+ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}` {
131
+ // Simple encoding for testing purposes
132
+ return `0x${epoch.toString(16).padStart(64, '0')}${slot.toString(16).padStart(64, '0')}${seed.toString(16).padStart(64, '0')}`;
133
+ }
134
+
135
+ computeProposerIndex(slot: SlotNumber, _epoch: EpochNumber, _seed: bigint, size: bigint): bigint {
136
+ if (size === 0n) {
137
+ return 0n;
138
+ }
139
+ return BigInt(slot) % size;
140
+ }
141
+
142
+ getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
143
+ return {
144
+ currentSlot: this.currentSlot,
145
+ nextSlot: SlotNumber(this.currentSlot + 1),
146
+ };
147
+ }
148
+
149
+ getProposerAttesterAddressInSlot(_slot: SlotNumber): Promise<EthAddress | undefined> {
150
+ return Promise.resolve(this.proposerAddress);
151
+ }
152
+
153
+ getRegisteredValidators(): Promise<EthAddress[]> {
154
+ return Promise.resolve(this.registeredValidators);
155
+ }
156
+
157
+ isInCommittee(_slot: SlotTag, validator: EthAddress): Promise<boolean> {
158
+ return Promise.resolve(this.committee.some(v => v.equals(validator)));
159
+ }
160
+
161
+ filterInCommittee(_slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]> {
162
+ const committeeSet = new Set(this.committee.map(v => v.toString()));
163
+ return Promise.resolve(validators.filter(v => committeeSet.has(v.toString())));
164
+ }
165
+
166
+ isEscapeHatchOpenAtSlot(_slot?: SlotTag): Promise<boolean> {
167
+ return Promise.resolve(this.escapeHatchOpen);
168
+ }
169
+ }