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