@aztec/epoch-cache 3.0.0-canary.a9708bd → 3.0.0-devnet.20251212
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 +4 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -1
- package/dest/epoch_cache.d.ts +31 -24
- package/dest/epoch_cache.d.ts.map +1 -1
- package/dest/epoch_cache.js +53 -34
- package/dest/index.d.ts +1 -1
- package/package.json +10 -9
- package/src/config.ts +3 -12
- package/src/epoch_cache.ts +93 -63
package/dest/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type L1ContractsConfig
|
|
2
|
-
|
|
1
|
+
import { type L1ContractsConfig } from '@aztec/ethereum/config';
|
|
2
|
+
import { type L1ReaderConfig } from '@aztec/ethereum/l1-reader';
|
|
3
|
+
export type EpochCacheConfig = Pick<L1ReaderConfig & L1ContractsConfig, 'l1RpcUrls' | 'l1ChainId' | 'viemPollingIntervalMS' | 'ethereumSlotDuration'>;
|
|
3
4
|
export declare function getEpochCacheConfigEnvVars(): EpochCacheConfig;
|
|
4
|
-
//# sourceMappingURL=
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGlCQUFpQixFQUErQixNQUFNLHdCQUF3QixDQUFDO0FBQzdGLE9BQU8sRUFBRSxLQUFLLGNBQWMsRUFBNEIsTUFBTSwyQkFBMkIsQ0FBQztBQUUxRixNQUFNLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUNqQyxjQUFjLEdBQUcsaUJBQWlCLEVBQ2xDLFdBQVcsR0FBRyxXQUFXLEdBQUcsdUJBQXVCLEdBQUcsc0JBQXNCLENBQzdFLENBQUM7QUFFRix3QkFBZ0IsMEJBQTBCLElBQUksZ0JBQWdCLENBRTdEIn0=
|
package/dest/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
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
|
|
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(),
|
package/dest/epoch_cache.d.ts
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
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:
|
|
8
|
-
slot:
|
|
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:
|
|
15
|
+
epoch: EpochNumber;
|
|
15
16
|
};
|
|
16
|
-
export type SlotTag = 'now' | 'next' |
|
|
17
|
+
export type SlotTag = 'now' | 'next' | SlotNumber;
|
|
17
18
|
export interface EpochCacheInterface {
|
|
18
19
|
getCommittee(slot: SlotTag | undefined): Promise<EpochCommitteeInfo>;
|
|
19
20
|
getEpochAndSlotNow(): EpochAndSlot;
|
|
20
21
|
getEpochAndSlotInNextL1Slot(): EpochAndSlot & {
|
|
21
22
|
now: bigint;
|
|
22
23
|
};
|
|
23
|
-
getProposerIndexEncoding(epoch:
|
|
24
|
-
computeProposerIndex(slot:
|
|
24
|
+
getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
|
|
25
|
+
computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
|
|
25
26
|
getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
26
27
|
currentProposer: EthAddress | undefined;
|
|
27
28
|
nextProposer: EthAddress | undefined;
|
|
28
|
-
currentSlot:
|
|
29
|
-
nextSlot:
|
|
29
|
+
currentSlot: SlotNumber;
|
|
30
|
+
nextSlot: SlotNumber;
|
|
30
31
|
}>;
|
|
31
32
|
getRegisteredValidators(): Promise<EthAddress[]>;
|
|
32
33
|
isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
|
|
@@ -45,16 +46,22 @@ export declare class EpochCache implements EpochCacheInterface {
|
|
|
45
46
|
private rollup;
|
|
46
47
|
private readonly l1constants;
|
|
47
48
|
private readonly dateProvider;
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
protected readonly config: {
|
|
50
|
+
cacheSize: number;
|
|
51
|
+
validatorRefreshIntervalSeconds: number;
|
|
52
|
+
};
|
|
53
|
+
protected cache: Map<EpochNumber, EpochCommitteeInfo>;
|
|
50
54
|
private allValidators;
|
|
51
55
|
private lastValidatorRefresh;
|
|
52
56
|
private readonly log;
|
|
53
|
-
constructor(rollup: RollupContract,
|
|
57
|
+
constructor(rollup: RollupContract, l1constants: L1RollupConstants & {
|
|
58
|
+
lagInEpochsForValidatorSet: number;
|
|
59
|
+
lagInEpochsForRandao: number;
|
|
60
|
+
}, dateProvider?: DateProvider, config?: {
|
|
54
61
|
cacheSize: number;
|
|
55
62
|
validatorRefreshIntervalSeconds: number;
|
|
56
63
|
});
|
|
57
|
-
static create(
|
|
64
|
+
static create(rollupOrAddress: EthAddress | RollupContract, config?: EpochCacheConfig, deps?: {
|
|
58
65
|
dateProvider?: DateProvider;
|
|
59
66
|
}): Promise<EpochCache>;
|
|
60
67
|
getL1Constants(): L1RollupConstants;
|
|
@@ -67,7 +74,7 @@ export declare class EpochCache implements EpochCacheInterface {
|
|
|
67
74
|
now: bigint;
|
|
68
75
|
};
|
|
69
76
|
private getEpochAndSlotAtTimestamp;
|
|
70
|
-
getCommitteeForEpoch(epoch:
|
|
77
|
+
getCommitteeForEpoch(epoch: EpochNumber): Promise<EpochCommitteeInfo>;
|
|
71
78
|
/**
|
|
72
79
|
* Get the current validator set
|
|
73
80
|
* @param nextSlot - If true, get the validator set for the next slot.
|
|
@@ -79,8 +86,8 @@ export declare class EpochCache implements EpochCacheInterface {
|
|
|
79
86
|
/**
|
|
80
87
|
* Get the ABI encoding of the proposer index - see ValidatorSelectionLib.sol computeProposerIndex
|
|
81
88
|
*/
|
|
82
|
-
getProposerIndexEncoding(epoch:
|
|
83
|
-
computeProposerIndex(slot:
|
|
89
|
+
getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
|
|
90
|
+
computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
|
|
84
91
|
/**
|
|
85
92
|
* Returns the current and next proposer's attester address
|
|
86
93
|
*
|
|
@@ -88,29 +95,29 @@ export declare class EpochCache implements EpochCacheInterface {
|
|
|
88
95
|
* which can be the next slot. If this is the case, then it will send proposals early.
|
|
89
96
|
*/
|
|
90
97
|
getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
91
|
-
currentSlot:
|
|
92
|
-
nextSlot:
|
|
98
|
+
currentSlot: SlotNumber;
|
|
99
|
+
nextSlot: SlotNumber;
|
|
93
100
|
currentProposer: EthAddress | undefined;
|
|
94
101
|
nextProposer: EthAddress | undefined;
|
|
95
102
|
}>;
|
|
96
103
|
/**
|
|
97
|
-
* Get the proposer attester address in the
|
|
104
|
+
* Get the proposer attester address in the given L2 slot
|
|
98
105
|
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
99
106
|
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
100
107
|
*/
|
|
101
|
-
|
|
108
|
+
getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined>;
|
|
102
109
|
/**
|
|
103
|
-
* Get the proposer attester address
|
|
104
|
-
* @param when - The epoch and slot to get the proposer attester address at
|
|
110
|
+
* Get the proposer attester address in the next slot
|
|
105
111
|
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
106
112
|
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
107
113
|
*/
|
|
114
|
+
getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined>;
|
|
108
115
|
private getProposerAttesterAddressAt;
|
|
109
|
-
getProposerFromEpochCommittee(epochCommitteeInfo: EpochCommitteeInfo, slot:
|
|
116
|
+
getProposerFromEpochCommittee(epochCommitteeInfo: EpochCommitteeInfo, slot: SlotNumber): EthAddress | undefined;
|
|
110
117
|
/** Check if a validator is in the given slot's committee */
|
|
111
118
|
isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
|
|
112
119
|
/** From the set of given addresses, return all that are on the committee for the given slot */
|
|
113
120
|
filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise<EthAddress[]>;
|
|
114
121
|
getRegisteredValidators(): Promise<EthAddress[]>;
|
|
115
122
|
}
|
|
116
|
-
//# sourceMappingURL=
|
|
123
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfY2FjaGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQW9CLGNBQWMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDMUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTNELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEVBQ0wsS0FBSyxpQkFBaUIsRUFPdkIsTUFBTSw2QkFBNkIsQ0FBQztBQUlyQyxPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFFaEYsTUFBTSxNQUFNLFlBQVksR0FBRztJQUN6QixLQUFLLEVBQUUsV0FBVyxDQUFDO0lBQ25CLElBQUksRUFBRSxVQUFVLENBQUM7SUFDakIsRUFBRSxFQUFFLE1BQU0sQ0FBQztDQUNaLENBQUM7QUFFRixNQUFNLE1BQU0sa0JBQWtCLEdBQUc7SUFDL0IsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLFNBQVMsQ0FBQztJQUNwQyxJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsS0FBSyxFQUFFLFdBQVcsQ0FBQztDQUNwQixDQUFDO0FBRUYsTUFBTSxNQUFNLE9BQU8sR0FBRyxLQUFLLEdBQUcsTUFBTSxHQUFHLFVBQVUsQ0FBQztBQUVsRCxNQUFNLFdBQVcsbUJBQW1CO0lBQ2xDLFlBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxHQUFHLFNBQVMsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUNyRSxrQkFBa0IsSUFBSSxZQUFZLENBQUM7SUFDbkMsMkJBQTJCLElBQUksWUFBWSxHQUFHO1FBQUUsR0FBRyxFQUFFLE1BQU0sQ0FBQTtLQUFFLENBQUM7SUFDOUQsd0JBQXdCLENBQUMs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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epoch_cache.d.ts","sourceRoot":"","sources":["../src/epoch_cache.ts"],"names":[],"mappings":"
|
|
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
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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,
|
|
27
|
+
constructor(rollup, l1constants, dateProvider = new DateProvider(), config = {
|
|
26
28
|
cacheSize: 12,
|
|
27
29
|
validatorRefreshIntervalSeconds: 60
|
|
28
30
|
}){
|
|
@@ -34,44 +36,45 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
34
36
|
this.allValidators = new Set();
|
|
35
37
|
this.lastValidatorRefresh = 0;
|
|
36
38
|
this.log = createLogger('epoch-cache');
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
committee: initialValidators,
|
|
40
|
-
seed: initialSampleSeed
|
|
41
|
-
});
|
|
42
|
-
this.log.debug(`Initialized EpochCache with ${initialValidators?.length ?? 'no'} validators`, {
|
|
43
|
-
l1constants,
|
|
44
|
-
initialValidators,
|
|
45
|
-
initialSampleSeed,
|
|
46
|
-
initialEpoch
|
|
39
|
+
this.log.debug(`Initialized EpochCache`, {
|
|
40
|
+
l1constants
|
|
47
41
|
});
|
|
48
42
|
}
|
|
49
|
-
static async create(
|
|
43
|
+
static async create(rollupOrAddress, config, deps = {}) {
|
|
50
44
|
config = config ?? getEpochCacheConfigEnvVars();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
// Load the rollup contract if we were given an address
|
|
46
|
+
let rollup;
|
|
47
|
+
if ('address' in rollupOrAddress) {
|
|
48
|
+
rollup = rollupOrAddress;
|
|
49
|
+
} else {
|
|
50
|
+
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
51
|
+
const publicClient = createPublicClient({
|
|
52
|
+
chain: chain.chainInfo,
|
|
53
|
+
transport: fallback(config.l1RpcUrls.map((url)=>http(url))),
|
|
54
|
+
pollingInterval: config.viemPollingIntervalMS
|
|
55
|
+
});
|
|
56
|
+
rollup = new RollupContract(publicClient, rollupOrAddress.toString());
|
|
57
|
+
}
|
|
58
|
+
const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration, lagInEpochsForValidatorSet, lagInEpochsForRandao] = await Promise.all([
|
|
59
59
|
rollup.getL1StartBlock(),
|
|
60
60
|
rollup.getL1GenesisTime(),
|
|
61
|
-
rollup.
|
|
62
|
-
rollup.
|
|
63
|
-
rollup.
|
|
64
|
-
rollup.
|
|
61
|
+
rollup.getProofSubmissionEpochs(),
|
|
62
|
+
rollup.getSlotDuration(),
|
|
63
|
+
rollup.getEpochDuration(),
|
|
64
|
+
rollup.getLagInEpochsForValidatorSet(),
|
|
65
|
+
rollup.getLagInEpochsForRandao()
|
|
65
66
|
]);
|
|
66
67
|
const l1RollupConstants = {
|
|
67
68
|
l1StartBlock,
|
|
68
69
|
l1GenesisTime,
|
|
69
70
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
70
|
-
slotDuration:
|
|
71
|
-
epochDuration:
|
|
72
|
-
ethereumSlotDuration: config.ethereumSlotDuration
|
|
71
|
+
slotDuration: Number(slotDuration),
|
|
72
|
+
epochDuration: Number(epochDuration),
|
|
73
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
74
|
+
lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
|
|
75
|
+
lagInEpochsForRandao: Number(lagInEpochsForRandao)
|
|
73
76
|
};
|
|
74
|
-
return new EpochCache(rollup,
|
|
77
|
+
return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
|
|
75
78
|
}
|
|
76
79
|
getL1Constants() {
|
|
77
80
|
return this.l1constants;
|
|
@@ -148,10 +151,18 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
148
151
|
}
|
|
149
152
|
async computeCommittee(when) {
|
|
150
153
|
const { ts, epoch } = when;
|
|
151
|
-
const [committeeHex, seed] = await Promise.all([
|
|
154
|
+
const [committeeHex, seed, l1Timestamp] = await Promise.all([
|
|
152
155
|
this.rollup.getCommitteeAt(ts),
|
|
153
|
-
this.rollup.getSampleSeedAt(ts)
|
|
156
|
+
this.rollup.getSampleSeedAt(ts),
|
|
157
|
+
this.rollup.client.getBlock({
|
|
158
|
+
includeTransactions: false
|
|
159
|
+
}).then((b)=>b.timestamp)
|
|
154
160
|
]);
|
|
161
|
+
const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
|
|
162
|
+
const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
|
|
163
|
+
if (ts - sub > l1Timestamp) {
|
|
164
|
+
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
|
+
}
|
|
155
166
|
const committee = committeeHex?.map((v)=>EthAddress.fromString(v));
|
|
156
167
|
return {
|
|
157
168
|
committee,
|
|
@@ -176,8 +187,8 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
176
187
|
name: 'seed'
|
|
177
188
|
}
|
|
178
189
|
], [
|
|
179
|
-
epoch,
|
|
180
|
-
slot,
|
|
190
|
+
BigInt(epoch),
|
|
191
|
+
BigInt(slot),
|
|
181
192
|
seed
|
|
182
193
|
]);
|
|
183
194
|
}
|
|
@@ -204,6 +215,14 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
204
215
|
};
|
|
205
216
|
}
|
|
206
217
|
/**
|
|
218
|
+
* Get the proposer attester address in the given L2 slot
|
|
219
|
+
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
220
|
+
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
221
|
+
*/ getProposerAttesterAddressInSlot(slot) {
|
|
222
|
+
const epochAndSlot = this.getEpochAndSlotAtSlot(slot);
|
|
223
|
+
return this.getProposerAttesterAddressAt(epochAndSlot);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
207
226
|
* Get the proposer attester address in the next slot
|
|
208
227
|
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
209
228
|
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
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=
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsYUFBYSxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/epoch-cache",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-devnet.20251212",
|
|
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
|
|
19
|
-
"build:dev": "tsc
|
|
18
|
+
"build": "yarn clean && ../scripts/tsc.sh",
|
|
19
|
+
"build:dev": "../scripts/tsc.sh --watch",
|
|
20
20
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
21
|
-
"start:dev": "
|
|
21
|
+
"start:dev": "concurrently -k \"tsgo -b -w\" \"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": "3.0.0-
|
|
30
|
-
"@aztec/foundation": "3.0.0-
|
|
31
|
-
"@aztec/l1-artifacts": "3.0.0-
|
|
32
|
-
"@aztec/stdlib": "3.0.0-
|
|
29
|
+
"@aztec/ethereum": "3.0.0-devnet.20251212",
|
|
30
|
+
"@aztec/foundation": "3.0.0-devnet.20251212",
|
|
31
|
+
"@aztec/l1-artifacts": "3.0.0-devnet.20251212",
|
|
32
|
+
"@aztec/stdlib": "3.0.0-devnet.20251212",
|
|
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": "2.
|
|
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.20251126.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,18 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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,
|
|
10
|
-
| '
|
|
11
|
-
| 'l1ChainId'
|
|
12
|
-
| 'viemPollingIntervalMS'
|
|
13
|
-
| 'aztecSlotDuration'
|
|
14
|
-
| 'ethereumSlotDuration'
|
|
15
|
-
| 'aztecEpochDuration'
|
|
6
|
+
'l1RpcUrls' | 'l1ChainId' | 'viemPollingIntervalMS' | 'ethereumSlotDuration'
|
|
16
7
|
>;
|
|
17
8
|
|
|
18
9
|
export function getEpochCacheConfigEnvVars(): EpochCacheConfig {
|
package/src/epoch_cache.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
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,30 +19,30 @@ 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:
|
|
22
|
-
slot:
|
|
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:
|
|
30
|
+
epoch: EpochNumber;
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
export type SlotTag = 'now' | 'next' |
|
|
33
|
+
export type SlotTag = 'now' | 'next' | SlotNumber;
|
|
33
34
|
|
|
34
35
|
export interface EpochCacheInterface {
|
|
35
36
|
getCommittee(slot: SlotTag | undefined): Promise<EpochCommitteeInfo>;
|
|
36
37
|
getEpochAndSlotNow(): EpochAndSlot;
|
|
37
38
|
getEpochAndSlotInNextL1Slot(): EpochAndSlot & { now: bigint };
|
|
38
|
-
getProposerIndexEncoding(epoch:
|
|
39
|
-
computeProposerIndex(slot:
|
|
39
|
+
getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`;
|
|
40
|
+
computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint;
|
|
40
41
|
getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
41
42
|
currentProposer: EthAddress | undefined;
|
|
42
43
|
nextProposer: EthAddress | undefined;
|
|
43
|
-
currentSlot:
|
|
44
|
-
nextSlot:
|
|
44
|
+
currentSlot: SlotNumber;
|
|
45
|
+
nextSlot: SlotNumber;
|
|
45
46
|
}>;
|
|
46
47
|
getRegisteredValidators(): Promise<EthAddress[]>;
|
|
47
48
|
isInCommittee(slot: SlotTag, validator: EthAddress): Promise<boolean>;
|
|
@@ -58,71 +59,77 @@ export interface EpochCacheInterface {
|
|
|
58
59
|
* Note: This class is very dependent on the system clock being in sync.
|
|
59
60
|
*/
|
|
60
61
|
export class EpochCache implements EpochCacheInterface {
|
|
61
|
-
|
|
62
|
+
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
63
|
+
protected cache: Map<EpochNumber, EpochCommitteeInfo> = new Map();
|
|
62
64
|
private allValidators: Set<string> = new Set();
|
|
63
65
|
private lastValidatorRefresh = 0;
|
|
64
66
|
private readonly log: Logger = createLogger('epoch-cache');
|
|
65
67
|
|
|
66
68
|
constructor(
|
|
67
69
|
private rollup: RollupContract,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
private readonly l1constants: L1RollupConstants & {
|
|
71
|
+
lagInEpochsForValidatorSet: number;
|
|
72
|
+
lagInEpochsForRandao: number;
|
|
73
|
+
},
|
|
72
74
|
private readonly dateProvider: DateProvider = new DateProvider(),
|
|
73
|
-
|
|
75
|
+
protected readonly config = { cacheSize: 12, validatorRefreshIntervalSeconds: 60 },
|
|
74
76
|
) {
|
|
75
|
-
this.
|
|
76
|
-
this.log.debug(`Initialized EpochCache with ${initialValidators?.length ?? 'no'} validators`, {
|
|
77
|
+
this.log.debug(`Initialized EpochCache`, {
|
|
77
78
|
l1constants,
|
|
78
|
-
initialValidators,
|
|
79
|
-
initialSampleSeed,
|
|
80
|
-
initialEpoch,
|
|
81
79
|
});
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
static async create(
|
|
85
|
-
|
|
83
|
+
rollupOrAddress: EthAddress | RollupContract,
|
|
86
84
|
config?: EpochCacheConfig,
|
|
87
85
|
deps: { dateProvider?: DateProvider } = {},
|
|
88
86
|
) {
|
|
89
87
|
config = config ?? getEpochCacheConfigEnvVars();
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
// Load the rollup contract if we were given an address
|
|
90
|
+
let rollup: RollupContract;
|
|
91
|
+
if ('address' in rollupOrAddress) {
|
|
92
|
+
rollup = rollupOrAddress;
|
|
93
|
+
} else {
|
|
94
|
+
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
95
|
+
const publicClient = createPublicClient({
|
|
96
|
+
chain: chain.chainInfo,
|
|
97
|
+
transport: fallback(config.l1RpcUrls.map(url => http(url))),
|
|
98
|
+
pollingInterval: config.viemPollingIntervalMS,
|
|
99
|
+
});
|
|
100
|
+
rollup = new RollupContract(publicClient, rollupOrAddress.toString());
|
|
101
|
+
}
|
|
97
102
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
const [
|
|
104
|
+
l1StartBlock,
|
|
105
|
+
l1GenesisTime,
|
|
106
|
+
proofSubmissionEpochs,
|
|
107
|
+
slotDuration,
|
|
108
|
+
epochDuration,
|
|
109
|
+
lagInEpochsForValidatorSet,
|
|
110
|
+
lagInEpochsForRandao,
|
|
111
|
+
] = await Promise.all([
|
|
112
|
+
rollup.getL1StartBlock(),
|
|
113
|
+
rollup.getL1GenesisTime(),
|
|
114
|
+
rollup.getProofSubmissionEpochs(),
|
|
115
|
+
rollup.getSlotDuration(),
|
|
116
|
+
rollup.getEpochDuration(),
|
|
117
|
+
rollup.getLagInEpochsForValidatorSet(),
|
|
118
|
+
rollup.getLagInEpochsForRandao(),
|
|
119
|
+
] as const);
|
|
120
|
+
|
|
121
|
+
const l1RollupConstants = {
|
|
110
122
|
l1StartBlock,
|
|
111
123
|
l1GenesisTime,
|
|
112
124
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
113
|
-
slotDuration:
|
|
114
|
-
epochDuration:
|
|
125
|
+
slotDuration: Number(slotDuration),
|
|
126
|
+
epochDuration: Number(epochDuration),
|
|
115
127
|
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
128
|
+
lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
|
|
129
|
+
lagInEpochsForRandao: Number(lagInEpochsForRandao),
|
|
116
130
|
};
|
|
117
131
|
|
|
118
|
-
return new EpochCache(
|
|
119
|
-
rollup,
|
|
120
|
-
epochNumber,
|
|
121
|
-
initialValidators?.map(v => EthAddress.fromString(v)),
|
|
122
|
-
sampleSeed,
|
|
123
|
-
l1RollupConstants,
|
|
124
|
-
deps.dateProvider,
|
|
125
|
-
);
|
|
132
|
+
return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
public getL1Constants(): L1RollupConstants {
|
|
@@ -138,7 +145,7 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
138
145
|
return BigInt(Math.floor(this.dateProvider.now() / 1000));
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
private getEpochAndSlotAtSlot(slot:
|
|
148
|
+
private getEpochAndSlotAtSlot(slot: SlotNumber): EpochAndSlot {
|
|
142
149
|
const epoch = getEpochAtSlot(slot, this.l1constants);
|
|
143
150
|
const ts = getTimestampRangeForEpoch(epoch, this.l1constants)[0];
|
|
144
151
|
return { epoch, ts, slot };
|
|
@@ -159,7 +166,7 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
159
166
|
};
|
|
160
167
|
}
|
|
161
168
|
|
|
162
|
-
public getCommitteeForEpoch(epoch:
|
|
169
|
+
public getCommitteeForEpoch(epoch: EpochNumber): Promise<EpochCommitteeInfo> {
|
|
163
170
|
const [startSlot] = getSlotRangeForEpoch(epoch, this.l1constants);
|
|
164
171
|
return this.getCommittee(startSlot);
|
|
165
172
|
}
|
|
@@ -201,9 +208,20 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
210
|
|
|
204
|
-
private async computeCommittee(when: { epoch:
|
|
211
|
+
private async computeCommittee(when: { epoch: EpochNumber; ts: bigint }): Promise<EpochCommitteeInfo> {
|
|
205
212
|
const { ts, epoch } = when;
|
|
206
|
-
const [committeeHex, seed] = await Promise.all([
|
|
213
|
+
const [committeeHex, seed, l1Timestamp] = await Promise.all([
|
|
214
|
+
this.rollup.getCommitteeAt(ts),
|
|
215
|
+
this.rollup.getSampleSeedAt(ts),
|
|
216
|
+
this.rollup.client.getBlock({ includeTransactions: false }).then(b => b.timestamp),
|
|
217
|
+
]);
|
|
218
|
+
const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
|
|
219
|
+
const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
|
|
220
|
+
if (ts - sub > l1Timestamp) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
207
225
|
const committee = committeeHex?.map((v: `0x${string}`) => EthAddress.fromString(v));
|
|
208
226
|
return { committee, seed, epoch };
|
|
209
227
|
}
|
|
@@ -211,18 +229,18 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
211
229
|
/**
|
|
212
230
|
* Get the ABI encoding of the proposer index - see ValidatorSelectionLib.sol computeProposerIndex
|
|
213
231
|
*/
|
|
214
|
-
getProposerIndexEncoding(epoch:
|
|
232
|
+
getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}` {
|
|
215
233
|
return encodeAbiParameters(
|
|
216
234
|
[
|
|
217
235
|
{ type: 'uint256', name: 'epoch' },
|
|
218
236
|
{ type: 'uint256', name: 'slot' },
|
|
219
237
|
{ type: 'uint256', name: 'seed' },
|
|
220
238
|
],
|
|
221
|
-
[epoch, slot, seed],
|
|
239
|
+
[BigInt(epoch), BigInt(slot), seed],
|
|
222
240
|
);
|
|
223
241
|
}
|
|
224
242
|
|
|
225
|
-
public computeProposerIndex(slot:
|
|
243
|
+
public computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint {
|
|
226
244
|
// if committe size is 0, then mod 1 is 0
|
|
227
245
|
if (size === 0n) {
|
|
228
246
|
return 0n;
|
|
@@ -236,9 +254,9 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
236
254
|
* We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
|
|
237
255
|
* which can be the next slot. If this is the case, then it will send proposals early.
|
|
238
256
|
*/
|
|
239
|
-
async getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
240
|
-
currentSlot:
|
|
241
|
-
nextSlot:
|
|
257
|
+
public async getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
258
|
+
currentSlot: SlotNumber;
|
|
259
|
+
nextSlot: SlotNumber;
|
|
242
260
|
currentProposer: EthAddress | undefined;
|
|
243
261
|
nextProposer: EthAddress | undefined;
|
|
244
262
|
}> {
|
|
@@ -253,14 +271,23 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
253
271
|
};
|
|
254
272
|
}
|
|
255
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Get the proposer attester address in the given L2 slot
|
|
276
|
+
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
277
|
+
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
278
|
+
*/
|
|
279
|
+
public getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined> {
|
|
280
|
+
const epochAndSlot = this.getEpochAndSlotAtSlot(slot);
|
|
281
|
+
return this.getProposerAttesterAddressAt(epochAndSlot);
|
|
282
|
+
}
|
|
283
|
+
|
|
256
284
|
/**
|
|
257
285
|
* Get the proposer attester address in the next slot
|
|
258
286
|
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
259
287
|
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
260
288
|
*/
|
|
261
|
-
getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined> {
|
|
289
|
+
public getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined> {
|
|
262
290
|
const epochAndSlot = this.getEpochAndSlotInNextL1Slot();
|
|
263
|
-
|
|
264
291
|
return this.getProposerAttesterAddressAt(epochAndSlot);
|
|
265
292
|
}
|
|
266
293
|
|
|
@@ -283,7 +310,10 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
283
310
|
return committee[Number(proposerIndex)];
|
|
284
311
|
}
|
|
285
312
|
|
|
286
|
-
public getProposerFromEpochCommittee(
|
|
313
|
+
public getProposerFromEpochCommittee(
|
|
314
|
+
epochCommitteeInfo: EpochCommitteeInfo,
|
|
315
|
+
slot: SlotNumber,
|
|
316
|
+
): EthAddress | undefined {
|
|
287
317
|
if (!epochCommitteeInfo.committee || epochCommitteeInfo.committee.length === 0) {
|
|
288
318
|
return undefined;
|
|
289
319
|
}
|