@aztec/epoch-cache 4.0.0-nightly.20250907 → 4.0.0-nightly.20260107
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 +58 -38
- package/dest/index.d.ts +1 -1
- package/package.json +10 -9
- package/src/config.ts +3 -12
- package/src/epoch_cache.ts +96 -67
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,47 @@ 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
|
+
batch: false
|
|
55
|
+
}))),
|
|
56
|
+
pollingInterval: config.viemPollingIntervalMS
|
|
57
|
+
});
|
|
58
|
+
rollup = new RollupContract(publicClient, rollupOrAddress.toString());
|
|
59
|
+
}
|
|
60
|
+
const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, slotDuration, epochDuration, lagInEpochsForValidatorSet, lagInEpochsForRandao] = await Promise.all([
|
|
59
61
|
rollup.getL1StartBlock(),
|
|
60
62
|
rollup.getL1GenesisTime(),
|
|
61
|
-
rollup.
|
|
62
|
-
rollup.
|
|
63
|
-
rollup.
|
|
64
|
-
rollup.
|
|
63
|
+
rollup.getProofSubmissionEpochs(),
|
|
64
|
+
rollup.getSlotDuration(),
|
|
65
|
+
rollup.getEpochDuration(),
|
|
66
|
+
rollup.getLagInEpochsForValidatorSet(),
|
|
67
|
+
rollup.getLagInEpochsForRandao()
|
|
65
68
|
]);
|
|
66
69
|
const l1RollupConstants = {
|
|
67
70
|
l1StartBlock,
|
|
68
71
|
l1GenesisTime,
|
|
69
72
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
70
|
-
slotDuration:
|
|
71
|
-
epochDuration:
|
|
72
|
-
ethereumSlotDuration: config.ethereumSlotDuration
|
|
73
|
+
slotDuration: Number(slotDuration),
|
|
74
|
+
epochDuration: Number(epochDuration),
|
|
75
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
76
|
+
lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
|
|
77
|
+
lagInEpochsForRandao: Number(lagInEpochsForRandao)
|
|
73
78
|
};
|
|
74
|
-
return new EpochCache(rollup,
|
|
79
|
+
return new EpochCache(rollup, l1RollupConstants, deps.dateProvider);
|
|
75
80
|
}
|
|
76
81
|
getL1Constants() {
|
|
77
82
|
return this.l1constants;
|
|
@@ -148,14 +153,21 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
148
153
|
}
|
|
149
154
|
async computeCommittee(when) {
|
|
150
155
|
const { ts, epoch } = when;
|
|
151
|
-
const [
|
|
156
|
+
const [committee, seedBuffer, l1Timestamp] = await Promise.all([
|
|
152
157
|
this.rollup.getCommitteeAt(ts),
|
|
153
|
-
this.rollup.getSampleSeedAt(ts)
|
|
158
|
+
this.rollup.getSampleSeedAt(ts),
|
|
159
|
+
this.rollup.client.getBlock({
|
|
160
|
+
includeTransactions: false
|
|
161
|
+
}).then((b)=>b.timestamp)
|
|
154
162
|
]);
|
|
155
|
-
const
|
|
163
|
+
const { lagInEpochsForValidatorSet, epochDuration, slotDuration } = this.l1constants;
|
|
164
|
+
const sub = BigInt(lagInEpochsForValidatorSet) * BigInt(epochDuration) * BigInt(slotDuration);
|
|
165
|
+
if (ts - sub > l1Timestamp) {
|
|
166
|
+
throw new Error(`Cannot query committee for future epoch ${epoch} with timestamp ${ts} (current L1 time is ${l1Timestamp}). Check your Ethereum node is synced.`);
|
|
167
|
+
}
|
|
156
168
|
return {
|
|
157
169
|
committee,
|
|
158
|
-
seed,
|
|
170
|
+
seed: seedBuffer.toBigInt(),
|
|
159
171
|
epoch
|
|
160
172
|
};
|
|
161
173
|
}
|
|
@@ -176,8 +188,8 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
176
188
|
name: 'seed'
|
|
177
189
|
}
|
|
178
190
|
], [
|
|
179
|
-
epoch,
|
|
180
|
-
slot,
|
|
191
|
+
BigInt(epoch),
|
|
192
|
+
BigInt(slot),
|
|
181
193
|
seed
|
|
182
194
|
]);
|
|
183
195
|
}
|
|
@@ -204,6 +216,14 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
204
216
|
};
|
|
205
217
|
}
|
|
206
218
|
/**
|
|
219
|
+
* Get the proposer attester address in the given L2 slot
|
|
220
|
+
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
221
|
+
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
222
|
+
*/ getProposerAttesterAddressInSlot(slot) {
|
|
223
|
+
const epochAndSlot = this.getEpochAndSlotAtSlot(slot);
|
|
224
|
+
return this.getProposerAttesterAddressAt(epochAndSlot);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
207
227
|
* Get the proposer attester address in the next slot
|
|
208
228
|
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
209
229
|
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
@@ -254,9 +274,9 @@ import { getEpochCacheConfigEnvVars } from './config.js';
|
|
|
254
274
|
const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
|
|
255
275
|
if (validatorRefreshTime < this.dateProvider.now()) {
|
|
256
276
|
const currentSet = await this.rollup.getAttesters();
|
|
257
|
-
this.allValidators = new Set(currentSet);
|
|
277
|
+
this.allValidators = new Set(currentSet.map((v)=>v.toString()));
|
|
258
278
|
this.lastValidatorRefresh = this.dateProvider.now();
|
|
259
279
|
}
|
|
260
|
-
return Array.from(this.allValidators.keys().map((v)=>EthAddress.fromString(v))
|
|
280
|
+
return Array.from(this.allValidators.keys()).map((v)=>EthAddress.fromString(v));
|
|
261
281
|
}
|
|
262
282
|
}
|
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": "4.0.0-nightly.
|
|
3
|
+
"version": "4.0.0-nightly.20260107",
|
|
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": "4.0.0-nightly.
|
|
30
|
-
"@aztec/foundation": "4.0.0-nightly.
|
|
31
|
-
"@aztec/l1-artifacts": "4.0.0-nightly.
|
|
32
|
-
"@aztec/stdlib": "4.0.0-nightly.
|
|
29
|
+
"@aztec/ethereum": "4.0.0-nightly.20260107",
|
|
30
|
+
"@aztec/foundation": "4.0.0-nightly.20260107",
|
|
31
|
+
"@aztec/l1-artifacts": "4.0.0-nightly.20260107",
|
|
32
|
+
"@aztec/stdlib": "4.0.0-nightly.20260107",
|
|
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, { batch: false }))),
|
|
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,28 +208,38 @@ 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 [
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
const [committee, seedBuffer, 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
|
+
}
|
|
225
|
+
return { committee, seed: seedBuffer.toBigInt(), epoch };
|
|
209
226
|
}
|
|
210
227
|
|
|
211
228
|
/**
|
|
212
229
|
* Get the ABI encoding of the proposer index - see ValidatorSelectionLib.sol computeProposerIndex
|
|
213
230
|
*/
|
|
214
|
-
getProposerIndexEncoding(epoch:
|
|
231
|
+
getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}` {
|
|
215
232
|
return encodeAbiParameters(
|
|
216
233
|
[
|
|
217
234
|
{ type: 'uint256', name: 'epoch' },
|
|
218
235
|
{ type: 'uint256', name: 'slot' },
|
|
219
236
|
{ type: 'uint256', name: 'seed' },
|
|
220
237
|
],
|
|
221
|
-
[epoch, slot, seed],
|
|
238
|
+
[BigInt(epoch), BigInt(slot), seed],
|
|
222
239
|
);
|
|
223
240
|
}
|
|
224
241
|
|
|
225
|
-
public computeProposerIndex(slot:
|
|
242
|
+
public computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint {
|
|
226
243
|
// if committe size is 0, then mod 1 is 0
|
|
227
244
|
if (size === 0n) {
|
|
228
245
|
return 0n;
|
|
@@ -236,9 +253,9 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
236
253
|
* We return the next proposer's attester address as the node will check if it is the proposer at the next ethereum block,
|
|
237
254
|
* which can be the next slot. If this is the case, then it will send proposals early.
|
|
238
255
|
*/
|
|
239
|
-
async getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
240
|
-
currentSlot:
|
|
241
|
-
nextSlot:
|
|
256
|
+
public async getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
|
|
257
|
+
currentSlot: SlotNumber;
|
|
258
|
+
nextSlot: SlotNumber;
|
|
242
259
|
currentProposer: EthAddress | undefined;
|
|
243
260
|
nextProposer: EthAddress | undefined;
|
|
244
261
|
}> {
|
|
@@ -253,14 +270,23 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
253
270
|
};
|
|
254
271
|
}
|
|
255
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Get the proposer attester address in the given L2 slot
|
|
275
|
+
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
276
|
+
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
277
|
+
*/
|
|
278
|
+
public getProposerAttesterAddressInSlot(slot: SlotNumber): Promise<EthAddress | undefined> {
|
|
279
|
+
const epochAndSlot = this.getEpochAndSlotAtSlot(slot);
|
|
280
|
+
return this.getProposerAttesterAddressAt(epochAndSlot);
|
|
281
|
+
}
|
|
282
|
+
|
|
256
283
|
/**
|
|
257
284
|
* Get the proposer attester address in the next slot
|
|
258
285
|
* @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError.
|
|
259
286
|
* If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined.
|
|
260
287
|
*/
|
|
261
|
-
getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined> {
|
|
288
|
+
public getProposerAttesterAddressInNextSlot(): Promise<EthAddress | undefined> {
|
|
262
289
|
const epochAndSlot = this.getEpochAndSlotInNextL1Slot();
|
|
263
|
-
|
|
264
290
|
return this.getProposerAttesterAddressAt(epochAndSlot);
|
|
265
291
|
}
|
|
266
292
|
|
|
@@ -283,7 +309,10 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
283
309
|
return committee[Number(proposerIndex)];
|
|
284
310
|
}
|
|
285
311
|
|
|
286
|
-
public getProposerFromEpochCommittee(
|
|
312
|
+
public getProposerFromEpochCommittee(
|
|
313
|
+
epochCommitteeInfo: EpochCommitteeInfo,
|
|
314
|
+
slot: SlotNumber,
|
|
315
|
+
): EthAddress | undefined {
|
|
287
316
|
if (!epochCommitteeInfo.committee || epochCommitteeInfo.committee.length === 0) {
|
|
288
317
|
return undefined;
|
|
289
318
|
}
|
|
@@ -321,9 +350,9 @@ export class EpochCache implements EpochCacheInterface {
|
|
|
321
350
|
const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs;
|
|
322
351
|
if (validatorRefreshTime < this.dateProvider.now()) {
|
|
323
352
|
const currentSet = await this.rollup.getAttesters();
|
|
324
|
-
this.allValidators = new Set(currentSet);
|
|
353
|
+
this.allValidators = new Set(currentSet.map(v => v.toString()));
|
|
325
354
|
this.lastValidatorRefresh = this.dateProvider.now();
|
|
326
355
|
}
|
|
327
|
-
return Array.from(this.allValidators.keys().map(v => EthAddress.fromString(v))
|
|
356
|
+
return Array.from(this.allValidators.keys()).map(v => EthAddress.fromString(v));
|
|
328
357
|
}
|
|
329
358
|
}
|