@aztec/sequencer-client 0.76.4 → 0.77.0-testnet-ignition.21
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/client/index.js +0 -1
- package/dest/client/sequencer-client.d.ts +12 -9
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +55 -60
- package/dest/config.d.ts +2 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +41 -46
- package/dest/global_variable_builder/global_builder.d.ts +5 -2
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +45 -31
- package/dest/global_variable_builder/index.js +0 -1
- package/dest/index.js +0 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +33 -48
- package/dest/publisher/index.js +0 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +5 -2
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.js +47 -23
- package/dest/publisher/sequencer-publisher.d.ts +10 -9
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +191 -160
- package/dest/sequencer/allowed.d.ts +1 -1
- package/dest/sequencer/allowed.d.ts.map +1 -1
- package/dest/sequencer/allowed.js +6 -13
- package/dest/sequencer/config.d.ts +1 -1
- package/dest/sequencer/config.d.ts.map +1 -1
- package/dest/sequencer/config.js +1 -2
- package/dest/sequencer/index.js +0 -1
- package/dest/sequencer/metrics.js +26 -18
- package/dest/sequencer/sequencer.d.ts +18 -12
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +589 -560
- package/dest/sequencer/timetable.d.ts +1 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +33 -19
- package/dest/sequencer/utils.d.ts +2 -2
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +22 -32
- package/dest/slasher/factory.d.ts +4 -8
- package/dest/slasher/factory.d.ts.map +1 -1
- package/dest/slasher/factory.js +5 -7
- package/dest/slasher/index.js +0 -1
- package/dest/slasher/slasher_client.d.ts +10 -62
- package/dest/slasher/slasher_client.d.ts.map +1 -1
- package/dest/slasher/slasher_client.js +47 -231
- package/dest/test/index.d.ts +3 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +4 -1
- package/dest/tx_validator/archive_cache.d.ts +3 -3
- package/dest/tx_validator/archive_cache.d.ts.map +1 -1
- package/dest/tx_validator/archive_cache.js +8 -8
- package/dest/tx_validator/gas_validator.d.ts +5 -3
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +70 -70
- package/dest/tx_validator/nullifier_cache.d.ts +2 -2
- package/dest/tx_validator/nullifier_cache.d.ts.map +1 -1
- package/dest/tx_validator/nullifier_cache.js +9 -9
- package/dest/tx_validator/phases_validator.d.ts +3 -2
- package/dest/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/tx_validator/phases_validator.js +29 -21
- package/dest/tx_validator/test_utils.d.ts +4 -2
- package/dest/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/tx_validator/test_utils.js +2 -3
- package/dest/tx_validator/tx_validator_factory.d.ts +7 -5
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +14 -13
- package/package.json +28 -29
- package/src/client/sequencer-client.ts +17 -14
- package/src/config.ts +6 -14
- package/src/global_variable_builder/global_builder.ts +18 -19
- package/src/publisher/config.ts +9 -17
- package/src/publisher/sequencer-publisher-metrics.ts +23 -2
- package/src/publisher/sequencer-publisher.ts +15 -17
- package/src/sequencer/allowed.ts +4 -4
- package/src/sequencer/config.ts +1 -1
- package/src/sequencer/sequencer.ts +40 -35
- package/src/sequencer/timetable.ts +1 -1
- package/src/sequencer/utils.ts +2 -2
- package/src/slasher/factory.ts +7 -13
- package/src/slasher/slasher_client.ts +30 -252
- package/src/test/index.ts +3 -3
- package/src/tx_validator/archive_cache.ts +4 -3
- package/src/tx_validator/gas_validator.ts +22 -31
- package/src/tx_validator/nullifier_cache.ts +3 -2
- package/src/tx_validator/phases_validator.ts +8 -8
- package/src/tx_validator/test_utils.ts +5 -3
- package/src/tx_validator/tx_validator_factory.ts +23 -17
|
@@ -1,36 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
3
|
-
type
|
|
4
|
-
type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type L2Tips,
|
|
8
|
-
} from '@aztec/circuit-types';
|
|
9
|
-
import { INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js/constants';
|
|
10
|
-
import { type L1ContractsConfig, type L1ReaderConfig, createEthereumChain } from '@aztec/ethereum';
|
|
2
|
+
type L1ContractsConfig,
|
|
3
|
+
type L1ReaderConfig,
|
|
4
|
+
type ViemPublicClient,
|
|
5
|
+
createEthereumChain,
|
|
6
|
+
} from '@aztec/ethereum';
|
|
11
7
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
12
8
|
import { createLogger } from '@aztec/foundation/log';
|
|
13
|
-
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton } from '@aztec/kv-store';
|
|
14
9
|
import { SlashFactoryAbi } from '@aztec/l1-artifacts';
|
|
10
|
+
import {
|
|
11
|
+
type L2BlockId,
|
|
12
|
+
type L2BlockSourceEvent,
|
|
13
|
+
type L2BlockSourceEventEmitter,
|
|
14
|
+
L2BlockSourceEvents,
|
|
15
|
+
} from '@aztec/stdlib/block';
|
|
15
16
|
import { type TelemetryClient, WithTracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
16
17
|
|
|
17
|
-
import {
|
|
18
|
-
type Chain,
|
|
19
|
-
type GetContractReturnType,
|
|
20
|
-
type HttpTransport,
|
|
21
|
-
type PublicClient,
|
|
22
|
-
createPublicClient,
|
|
23
|
-
getAddress,
|
|
24
|
-
getContract,
|
|
25
|
-
http,
|
|
26
|
-
} from 'viem';
|
|
18
|
+
import { type GetContractReturnType, createPublicClient, fallback, getAddress, getContract, http } from 'viem';
|
|
27
19
|
|
|
28
20
|
/**
|
|
29
21
|
* Enum defining the possible states of the Slasher client.
|
|
30
22
|
*/
|
|
31
23
|
export enum SlasherClientState {
|
|
32
24
|
IDLE,
|
|
33
|
-
SYNCHING,
|
|
34
25
|
RUNNING,
|
|
35
26
|
STOPPED,
|
|
36
27
|
}
|
|
@@ -86,22 +77,9 @@ type SlashEvent = {
|
|
|
86
77
|
* slashing only the first, because the "lifetime" of the second would have passed after that vote
|
|
87
78
|
*/
|
|
88
79
|
export class SlasherClient extends WithTracer {
|
|
89
|
-
private currentState = SlasherClientState.IDLE;
|
|
90
|
-
private syncPromise = Promise.resolve();
|
|
91
|
-
private syncResolve?: () => void = undefined;
|
|
92
|
-
private latestBlockNumberAtStart = -1;
|
|
93
|
-
private provenBlockNumberAtStart = -1;
|
|
94
|
-
|
|
95
|
-
private synchedBlockHashes: AztecAsyncMap<number, string>;
|
|
96
|
-
private synchedLatestBlockNumber: AztecAsyncSingleton<number>;
|
|
97
|
-
private synchedProvenBlockNumber: AztecAsyncSingleton<number>;
|
|
98
|
-
|
|
99
|
-
private blockStream;
|
|
100
|
-
|
|
101
80
|
private slashEvents: SlashEvent[] = [];
|
|
102
81
|
|
|
103
|
-
protected slashFactoryContract?: GetContractReturnType<typeof SlashFactoryAbi,
|
|
104
|
-
undefined;
|
|
82
|
+
protected slashFactoryContract?: GetContractReturnType<typeof SlashFactoryAbi, ViemPublicClient> = undefined;
|
|
105
83
|
|
|
106
84
|
// The amount to slash for a prune.
|
|
107
85
|
// Note that we set it to 0, such that no actual slashing will happen, but the event will be fired,
|
|
@@ -110,27 +88,17 @@ export class SlasherClient extends WithTracer {
|
|
|
110
88
|
|
|
111
89
|
constructor(
|
|
112
90
|
private config: SlasherConfig & L1ContractsConfig & L1ReaderConfig,
|
|
113
|
-
private
|
|
114
|
-
private l2BlockSource: L2BlockSource,
|
|
91
|
+
private l2BlockSource: L2BlockSourceEventEmitter,
|
|
115
92
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
116
93
|
private log = createLogger('slasher'),
|
|
117
94
|
) {
|
|
118
95
|
super(telemetry, 'slasher');
|
|
119
96
|
|
|
120
|
-
this.blockStream = new L2BlockStream(l2BlockSource, this, this, createLogger('slasher:block_stream'), {
|
|
121
|
-
batchSize: config.blockRequestBatchSize,
|
|
122
|
-
pollIntervalMS: config.blockCheckIntervalMS,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
this.synchedBlockHashes = store.openMap('slasher_block_hashes');
|
|
126
|
-
this.synchedLatestBlockNumber = store.openSingleton('slasher_last_l2_block');
|
|
127
|
-
this.synchedProvenBlockNumber = store.openSingleton('slasher_last_proven_l2_block');
|
|
128
|
-
|
|
129
97
|
if (config.l1Contracts.slashFactoryAddress && config.l1Contracts.slashFactoryAddress !== EthAddress.ZERO) {
|
|
130
|
-
const chain = createEthereumChain(config.
|
|
98
|
+
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
131
99
|
const publicClient = createPublicClient({
|
|
132
100
|
chain: chain.chainInfo,
|
|
133
|
-
transport:
|
|
101
|
+
transport: fallback(chain.rpcUrls.map(url => http(url))),
|
|
134
102
|
pollingInterval: config.viemPollingIntervalMS,
|
|
135
103
|
});
|
|
136
104
|
|
|
@@ -146,6 +114,11 @@ export class SlasherClient extends WithTracer {
|
|
|
146
114
|
this.log.info(`Slasher client initialized`);
|
|
147
115
|
}
|
|
148
116
|
|
|
117
|
+
public start() {
|
|
118
|
+
this.log.info('Starting Slasher client...');
|
|
119
|
+
this.l2BlockSource.on(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
|
|
120
|
+
}
|
|
121
|
+
|
|
149
122
|
// This is where we should put a bunch of the improvements mentioned earlier.
|
|
150
123
|
public async getSlashPayload(slotNumber: bigint): Promise<EthAddress | undefined> {
|
|
151
124
|
if (!this.slashFactoryContract) {
|
|
@@ -177,198 +150,32 @@ export class SlasherClient extends WithTracer {
|
|
|
177
150
|
return EthAddress.fromString(payloadAddress);
|
|
178
151
|
}
|
|
179
152
|
|
|
180
|
-
public
|
|
181
|
-
return this.synchedBlockHashes.getAsync(number);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
public async getL2Tips(): Promise<L2Tips> {
|
|
185
|
-
const latestBlockNumber = await this.getSyncedLatestBlockNum();
|
|
186
|
-
let latestBlockHash: string | undefined;
|
|
187
|
-
const provenBlockNumber = await this.getSyncedProvenBlockNum();
|
|
188
|
-
let provenBlockHash: string | undefined;
|
|
189
|
-
|
|
190
|
-
if (latestBlockNumber > 0) {
|
|
191
|
-
latestBlockHash = await this.synchedBlockHashes.getAsync(latestBlockNumber);
|
|
192
|
-
if (typeof latestBlockHash === 'undefined') {
|
|
193
|
-
this.log.warn(`Block hash for latest block ${latestBlockNumber} not found`);
|
|
194
|
-
throw new Error();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (provenBlockNumber > 0) {
|
|
199
|
-
provenBlockHash = await this.synchedBlockHashes.getAsync(provenBlockNumber);
|
|
200
|
-
if (typeof provenBlockHash === 'undefined') {
|
|
201
|
-
this.log.warn(`Block hash for proven block ${provenBlockNumber} not found`);
|
|
202
|
-
throw new Error();
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return Promise.resolve({
|
|
207
|
-
latest: { hash: latestBlockHash!, number: latestBlockNumber },
|
|
208
|
-
proven: { hash: provenBlockHash!, number: provenBlockNumber },
|
|
209
|
-
finalized: { hash: provenBlockHash!, number: provenBlockNumber },
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
153
|
+
public handleBlockStreamEvent(event: L2BlockSourceEvent): Promise<void> {
|
|
214
154
|
this.log.debug(`Handling block stream event ${event.type}`);
|
|
215
155
|
switch (event.type) {
|
|
216
|
-
case
|
|
217
|
-
|
|
218
|
-
break;
|
|
219
|
-
case 'chain-finalized':
|
|
220
|
-
// TODO (alexg): I think we can prune the block hashes map here
|
|
221
|
-
break;
|
|
222
|
-
case 'chain-proven': {
|
|
223
|
-
const from = (await this.getSyncedProvenBlockNum()) + 1;
|
|
224
|
-
const limit = event.blockNumber - from + 1;
|
|
225
|
-
await this.handleProvenL2Blocks(await this.l2BlockSource.getBlocks(from, limit));
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
case 'chain-pruned':
|
|
229
|
-
await this.handlePruneL2Blocks(event.blockNumber);
|
|
156
|
+
case L2BlockSourceEvents.L2PruneDetected:
|
|
157
|
+
this.handlePruneL2Blocks(event);
|
|
230
158
|
break;
|
|
231
159
|
default: {
|
|
232
|
-
const _: never = event;
|
|
233
160
|
break;
|
|
234
161
|
}
|
|
235
162
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
public async start() {
|
|
239
|
-
if (this.currentState === SlasherClientState.STOPPED) {
|
|
240
|
-
throw new Error('Slasher already stopped');
|
|
241
|
-
}
|
|
242
|
-
if (this.currentState !== SlasherClientState.IDLE) {
|
|
243
|
-
return this.syncPromise;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// get the current latest block numbers
|
|
247
|
-
this.latestBlockNumberAtStart = await this.l2BlockSource.getBlockNumber();
|
|
248
|
-
this.provenBlockNumberAtStart = await this.l2BlockSource.getProvenBlockNumber();
|
|
249
|
-
|
|
250
|
-
const syncedLatestBlock = (await this.getSyncedLatestBlockNum()) + 1;
|
|
251
|
-
const syncedProvenBlock = (await this.getSyncedProvenBlockNum()) + 1;
|
|
252
|
-
|
|
253
|
-
// if there are blocks to be retrieved, go to a synching state
|
|
254
|
-
if (syncedLatestBlock <= this.latestBlockNumberAtStart || syncedProvenBlock <= this.provenBlockNumberAtStart) {
|
|
255
|
-
this.setCurrentState(SlasherClientState.SYNCHING);
|
|
256
|
-
this.syncPromise = new Promise(resolve => {
|
|
257
|
-
this.syncResolve = resolve;
|
|
258
|
-
});
|
|
259
|
-
this.log.verbose(`Starting sync from ${syncedLatestBlock} (last proven ${syncedProvenBlock})`);
|
|
260
|
-
} else {
|
|
261
|
-
// if no blocks to be retrieved, go straight to running
|
|
262
|
-
this.setCurrentState(SlasherClientState.RUNNING);
|
|
263
|
-
this.syncPromise = Promise.resolve();
|
|
264
|
-
this.log.verbose(`Block ${syncedLatestBlock} (proven ${syncedProvenBlock}) already beyond current block`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
this.blockStream.start();
|
|
268
|
-
this.log.verbose(`Started block downloader from block ${syncedLatestBlock}`);
|
|
269
|
-
|
|
270
|
-
return this.syncPromise;
|
|
163
|
+
return Promise.resolve();
|
|
271
164
|
}
|
|
272
165
|
|
|
273
166
|
/**
|
|
274
167
|
* Allows consumers to stop the instance of the slasher client.
|
|
275
168
|
* 'ready' will now return 'false' and the running promise that keeps the client synced is interrupted.
|
|
276
169
|
*/
|
|
277
|
-
public
|
|
170
|
+
public stop() {
|
|
278
171
|
this.log.debug('Stopping Slasher client...');
|
|
279
|
-
|
|
280
|
-
this.log.debug('Stopped block downloader');
|
|
281
|
-
await this.store.close();
|
|
282
|
-
this.log.debug('Stopped slasher store');
|
|
283
|
-
this.setCurrentState(SlasherClientState.STOPPED);
|
|
172
|
+
this.l2BlockSource.removeListener(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
|
|
284
173
|
this.log.info('Slasher client stopped.');
|
|
285
174
|
}
|
|
286
175
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
*/
|
|
291
|
-
public isReady() {
|
|
292
|
-
return this.currentState === SlasherClientState.RUNNING;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Public function to check the latest block number that the slasher client is synced to.
|
|
297
|
-
* @returns Block number of latest L2 Block we've synced with.
|
|
298
|
-
*/
|
|
299
|
-
public async getSyncedLatestBlockNum(): Promise<number> {
|
|
300
|
-
return (await this.synchedLatestBlockNumber.getAsync()) ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Public function to check the latest proven block number that the slasher client is synced to.
|
|
305
|
-
* @returns Block number of latest proven L2 Block we've synced with.
|
|
306
|
-
*/
|
|
307
|
-
public async getSyncedProvenBlockNum(): Promise<number> {
|
|
308
|
-
return (await this.synchedProvenBlockNumber.getAsync()) ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Method to check the status of the slasher client.
|
|
313
|
-
* @returns Information about slasher client status: state & syncedToBlockNum.
|
|
314
|
-
*/
|
|
315
|
-
public async getStatus(): Promise<SlasherSyncState> {
|
|
316
|
-
const blockNumber = await this.getSyncedLatestBlockNum();
|
|
317
|
-
const blockHash =
|
|
318
|
-
blockNumber == 0
|
|
319
|
-
? ''
|
|
320
|
-
: await this.l2BlockSource
|
|
321
|
-
.getBlockHeader(blockNumber)
|
|
322
|
-
.then(header => header?.hash())
|
|
323
|
-
.then(hash => hash?.toString());
|
|
324
|
-
return Promise.resolve({
|
|
325
|
-
state: this.currentState,
|
|
326
|
-
syncedToL2Block: { number: blockNumber, hash: blockHash },
|
|
327
|
-
} as SlasherSyncState);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Handles new blocks
|
|
332
|
-
* @param blocks - A list of blocks that the slasher client needs to store block hashes for
|
|
333
|
-
* @returns Empty promise.
|
|
334
|
-
*/
|
|
335
|
-
private async handleLatestL2Blocks(blocks: L2Block[]): Promise<void> {
|
|
336
|
-
if (!blocks.length) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
await this.store.transactionAsync(async () => {
|
|
341
|
-
for (const block of blocks) {
|
|
342
|
-
await this.synchedBlockHashes.set(block.number, (await block.hash()).toString());
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
346
|
-
await this.synchedLatestBlockNumber.set(lastBlockNum);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
await this.startServiceIfSynched();
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Handles new proven blocks by updating the proven block number
|
|
354
|
-
* @param blocks - A list of proven L2 blocks.
|
|
355
|
-
* @returns Empty promise.
|
|
356
|
-
*/
|
|
357
|
-
private async handleProvenL2Blocks(blocks: L2Block[]): Promise<void> {
|
|
358
|
-
if (!blocks.length) {
|
|
359
|
-
return Promise.resolve();
|
|
360
|
-
}
|
|
361
|
-
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
362
|
-
await this.synchedProvenBlockNumber.set(lastBlockNum);
|
|
363
|
-
this.log.debug(`Synched to proven block ${lastBlockNum}`);
|
|
364
|
-
|
|
365
|
-
await this.startServiceIfSynched();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private async handlePruneL2Blocks(latestBlock: number): Promise<void> {
|
|
369
|
-
const blockHeader = await this.l2BlockSource.getBlockHeader(latestBlock);
|
|
370
|
-
const slotNumber = blockHeader ? blockHeader.globalVariables.slotNumber.toBigInt() : BigInt(0);
|
|
371
|
-
const epochNumber = slotNumber / BigInt(this.config.aztecEpochDuration);
|
|
176
|
+
// I need to get the slot number from the block that was just pruned
|
|
177
|
+
private handlePruneL2Blocks(event: L2BlockSourceEvent): void {
|
|
178
|
+
const { slotNumber, epochNumber } = event;
|
|
372
179
|
this.log.info(`Detected chain prune. Punishing the validators at epoch ${epochNumber}`);
|
|
373
180
|
|
|
374
181
|
// Set the lifetime such that we have a full round that we could vote throughout.
|
|
@@ -382,34 +189,5 @@ export class SlasherClient extends WithTracer {
|
|
|
382
189
|
amount: this.slashingAmount,
|
|
383
190
|
lifetime,
|
|
384
191
|
});
|
|
385
|
-
|
|
386
|
-
await this.synchedLatestBlockNumber.set(latestBlock);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private async startServiceIfSynched() {
|
|
390
|
-
const [latestBlock, provenBlock] = await Promise.all([
|
|
391
|
-
this.getSyncedLatestBlockNum(),
|
|
392
|
-
this.getSyncedProvenBlockNum(),
|
|
393
|
-
]);
|
|
394
|
-
if (
|
|
395
|
-
this.currentState === SlasherClientState.SYNCHING &&
|
|
396
|
-
latestBlock >= this.latestBlockNumberAtStart &&
|
|
397
|
-
provenBlock >= this.provenBlockNumberAtStart
|
|
398
|
-
) {
|
|
399
|
-
this.log.debug(`Synched to blocks at start`);
|
|
400
|
-
this.setCurrentState(SlasherClientState.RUNNING);
|
|
401
|
-
if (this.syncResolve !== undefined) {
|
|
402
|
-
this.syncResolve();
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Method to set the value of the current state.
|
|
409
|
-
* @param newState - New state value.
|
|
410
|
-
*/
|
|
411
|
-
private setCurrentState(newState: SlasherClientState) {
|
|
412
|
-
this.currentState = newState;
|
|
413
|
-
this.log.debug(`Moved to state ${SlasherClientState[this.currentState]}`);
|
|
414
192
|
}
|
|
415
193
|
}
|
package/src/test/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
2
2
|
|
|
3
3
|
import { SequencerClient } from '../client/sequencer-client.js';
|
|
4
|
-
import {
|
|
4
|
+
import type { SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
5
5
|
import { Sequencer } from '../sequencer/sequencer.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { SequencerTimetable } from '../sequencer/timetable.js';
|
|
7
7
|
|
|
8
8
|
class TestSequencer_ extends Sequencer {
|
|
9
9
|
public override publicProcessorFactory!: PublicProcessorFactory;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { Fr } from '@aztec/foundation/fields';
|
|
2
|
+
import type { ArchiveSource } from '@aztec/p2p';
|
|
3
|
+
import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
|
|
4
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Implements an archive source by checking a DB and an in-memory collection.
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type AztecAddress, Fr, FunctionSelector, type GasFees } from '@aztec/circuits.js';
|
|
3
|
-
import { U128 } from '@aztec/foundation/abi';
|
|
1
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
4
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
-
import { computeFeePayerBalanceStorageSlot
|
|
3
|
+
import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
4
|
+
import { getExecutionRequestsByPhase } from '@aztec/simulator/server';
|
|
5
|
+
import { FunctionSelector } from '@aztec/stdlib/abi';
|
|
6
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
|
+
import type { GasFees } from '@aztec/stdlib/gas';
|
|
8
|
+
import { type Tx, TxExecutionPhase, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
|
|
6
9
|
|
|
7
10
|
/** Provides a view into public contract state */
|
|
8
11
|
export interface PublicStateSource {
|
|
@@ -13,23 +16,16 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
13
16
|
#log = createLogger('sequencer:tx_validator:tx_gas');
|
|
14
17
|
#publicDataSource: PublicStateSource;
|
|
15
18
|
#feeJuiceAddress: AztecAddress;
|
|
16
|
-
#enforceFees: boolean;
|
|
17
19
|
#gasFees: GasFees;
|
|
18
20
|
|
|
19
|
-
constructor(
|
|
20
|
-
publicDataSource: PublicStateSource,
|
|
21
|
-
feeJuiceAddress: AztecAddress,
|
|
22
|
-
enforceFees: boolean,
|
|
23
|
-
gasFees: GasFees,
|
|
24
|
-
) {
|
|
21
|
+
constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, gasFees: GasFees) {
|
|
25
22
|
this.#publicDataSource = publicDataSource;
|
|
26
23
|
this.#feeJuiceAddress = feeJuiceAddress;
|
|
27
|
-
this.#enforceFees = enforceFees;
|
|
28
24
|
this.#gasFees = gasFees;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
validateTx(tx: Tx): Promise<TxValidationResult> {
|
|
32
|
-
if (this.#shouldSkip(tx)) {
|
|
27
|
+
async validateTx(tx: Tx): Promise<TxValidationResult> {
|
|
28
|
+
if (await this.#shouldSkip(tx)) {
|
|
33
29
|
return Promise.resolve({ result: 'skipped', reason: ['Insufficient fee per gas'] });
|
|
34
30
|
}
|
|
35
31
|
return this.#validateTxFee(tx);
|
|
@@ -37,11 +33,11 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
37
33
|
|
|
38
34
|
/**
|
|
39
35
|
* Check whether the tx's max fees are valid for the current block, and skip if not.
|
|
40
|
-
* We skip instead of invalidating since the tx may become
|
|
36
|
+
* We skip instead of invalidating since the tx may become eligible later.
|
|
41
37
|
* Note that circuits check max fees even if fee payer is unset, so we
|
|
42
38
|
* keep this validation even if the tx does not pay fees.
|
|
43
39
|
*/
|
|
44
|
-
#shouldSkip(tx: Tx): boolean {
|
|
40
|
+
async #shouldSkip(tx: Tx): Promise<boolean> {
|
|
45
41
|
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
46
42
|
|
|
47
43
|
// Skip the tx if its max fees are not enough for the current block's gas fees.
|
|
@@ -51,22 +47,16 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
51
47
|
maxFeesPerGas.feePerL2Gas.lt(this.#gasFees.feePerL2Gas);
|
|
52
48
|
|
|
53
49
|
if (notEnoughMaxFees) {
|
|
54
|
-
this.#log.warn(`Skipping transaction ${tx.getTxHash()} due to insufficient fee per gas
|
|
50
|
+
this.#log.warn(`Skipping transaction ${await tx.getTxHash()} due to insufficient fee per gas`, {
|
|
51
|
+
txMaxFeesPerGas: maxFeesPerGas.toInspect(),
|
|
52
|
+
currentGasFees: this.#gasFees.toInspect(),
|
|
53
|
+
});
|
|
55
54
|
}
|
|
56
55
|
return notEnoughMaxFees;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
async #validateTxFee(tx: Tx): Promise<TxValidationResult> {
|
|
60
59
|
const feePayer = tx.data.feePayer;
|
|
61
|
-
// TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx
|
|
62
|
-
if (feePayer.isZero()) {
|
|
63
|
-
if (this.#enforceFees) {
|
|
64
|
-
this.#log.warn(`Rejecting transaction ${tx.getTxHash()} due to missing fee payer`);
|
|
65
|
-
return { result: 'invalid', reason: ['Missing fee payer'] };
|
|
66
|
-
} else {
|
|
67
|
-
return { result: 'valid' };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
60
|
|
|
71
61
|
// Compute the maximum fee that this tx may pay, based on its gasLimits and maxFeePerGas
|
|
72
62
|
const feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit();
|
|
@@ -80,7 +70,7 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
80
70
|
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
81
71
|
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
82
72
|
const increasePublicBalanceSelector = await FunctionSelector.fromSignature(
|
|
83
|
-
'_increase_public_balance((Field),
|
|
73
|
+
'_increase_public_balance((Field),u128)',
|
|
84
74
|
);
|
|
85
75
|
const claimFunctionCall = setupFns.find(
|
|
86
76
|
fn =>
|
|
@@ -93,10 +83,11 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
93
83
|
!fn.callContext.isStaticCall,
|
|
94
84
|
);
|
|
95
85
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
// The claim amount is at index 2 in the args array because:
|
|
87
|
+
// - Index 0: Target function selector (due to dispatch routing)
|
|
88
|
+
// - Index 1: Amount recipient
|
|
89
|
+
// - Index 2: Amount being claimed
|
|
90
|
+
const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[2]) : initialBalance;
|
|
100
91
|
if (balance.lt(feeLimit)) {
|
|
101
92
|
this.#log.warn(`Rejecting transaction due to not enough fee payer balance`, {
|
|
102
93
|
feePayer,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { NullifierSource } from '@aztec/p2p';
|
|
2
|
+
import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
|
|
3
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Implements a nullifier source by checking a DB and an in-memory collection.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { ContractsDataSourcePublicDB, getExecutionRequestsByPhase } from '@aztec/simulator/server';
|
|
3
|
+
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
4
|
+
import type { AllowedElement } from '@aztec/stdlib/interfaces/server';
|
|
1
5
|
import {
|
|
2
|
-
type AllowedElement,
|
|
3
6
|
type PublicExecutionRequest,
|
|
4
7
|
Tx,
|
|
5
8
|
TxExecutionPhase,
|
|
6
9
|
type TxValidationResult,
|
|
7
10
|
type TxValidator,
|
|
8
|
-
} from '@aztec/
|
|
9
|
-
import { type ContractDataSource } from '@aztec/circuits.js';
|
|
10
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
11
|
-
import { ContractsDataSourcePublicDB, getExecutionRequestsByPhase } from '@aztec/simulator/server';
|
|
11
|
+
} from '@aztec/stdlib/tx';
|
|
12
12
|
|
|
13
13
|
export class PhasesTxValidator implements TxValidator<Tx> {
|
|
14
14
|
#log = createLogger('sequencer:tx_validator:tx_phases');
|
|
@@ -46,7 +46,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
46
46
|
|
|
47
47
|
return { result: 'valid' };
|
|
48
48
|
} finally {
|
|
49
|
-
|
|
49
|
+
this.contractDataSource.clearContractsForTx();
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -78,14 +78,14 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if ('classId' in entry && !('selector' in entry)) {
|
|
81
|
-
if (contractClass.
|
|
81
|
+
if (contractClass.currentContractClassId.equals(entry.classId)) {
|
|
82
82
|
return true;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
if ('classId' in entry && 'selector' in entry) {
|
|
87
87
|
if (
|
|
88
|
-
contractClass.
|
|
88
|
+
contractClass.currentContractClassId.equals(entry.classId) &&
|
|
89
89
|
(entry.selector === undefined || entry.selector.equals(functionSelector))
|
|
90
90
|
) {
|
|
91
91
|
return true;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import type { Fr } from '@aztec/foundation/fields';
|
|
2
|
+
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
3
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
|
+
import { computeVarArgsHash } from '@aztec/stdlib/hash';
|
|
5
|
+
import type { Tx } from '@aztec/stdlib/tx';
|
|
4
6
|
|
|
5
7
|
export function patchNonRevertibleFn(
|
|
6
8
|
tx: Tx,
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type AllowedElement,
|
|
3
|
-
type ClientProtocolCircuitVerifier,
|
|
4
|
-
type MerkleTreeReadOperations,
|
|
5
|
-
type ProcessedTx,
|
|
6
|
-
type Tx,
|
|
7
|
-
type TxValidator,
|
|
8
|
-
} from '@aztec/circuit-types';
|
|
9
|
-
import { type AztecAddress, type ContractDataSource, Fr, type GasFees, type GlobalVariables } from '@aztec/circuits.js';
|
|
1
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
10
2
|
import {
|
|
11
3
|
AggregateTxValidator,
|
|
12
4
|
BlockHeaderTxValidator,
|
|
@@ -17,6 +9,15 @@ import {
|
|
|
17
9
|
} from '@aztec/p2p';
|
|
18
10
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
19
11
|
import { readPublicState } from '@aztec/simulator/server';
|
|
12
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
13
|
+
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
14
|
+
import type { GasFees } from '@aztec/stdlib/gas';
|
|
15
|
+
import type {
|
|
16
|
+
AllowedElement,
|
|
17
|
+
ClientProtocolCircuitVerifier,
|
|
18
|
+
MerkleTreeReadOperations,
|
|
19
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
20
|
+
import { GlobalVariables, type ProcessedTx, type Tx, type TxValidator } from '@aztec/stdlib/tx';
|
|
20
21
|
|
|
21
22
|
import { ArchiveCache } from './archive_cache.js';
|
|
22
23
|
import { GasTxValidator, type PublicStateSource } from './gas_validator.js';
|
|
@@ -27,24 +28,32 @@ export function createValidatorForAcceptingTxs(
|
|
|
27
28
|
db: MerkleTreeReadOperations,
|
|
28
29
|
contractDataSource: ContractDataSource,
|
|
29
30
|
verifier: ClientProtocolCircuitVerifier | undefined,
|
|
30
|
-
|
|
31
|
+
{
|
|
32
|
+
blockNumber,
|
|
33
|
+
l1ChainId,
|
|
34
|
+
setupAllowList,
|
|
35
|
+
gasFees,
|
|
36
|
+
skipFeeEnforcement,
|
|
37
|
+
}: {
|
|
31
38
|
blockNumber: number;
|
|
32
39
|
l1ChainId: number;
|
|
33
|
-
enforceFees: boolean;
|
|
34
40
|
setupAllowList: AllowedElement[];
|
|
35
41
|
gasFees: GasFees;
|
|
42
|
+
skipFeeEnforcement?: boolean;
|
|
36
43
|
},
|
|
37
44
|
): TxValidator<Tx> {
|
|
38
|
-
const { blockNumber, l1ChainId, enforceFees, setupAllowList, gasFees } = data;
|
|
39
45
|
const validators: TxValidator<Tx>[] = [
|
|
40
46
|
new DataTxValidator(),
|
|
41
47
|
new MetadataTxValidator(new Fr(l1ChainId), new Fr(blockNumber)),
|
|
42
48
|
new DoubleSpendTxValidator(new NullifierCache(db)),
|
|
43
49
|
new PhasesTxValidator(contractDataSource, setupAllowList),
|
|
44
|
-
new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, enforceFees, gasFees),
|
|
45
50
|
new BlockHeaderTxValidator(new ArchiveCache(db)),
|
|
46
51
|
];
|
|
47
52
|
|
|
53
|
+
if (!skipFeeEnforcement) {
|
|
54
|
+
validators.push(new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees));
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
if (verifier) {
|
|
49
58
|
validators.push(new TxProofValidator(verifier));
|
|
50
59
|
}
|
|
@@ -56,7 +65,6 @@ export function createValidatorsForBlockBuilding(
|
|
|
56
65
|
db: MerkleTreeReadOperations,
|
|
57
66
|
contractDataSource: ContractDataSource,
|
|
58
67
|
globalVariables: GlobalVariables,
|
|
59
|
-
enforceFees: boolean,
|
|
60
68
|
setupAllowList: AllowedElement[],
|
|
61
69
|
): {
|
|
62
70
|
preprocessValidator: TxValidator<Tx>;
|
|
@@ -73,7 +81,6 @@ export function createValidatorsForBlockBuilding(
|
|
|
73
81
|
archiveCache,
|
|
74
82
|
publicStateSource,
|
|
75
83
|
contractDataSource,
|
|
76
|
-
enforceFees,
|
|
77
84
|
globalVariables,
|
|
78
85
|
setupAllowList,
|
|
79
86
|
),
|
|
@@ -95,7 +102,6 @@ function preprocessValidator(
|
|
|
95
102
|
archiveCache: ArchiveCache,
|
|
96
103
|
publicStateSource: PublicStateSource,
|
|
97
104
|
contractDataSource: ContractDataSource,
|
|
98
|
-
enforceFees: boolean,
|
|
99
105
|
globalVariables: GlobalVariables,
|
|
100
106
|
setupAllowList: AllowedElement[],
|
|
101
107
|
): TxValidator<Tx> {
|
|
@@ -104,7 +110,7 @@ function preprocessValidator(
|
|
|
104
110
|
new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber),
|
|
105
111
|
new DoubleSpendTxValidator(nullifierCache),
|
|
106
112
|
new PhasesTxValidator(contractDataSource, setupAllowList),
|
|
107
|
-
new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice,
|
|
113
|
+
new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees),
|
|
108
114
|
new BlockHeaderTxValidator(archiveCache),
|
|
109
115
|
);
|
|
110
116
|
}
|