@aztec/epoch-cache 3.0.3 → 3.9.9-nightly.20260312
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/epoch_cache.d.ts +28 -17
- package/dest/epoch_cache.d.ts.map +1 -1
- package/dest/epoch_cache.js +45 -22
- package/dest/test/index.d.ts +2 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +1 -0
- package/dest/test/test_epoch_cache.d.ts +76 -0
- package/dest/test/test_epoch_cache.d.ts.map +1 -0
- package/dest/test/test_epoch_cache.js +152 -0
- package/package.json +7 -7
- package/src/epoch_cache.ts +56 -30
- package/src/test/index.ts +1 -0
- package/src/test/test_epoch_cache.ts +171 -0
package/dest/epoch_cache.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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":"AACA,OAAO,EAAoB,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,KAAK,iBAAiB,EAOvB,MAAM,6BAA6B,CAAC;AAIrC,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAEhF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"epoch_cache.d.ts","sourceRoot":"","sources":["../src/epoch_cache.ts"],"names":[],"mappings":"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"}
|
package/dest/epoch_cache.js
CHANGED
|
@@ -50,19 +50,23 @@ 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, rollupManaLimit] = 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(),
|
|
69
|
+
rollup.getManaLimit()
|
|
66
70
|
]);
|
|
67
71
|
const l1RollupConstants = {
|
|
68
72
|
l1StartBlock,
|
|
@@ -72,7 +76,9 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
72
76
|
epochDuration: Number(epochDuration),
|
|
73
77
|
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
74
78
|
lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
|
|
75
|
-
lagInEpochsForRandao: Number(lagInEpochsForRandao)
|
|
79
|
+
lagInEpochsForRandao: Number(lagInEpochsForRandao),
|
|
80
|
+
targetCommitteeSize: Number(targetCommitteeSize),
|
|
81
|
+
rollupManaLimit: Number(rollupManaLimit)
|
|
76
82
|
};
|
|
77
83
|
return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
|
|
78
84
|
}
|
|
@@ -80,10 +86,11 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
80
86
|
return this.l1constants;
|
|
81
87
|
}
|
|
82
88
|
getEpochAndSlotNow() {
|
|
83
|
-
const
|
|
89
|
+
const nowMs = BigInt(this.dateProvider.now());
|
|
90
|
+
const nowSeconds = nowMs / 1000n;
|
|
84
91
|
return {
|
|
85
|
-
...this.getEpochAndSlotAtTimestamp(
|
|
86
|
-
|
|
92
|
+
...this.getEpochAndSlotAtTimestamp(nowSeconds),
|
|
93
|
+
nowMs
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
nowInSeconds() {
|
|
@@ -119,6 +126,28 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
119
126
|
return this.getCommittee(startSlot);
|
|
120
127
|
}
|
|
121
128
|
/**
|
|
129
|
+
* Returns whether the escape hatch is open for the given epoch.
|
|
130
|
+
*
|
|
131
|
+
* Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
|
|
132
|
+
* the epoch committee info (which includes the escape hatch flag) and return it.
|
|
133
|
+
*/ async isEscapeHatchOpen(epoch) {
|
|
134
|
+
const cached = this.cache.get(epoch);
|
|
135
|
+
if (cached) {
|
|
136
|
+
return cached.isEscapeHatchOpen;
|
|
137
|
+
}
|
|
138
|
+
const info = await this.getCommitteeForEpoch(epoch);
|
|
139
|
+
return info.isEscapeHatchOpen;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Returns whether the escape hatch is open for the epoch containing the given slot.
|
|
143
|
+
*
|
|
144
|
+
* This is a lightweight helper intended for callers that already have a slot number and only
|
|
145
|
+
* need the escape hatch flag (without pulling full committee info).
|
|
146
|
+
*/ async isEscapeHatchOpenAtSlot(slot = 'now') {
|
|
147
|
+
const epoch = slot === 'now' ? this.getEpochAndSlotNow().epoch : slot === 'next' ? this.getEpochAndSlotInNextL1Slot().epoch : getEpochAtSlot(slot, this.l1constants);
|
|
148
|
+
return await this.isEscapeHatchOpen(epoch);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
122
151
|
* Get the current validator set
|
|
123
152
|
* @param nextSlot - If true, get the validator set for the next slot.
|
|
124
153
|
* @returns The current validator set.
|
|
@@ -151,23 +180,24 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
151
180
|
}
|
|
152
181
|
async computeCommittee(when) {
|
|
153
182
|
const { ts, epoch } = when;
|
|
154
|
-
const [
|
|
183
|
+
const [committee, seedBuffer, l1Timestamp, isEscapeHatchOpen] = await Promise.all([
|
|
155
184
|
this.rollup.getCommitteeAt(ts),
|
|
156
185
|
this.rollup.getSampleSeedAt(ts),
|
|
157
186
|
this.rollup.client.getBlock({
|
|
158
187
|
includeTransactions: false
|
|
159
|
-
}).then((b)=>b.timestamp)
|
|
188
|
+
}).then((b)=>b.timestamp),
|
|
189
|
+
this.rollup.isEscapeHatchOpen(epoch)
|
|
160
190
|
]);
|
|
161
191
|
const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
|
|
162
192
|
const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
|
|
163
193
|
if (ts - sub > l1Timestamp) {
|
|
164
194
|
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
195
|
}
|
|
166
|
-
const committee = committeeHex?.map((v)=>EthAddress.fromString(v));
|
|
167
196
|
return {
|
|
168
197
|
committee,
|
|
169
|
-
seed,
|
|
170
|
-
epoch
|
|
198
|
+
seed: seedBuffer.toBigInt(),
|
|
199
|
+
epoch,
|
|
200
|
+
isEscapeHatchOpen
|
|
171
201
|
};
|
|
172
202
|
}
|
|
173
203
|
/**
|
|
@@ -199,17 +229,10 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
199
229
|
}
|
|
200
230
|
return BigInt(keccak256(this.getProposerIndexEncoding(epoch, slot, seed))) % size;
|
|
201
231
|
}
|
|
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() {
|
|
232
|
+
/** Returns the current and next L2 slot numbers. */ getCurrentAndNextSlot() {
|
|
208
233
|
const current = this.getEpochAndSlotNow();
|
|
209
234
|
const next = this.getEpochAndSlotInNextL1Slot();
|
|
210
235
|
return {
|
|
211
|
-
currentProposer: await this.getProposerAttesterAddressAt(current),
|
|
212
|
-
nextProposer: await this.getProposerAttesterAddressAt(next),
|
|
213
236
|
currentSlot: current.slot,
|
|
214
237
|
nextSlot: next.slot
|
|
215
238
|
};
|
|
@@ -273,9 +296,9 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
273
296
|
const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
|
|
274
297
|
if (validatorRefreshTime < this.dateProvider.now()) {
|
|
275
298
|
const currentSet = await this.rollup.getAttesters();
|
|
276
|
-
this.allValidators = new Set(currentSet);
|
|
299
|
+
this.allValidators = new Set(currentSet.map((v)=>v.toString()));
|
|
277
300
|
this.lastValidatorRefresh = this.dateProvider.now();
|
|
278
301
|
}
|
|
279
|
-
return Array.from(this.allValidators.keys().map((v)=>EthAddress.fromString(v))
|
|
302
|
+
return Array.from(this.allValidators.keys()).map((v)=>EthAddress.fromString(v));
|
|
280
303
|
}
|
|
281
304
|
}
|
|
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdF9lcG9jaF9jYWNoZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3QvdGVzdF9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUMzRCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBR3JFLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxrQkFBa0IsRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQWN4Rzs7Ozs7O0dBTUc7QUFDSCxxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBQ3hELE9BQU8sQ0FBQyxTQUFTLENBQW9CO0lBQ3JDLE9BQU8sQ0FBQyxlQUFlLENBQXlCO0lBQ2hELE9BQU8sQ0FBQyxXQUFXLENBQTZCO0lBQ2hELE9BQU8sQ0FBQyxlQUFlLENBQWtCO0lBQ3pDLE9BQU8sQ0FBQyxJQUFJLENBQWM7SUFDMUIsT0FBTyxDQUFDLG9CQUFvQixDQUFvQjtJQUNoRCxPQUFPLENBQUMsV0FBVyxDQUFvQjtJQUV2QyxZQUFZLFdBQVcsR0FBRSxPQUFPLENBQUMsaUJBQWlCLENBQU0sRUFFdkQ7SUFFRDs7O09BR0c7SUFDSCxZQUFZLENBQUMsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FHMUM7SUFFRDs7O09BR0c7SUFDSCxXQUFXLENBQUMsUUFBUSxFQUFFLFVBQVUsR0FBRyxTQUFTLEdBQUcsSUFBSSxDQUdsRDtJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxJQUFJLEVBQUUsVUFBVSxHQUFHLElBQUksQ0FHckM7SUFFRDs7O09BR0c7SUFDSCxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsT0FBTyxHQUFHLElBQUksQ0FHdEM7SUFFRDs7O09BR0c7SUFDSCxPQUFPLENBQUMsSUFBSSxFQUFFLE1BQU0sR0FBRyxJQUFJLENBRzFCO0lBRUQ7OztPQUdHO0lBQ0gsdUJBQXVCLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FHdEQ7SUFFRDs7O09BR0c7SUFDSCxjQUFjLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLElBQUksQ0FHMUQ7SUFFRCxjQUFjLElBQUksaUJBQWlCLENBRWxDO0lBRUQsWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FRekQ7SUFFRCxrQkFBa0IsSUFBSSxZQUFZLEdBQUc7UUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FJckQ7SUFFRCwyQkFBMkIsSUFBSSxZQUFZLEdBQUc7UUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FPNUQ7SUFFRCx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxLQUFLLE1BQU0sRUFBRSxDQUcxRjtJQUVELG9CQUFvQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsTUFBTSxDQUsvRjtJQUVELHFCQUFxQixJQUFJO1FBQUUsV0FBVyxFQUFFLFVBQVUsQ0FBQztRQUFDLFFBQVEsRUFBRSxVQUFVLENBQUE7S0FBRSxDQUt6RTtJQUVELGdDQUFnQyxDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUMsQ0FFbkY7SUFFRCx1QkFBdUIsSUFBSSxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FFL0M7SUFFRCxhQUFhLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FFckU7SUFFRCxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsR0FBRyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FHakY7SUFFRCx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUV6RDtDQUNGIn0=
|
|
@@ -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;AAcxG;;;;;;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,152 @@
|
|
|
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
|
+
rollupManaLimit: Number.MAX_SAFE_INTEGER
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* A test implementation of EpochCacheInterface that allows manual configuration
|
|
15
|
+
* of committee, proposer, slot, and escape hatch state for use in tests.
|
|
16
|
+
*
|
|
17
|
+
* Unlike the real EpochCache, this class doesn't require any RPC connections
|
|
18
|
+
* or mock setup. Simply use the setter methods to configure the test state.
|
|
19
|
+
*/ export class TestEpochCache {
|
|
20
|
+
committee = [];
|
|
21
|
+
proposerAddress;
|
|
22
|
+
currentSlot = SlotNumber(0);
|
|
23
|
+
escapeHatchOpen = false;
|
|
24
|
+
seed = 0n;
|
|
25
|
+
registeredValidators = [];
|
|
26
|
+
l1Constants;
|
|
27
|
+
constructor(l1Constants = {}){
|
|
28
|
+
this.l1Constants = {
|
|
29
|
+
...DEFAULT_L1_CONSTANTS,
|
|
30
|
+
...l1Constants
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Sets the committee members. Used in validation and attestation flows.
|
|
35
|
+
* @param committee - Array of committee member addresses.
|
|
36
|
+
*/ setCommittee(committee) {
|
|
37
|
+
this.committee = committee;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sets the proposer address returned by getProposerAttesterAddressInSlot.
|
|
42
|
+
* @param proposer - The address of the current proposer.
|
|
43
|
+
*/ setProposer(proposer) {
|
|
44
|
+
this.proposerAddress = proposer;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sets the current slot number.
|
|
49
|
+
* @param slot - The slot number to set.
|
|
50
|
+
*/ setCurrentSlot(slot) {
|
|
51
|
+
this.currentSlot = slot;
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sets whether the escape hatch is open.
|
|
56
|
+
* @param open - True if escape hatch should be open.
|
|
57
|
+
*/ setEscapeHatchOpen(open) {
|
|
58
|
+
this.escapeHatchOpen = open;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Sets the randomness seed used for proposer selection.
|
|
63
|
+
* @param seed - The seed value.
|
|
64
|
+
*/ setSeed(seed) {
|
|
65
|
+
this.seed = seed;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Sets the list of registered validators (all validators, not just committee).
|
|
70
|
+
* @param validators - Array of validator addresses.
|
|
71
|
+
*/ setRegisteredValidators(validators) {
|
|
72
|
+
this.registeredValidators = validators;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Sets the L1 constants used for epoch/slot calculations.
|
|
77
|
+
* @param constants - Partial constants to override defaults.
|
|
78
|
+
*/ setL1Constants(constants) {
|
|
79
|
+
this.l1Constants = {
|
|
80
|
+
...this.l1Constants,
|
|
81
|
+
...constants
|
|
82
|
+
};
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
getL1Constants() {
|
|
86
|
+
return this.l1Constants;
|
|
87
|
+
}
|
|
88
|
+
getCommittee(_slot) {
|
|
89
|
+
const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
|
|
90
|
+
return Promise.resolve({
|
|
91
|
+
committee: this.committee,
|
|
92
|
+
epoch,
|
|
93
|
+
seed: this.seed,
|
|
94
|
+
isEscapeHatchOpen: this.escapeHatchOpen
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
getEpochAndSlotNow() {
|
|
98
|
+
const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
|
|
99
|
+
const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
|
|
100
|
+
return {
|
|
101
|
+
epoch,
|
|
102
|
+
slot: this.currentSlot,
|
|
103
|
+
ts,
|
|
104
|
+
nowMs: ts * 1000n
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
getEpochAndSlotInNextL1Slot() {
|
|
108
|
+
const now = getTimestampRangeForEpoch(getEpochAtSlot(this.currentSlot, this.l1Constants), this.l1Constants)[0];
|
|
109
|
+
const nextSlotTs = now + BigInt(this.l1Constants.ethereumSlotDuration);
|
|
110
|
+
const nextSlot = getSlotAtTimestamp(nextSlotTs, this.l1Constants);
|
|
111
|
+
const epoch = getEpochAtSlot(nextSlot, this.l1Constants);
|
|
112
|
+
const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
|
|
113
|
+
return {
|
|
114
|
+
epoch,
|
|
115
|
+
slot: nextSlot,
|
|
116
|
+
ts,
|
|
117
|
+
now
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
getProposerIndexEncoding(epoch, slot, seed) {
|
|
121
|
+
// Simple encoding for testing purposes
|
|
122
|
+
return `0x${epoch.toString(16).padStart(64, '0')}${slot.toString(16).padStart(64, '0')}${seed.toString(16).padStart(64, '0')}`;
|
|
123
|
+
}
|
|
124
|
+
computeProposerIndex(slot, _epoch, _seed, size) {
|
|
125
|
+
if (size === 0n) {
|
|
126
|
+
return 0n;
|
|
127
|
+
}
|
|
128
|
+
return BigInt(slot) % size;
|
|
129
|
+
}
|
|
130
|
+
getCurrentAndNextSlot() {
|
|
131
|
+
return {
|
|
132
|
+
currentSlot: this.currentSlot,
|
|
133
|
+
nextSlot: SlotNumber(this.currentSlot + 1)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
getProposerAttesterAddressInSlot(_slot) {
|
|
137
|
+
return Promise.resolve(this.proposerAddress);
|
|
138
|
+
}
|
|
139
|
+
getRegisteredValidators() {
|
|
140
|
+
return Promise.resolve(this.registeredValidators);
|
|
141
|
+
}
|
|
142
|
+
isInCommittee(_slot, validator) {
|
|
143
|
+
return Promise.resolve(this.committee.some((v)=>v.equals(validator)));
|
|
144
|
+
}
|
|
145
|
+
filterInCommittee(_slot, validators) {
|
|
146
|
+
const committeeSet = new Set(this.committee.map((v)=>v.toString()));
|
|
147
|
+
return Promise.resolve(validators.filter((v)=>committeeSet.has(v.toString())));
|
|
148
|
+
}
|
|
149
|
+
isEscapeHatchOpenAtSlot(_slot) {
|
|
150
|
+
return Promise.resolve(this.escapeHatchOpen);
|
|
151
|
+
}
|
|
152
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/epoch-cache",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.9-nightly.20260312",
|
|
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 \"
|
|
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.
|
|
30
|
-
"@aztec/foundation": "3.
|
|
31
|
-
"@aztec/l1-artifacts": "3.
|
|
32
|
-
"@aztec/stdlib": "3.
|
|
29
|
+
"@aztec/ethereum": "3.9.9-nightly.20260312",
|
|
30
|
+
"@aztec/foundation": "3.9.9-nightly.20260312",
|
|
31
|
+
"@aztec/l1-artifacts": "3.9.9-nightly.20260312",
|
|
32
|
+
"@aztec/stdlib": "3.9.9-nightly.20260312",
|
|
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.
|
|
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"
|
package/src/epoch_cache.ts
CHANGED
|
@@ -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
|
-
|
|
42
|
-
|
|
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,8 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
108
107
|
epochDuration,
|
|
109
108
|
lagInEpochsForValidatorSet,
|
|
110
109
|
lagInEpochsForRandao,
|
|
110
|
+
targetCommitteeSize,
|
|
111
|
+
rollupManaLimit,
|
|
111
112
|
] = await Promise.all([
|
|
112
113
|
rollup.getL1StartBlock(),
|
|
113
114
|
rollup.getL1GenesisTime(),
|
|
@@ -116,6 +117,8 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
116
117
|
rollup.getEpochDuration(),
|
|
117
118
|
rollup.getLagInEpochsForValidatorSet(),
|
|
118
119
|
rollup.getLagInEpochsForRandao(),
|
|
120
|
+
rollup.getTargetCommitteeSize(),
|
|
121
|
+
rollup.getManaLimit(),
|
|
119
122
|
] as const);
|
|
120
123
|
|
|
121
124
|
const l1RollupConstants = {
|
|
@@ -127,6 +130,8 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
127
130
|
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
128
131
|
lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
|
|
129
132
|
lagInEpochsForRandao: Number(lagInEpochsForRandao),
|
|
133
|
+
targetCommitteeSize: Number(targetCommitteeSize),
|
|
134
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
130
135
|
};
|
|
131
136
|
|
|
132
137
|
return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
|
|
@@ -136,9 +141,10 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
136
141
|
return this.l1constants;
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
public getEpochAndSlotNow(): EpochAndSlot & {
|
|
140
|
-
const
|
|
141
|
-
|
|
144
|
+
public getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
|
|
145
|
+
const nowMs = BigInt(this.dateProvider.now());
|
|
146
|
+
const nowSeconds = nowMs / 1000n;
|
|
147
|
+
return { ...this.getEpochAndSlotAtTimestamp(nowSeconds), nowMs };
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
public nowInSeconds(): bigint {
|
|
@@ -171,6 +177,38 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
171
177
|
return this.getCommittee(startSlot);
|
|
172
178
|
}
|
|
173
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Returns whether the escape hatch is open for the given epoch.
|
|
182
|
+
*
|
|
183
|
+
* Uses the already-cached EpochCommitteeInfo when available. If not cached, it will fetch
|
|
184
|
+
* the epoch committee info (which includes the escape hatch flag) and return it.
|
|
185
|
+
*/
|
|
186
|
+
public async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
|
|
187
|
+
const cached = this.cache.get(epoch);
|
|
188
|
+
if (cached) {
|
|
189
|
+
return cached.isEscapeHatchOpen;
|
|
190
|
+
}
|
|
191
|
+
const info = await this.getCommitteeForEpoch(epoch);
|
|
192
|
+
return info.isEscapeHatchOpen;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Returns whether the escape hatch is open for the epoch containing the given slot.
|
|
197
|
+
*
|
|
198
|
+
* This is a lightweight helper intended for callers that already have a slot number and only
|
|
199
|
+
* need the escape hatch flag (without pulling full committee info).
|
|
200
|
+
*/
|
|
201
|
+
public async isEscapeHatchOpenAtSlot(slot: SlotTag = 'now'): Promise<boolean> {
|
|
202
|
+
const epoch =
|
|
203
|
+
slot === 'now'
|
|
204
|
+
? this.getEpochAndSlotNow().epoch
|
|
205
|
+
: slot === 'next'
|
|
206
|
+
? this.getEpochAndSlotInNextL1Slot().epoch
|
|
207
|
+
: getEpochAtSlot(slot, this.l1constants);
|
|
208
|
+
|
|
209
|
+
return await this.isEscapeHatchOpen(epoch);
|
|
210
|
+
}
|
|
211
|
+
|
|
174
212
|
/**
|
|
175
213
|
* Get the current validator set
|
|
176
214
|
* @param nextSlot - If true, get the validator set for the next slot.
|
|
@@ -210,10 +248,11 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
210
248
|
|
|
211
249
|
private async computeCommittee(when: { epoch: EpochNumber; ts: bigint }): Promise<EpochCommitteeInfo> {
|
|
212
250
|
const { ts, epoch } = when;
|
|
213
|
-
const [
|
|
251
|
+
const [committee, seedBuffer, l1Timestamp, isEscapeHatchOpen] = await Promise.all([
|
|
214
252
|
this.rollup.getCommitteeAt(ts),
|
|
215
253
|
this.rollup.getSampleSeedAt(ts),
|
|
216
254
|
this.rollup.client.getBlock({ includeTransactions: false }).then(b => b.timestamp),
|
|
255
|
+
this.rollup.isEscapeHatchOpen(epoch),
|
|
217
256
|
]);
|
|
218
257
|
const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
|
|
219
258
|
const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
|
|
@@ -222,8 +261,7 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
222
261
|
`Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`,
|
|
223
262
|
);
|
|
224
263
|
}
|
|
225
|
-
|
|
226
|
-
return { committee, seed, epoch };
|
|
264
|
+
return { committee, seed: seedBuffer.toBigInt(), epoch, isEscapeHatchOpen };
|
|
227
265
|
}
|
|
228
266
|
|
|
229
267
|
/**
|
|
@@ -248,24 +286,12 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
248
286
|
return BigInt(keccak256(this.getProposerIndexEncoding(epoch, slot, seed))) % size;
|
|
249
287
|
}
|
|
250
288
|
|
|
251
|
-
/**
|
|
252
|
-
|
|
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
|
-
}> {
|
|
289
|
+
/** Returns the current and next L2 slot numbers. */
|
|
290
|
+
public getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
|
|
263
291
|
const current = this.getEpochAndSlotNow();
|
|
264
292
|
const next = this.getEpochAndSlotInNextL1Slot();
|
|
265
293
|
|
|
266
294
|
return {
|
|
267
|
-
currentProposer: await this.getProposerAttesterAddressAt(current),
|
|
268
|
-
nextProposer: await this.getProposerAttesterAddressAt(next),
|
|
269
295
|
currentSlot: current.slot,
|
|
270
296
|
nextSlot: next.slot,
|
|
271
297
|
};
|
|
@@ -351,9 +377,9 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
351
377
|
const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
|
|
352
378
|
if (validatorRefreshTime < this.dateProvider.now()) {
|
|
353
379
|
const currentSet = await this.rollup.getAttesters();
|
|
354
|
-
this.allValidators = new Set(currentSet);
|
|
380
|
+
this.allValidators = new Set(currentSet.map(v => v.toString()));
|
|
355
381
|
this.lastValidatorRefresh = this.dateProvider.now();
|
|
356
382
|
}
|
|
357
|
-
return Array.from(this.allValidators.keys().map(v => EthAddress.fromString(v))
|
|
383
|
+
return Array.from(this.allValidators.keys()).map(v => EthAddress.fromString(v));
|
|
358
384
|
}
|
|
359
385
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './test_epoch_cache.js';
|
|
@@ -0,0 +1,171 @@
|
|
|
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
|
+
rollupManaLimit: Number.MAX_SAFE_INTEGER,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A test implementation of EpochCacheInterface that allows manual configuration
|
|
22
|
+
* of committee, proposer, slot, and escape hatch state for use in tests.
|
|
23
|
+
*
|
|
24
|
+
* Unlike the real EpochCache, this class doesn't require any RPC connections
|
|
25
|
+
* or mock setup. Simply use the setter methods to configure the test state.
|
|
26
|
+
*/
|
|
27
|
+
export class TestEpochCache implements EpochCacheInterface {
|
|
28
|
+
private committee: EthAddress[] = [];
|
|
29
|
+
private proposerAddress: EthAddress | undefined;
|
|
30
|
+
private currentSlot: SlotNumber = SlotNumber(0);
|
|
31
|
+
private escapeHatchOpen: boolean = false;
|
|
32
|
+
private seed: bigint = 0n;
|
|
33
|
+
private registeredValidators: EthAddress[] = [];
|
|
34
|
+
private l1Constants: L1RollupConstants;
|
|
35
|
+
|
|
36
|
+
constructor(l1Constants: Partial<L1RollupConstants> = {}) {
|
|
37
|
+
this.l1Constants = { ...DEFAULT_L1_CONSTANTS, ...l1Constants };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets the committee members. Used in validation and attestation flows.
|
|
42
|
+
* @param committee - Array of committee member addresses.
|
|
43
|
+
*/
|
|
44
|
+
setCommittee(committee: EthAddress[]): this {
|
|
45
|
+
this.committee = committee;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Sets the proposer address returned by getProposerAttesterAddressInSlot.
|
|
51
|
+
* @param proposer - The address of the current proposer.
|
|
52
|
+
*/
|
|
53
|
+
setProposer(proposer: EthAddress | undefined): this {
|
|
54
|
+
this.proposerAddress = proposer;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Sets the current slot number.
|
|
60
|
+
* @param slot - The slot number to set.
|
|
61
|
+
*/
|
|
62
|
+
setCurrentSlot(slot: SlotNumber): this {
|
|
63
|
+
this.currentSlot = slot;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Sets whether the escape hatch is open.
|
|
69
|
+
* @param open - True if escape hatch should be open.
|
|
70
|
+
*/
|
|
71
|
+
setEscapeHatchOpen(open: boolean): this {
|
|
72
|
+
this.escapeHatchOpen = open;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sets the randomness seed used for proposer selection.
|
|
78
|
+
* @param seed - The seed value.
|
|
79
|
+
*/
|
|
80
|
+
setSeed(seed: bigint): this {
|
|
81
|
+
this.seed = seed;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Sets the list of registered validators (all validators, not just committee).
|
|
87
|
+
* @param validators - Array of validator addresses.
|
|
88
|
+
*/
|
|
89
|
+
setRegisteredValidators(validators: EthAddress[]): this {
|
|
90
|
+
this.registeredValidators = validators;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Sets the L1 constants used for epoch/slot calculations.
|
|
96
|
+
* @param constants - Partial constants to override defaults.
|
|
97
|
+
*/
|
|
98
|
+
setL1Constants(constants: Partial<L1RollupConstants>): this {
|
|
99
|
+
this.l1Constants = { ...this.l1Constants, ...constants };
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getL1Constants(): L1RollupConstants {
|
|
104
|
+
return this.l1Constants;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getCommittee(_slot?: SlotTag): Promise<EpochCommitteeInfo> {
|
|
108
|
+
const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
|
|
109
|
+
return Promise.resolve({
|
|
110
|
+
committee: this.committee,
|
|
111
|
+
epoch,
|
|
112
|
+
seed: this.seed,
|
|
113
|
+
isEscapeHatchOpen: this.escapeHatchOpen,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
|
|
118
|
+
const epoch = getEpochAtSlot(this.currentSlot, this.l1Constants);
|
|
119
|
+
const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
|
|
120
|
+
return { epoch, slot: this.currentSlot, ts, nowMs: ts * 1000n };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getEpochAndSlotInNextL1Slot(): EpochAndSlot & { now: bigint } {
|
|
124
|
+
const now = getTimestampRangeForEpoch(getEpochAtSlot(this.currentSlot, this.l1Constants), this.l1Constants)[0];
|
|
125
|
+
const nextSlotTs = now + BigInt(this.l1Constants.ethereumSlotDuration);
|
|
126
|
+
const nextSlot = getSlotAtTimestamp(nextSlotTs, this.l1Constants);
|
|
127
|
+
const epoch = getEpochAtSlot(nextSlot, this.l1Constants);
|
|
128
|
+
const ts = getTimestampRangeForEpoch(epoch, this.l1Constants)[0];
|
|
129
|
+
return { epoch, slot: nextSlot, ts, now };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}` {
|
|
133
|
+
// Simple encoding for testing purposes
|
|
134
|
+
return `0x${epoch.toString(16).padStart(64, '0')}${slot.toString(16).padStart(64, '0')}${seed.toString(16).padStart(64, '0')}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
computeProposerIndex(slot: SlotNumber, _epoch: EpochNumber, _seed: bigint, size: bigint): bigint {
|
|
138
|
+
if (size === 0n) {
|
|
139
|
+
return 0n;
|
|
140
|
+
}
|
|
141
|
+
return BigInt(slot) % size;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
|
|
145
|
+
return {
|
|
146
|
+
currentSlot: this.currentSlot,
|
|
147
|
+
nextSlot: SlotNumber(this.currentSlot + 1),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getProposerAttesterAddressInSlot(_slot: SlotNumber): Promise<EthAddress | undefined> {
|
|
152
|
+
return Promise.resolve(this.proposerAddress);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
getRegisteredValidators(): Promise<EthAddress[]> {
|
|
156
|
+
return Promise.resolve(this.registeredValidators);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
isInCommittee(_slot: SlotTag, validator: EthAddress): Promise<boolean> {
|
|
160
|
+
return Promise.resolve(this.committee.some(v => v.equals(validator)));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
filterInCommittee(_slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]> {
|
|
164
|
+
const committeeSet = new Set(this.committee.map(v => v.toString()));
|
|
165
|
+
return Promise.resolve(validators.filter(v => committeeSet.has(v.toString())));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
isEscapeHatchOpenAtSlot(_slot?: SlotTag): Promise<boolean> {
|
|
169
|
+
return Promise.resolve(this.escapeHatchOpen);
|
|
170
|
+
}
|
|
171
|
+
}
|