@aztec/sequencer-client 0.77.0-testnet-ignition.17 → 0.77.0-testnet-ignition.23
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/sequencer/sequencer.js +1 -1
- package/dest/slasher/factory.d.ts +2 -6
- package/dest/slasher/factory.d.ts.map +1 -1
- package/dest/slasher/factory.js +2 -5
- package/dest/slasher/slasher_client.d.ts +7 -59
- package/dest/slasher/slasher_client.d.ts.map +1 -1
- package/dest/slasher/slasher_client.js +18 -199
- package/dest/tx_validator/phases_validator.js +1 -1
- package/package.json +25 -25
- package/src/sequencer/sequencer.ts +1 -1
- package/src/slasher/factory.ts +5 -11
- package/src/slasher/slasher_client.ts +18 -235
- package/src/tx_validator/phases_validator.ts +1 -1
|
@@ -170,7 +170,7 @@ export { SequencerState };
|
|
|
170
170
|
this.log.debug(`Stopping sequencer`);
|
|
171
171
|
await this.validatorClient?.stop();
|
|
172
172
|
await this.runningPromise?.stop();
|
|
173
|
-
|
|
173
|
+
this.slasherClient.stop();
|
|
174
174
|
this.publisher.interrupt();
|
|
175
175
|
this.setState(SequencerState.STOPPED, 0n, true);
|
|
176
176
|
this.log.info('Stopped sequencer');
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { L1ContractsConfig, L1ReaderConfig } from '@aztec/ethereum';
|
|
2
|
-
import type {
|
|
3
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
4
|
-
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
2
|
+
import type { L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
5
3
|
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
6
4
|
import { SlasherClient } from './slasher_client.js';
|
|
7
5
|
import type { SlasherConfig } from './slasher_client.js';
|
|
8
|
-
export declare const createSlasherClient: (_config: SlasherConfig &
|
|
9
|
-
store?: AztecAsyncKVStore;
|
|
10
|
-
}) => Promise<SlasherClient>;
|
|
6
|
+
export declare const createSlasherClient: (_config: SlasherConfig & L1ContractsConfig & L1ReaderConfig, l2BlockSource: L2BlockSourceEventEmitter, telemetry?: TelemetryClient) => SlasherClient;
|
|
11
7
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/slasher/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/slasher/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEnF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,eAAO,MAAM,mBAAmB,YACrB,aAAa,GAAG,iBAAiB,GAAG,cAAc,iBAC5C,yBAAyB,cAC7B,eAAe,kBAI3B,CAAC"}
|
package/dest/slasher/factory.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
-
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
3
1
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
4
2
|
import { SlasherClient } from './slasher_client.js';
|
|
5
|
-
export const createSlasherClient =
|
|
3
|
+
export const createSlasherClient = (_config, l2BlockSource, telemetry = getTelemetryClient())=>{
|
|
6
4
|
const config = {
|
|
7
5
|
..._config
|
|
8
6
|
};
|
|
9
|
-
|
|
10
|
-
return new SlasherClient(config, store, l2BlockSource, telemetry);
|
|
7
|
+
return new SlasherClient(config, l2BlockSource, telemetry);
|
|
11
8
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type L1ContractsConfig, type L1ReaderConfig, type ViemPublicClient } from '@aztec/ethereum';
|
|
2
2
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
|
-
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
4
3
|
import { SlashFactoryAbi } from '@aztec/l1-artifacts';
|
|
5
|
-
import { type L2BlockId, type
|
|
4
|
+
import { type L2BlockId, type L2BlockSourceEvent, type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
6
5
|
import { type TelemetryClient, WithTracer } from '@aztec/telemetry-client';
|
|
7
6
|
import { type GetContractReturnType } from 'viem';
|
|
8
7
|
/**
|
|
@@ -10,9 +9,8 @@ import { type GetContractReturnType } from 'viem';
|
|
|
10
9
|
*/
|
|
11
10
|
export declare enum SlasherClientState {
|
|
12
11
|
IDLE = 0,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
STOPPED = 3
|
|
12
|
+
RUNNING = 1,
|
|
13
|
+
STOPPED = 2
|
|
16
14
|
}
|
|
17
15
|
/**
|
|
18
16
|
* The synchronization status of the Slasher client.
|
|
@@ -58,70 +56,20 @@ export interface SlasherConfig {
|
|
|
58
56
|
*/
|
|
59
57
|
export declare class SlasherClient extends WithTracer {
|
|
60
58
|
private config;
|
|
61
|
-
private store;
|
|
62
59
|
private l2BlockSource;
|
|
63
60
|
private log;
|
|
64
|
-
private currentState;
|
|
65
|
-
private syncPromise;
|
|
66
|
-
private syncResolve?;
|
|
67
|
-
private latestBlockNumberAtStart;
|
|
68
|
-
private provenBlockNumberAtStart;
|
|
69
|
-
private synchedBlockHashes;
|
|
70
|
-
private synchedLatestBlockNumber;
|
|
71
|
-
private synchedProvenBlockNumber;
|
|
72
|
-
private blockStream;
|
|
73
61
|
private slashEvents;
|
|
74
62
|
protected slashFactoryContract?: GetContractReturnType<typeof SlashFactoryAbi, ViemPublicClient>;
|
|
75
63
|
private slashingAmount;
|
|
76
|
-
constructor(config: SlasherConfig & L1ContractsConfig & L1ReaderConfig,
|
|
64
|
+
constructor(config: SlasherConfig & L1ContractsConfig & L1ReaderConfig, l2BlockSource: L2BlockSourceEventEmitter, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
|
|
65
|
+
start(): void;
|
|
77
66
|
getSlashPayload(slotNumber: bigint): Promise<EthAddress | undefined>;
|
|
78
|
-
|
|
79
|
-
getL2Tips(): Promise<L2Tips>;
|
|
80
|
-
handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
|
|
81
|
-
start(): Promise<void>;
|
|
67
|
+
handleBlockStreamEvent(event: L2BlockSourceEvent): Promise<void>;
|
|
82
68
|
/**
|
|
83
69
|
* Allows consumers to stop the instance of the slasher client.
|
|
84
70
|
* 'ready' will now return 'false' and the running promise that keeps the client synced is interrupted.
|
|
85
71
|
*/
|
|
86
|
-
stop():
|
|
87
|
-
/**
|
|
88
|
-
* Public function to check if the slasher client is fully synced and ready to receive txs.
|
|
89
|
-
* @returns True if the slasher client is ready to receive txs.
|
|
90
|
-
*/
|
|
91
|
-
isReady(): boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Public function to check the latest block number that the slasher client is synced to.
|
|
94
|
-
* @returns Block number of latest L2 Block we've synced with.
|
|
95
|
-
*/
|
|
96
|
-
getSyncedLatestBlockNum(): Promise<number>;
|
|
97
|
-
/**
|
|
98
|
-
* Public function to check the latest proven block number that the slasher client is synced to.
|
|
99
|
-
* @returns Block number of latest proven L2 Block we've synced with.
|
|
100
|
-
*/
|
|
101
|
-
getSyncedProvenBlockNum(): Promise<number>;
|
|
102
|
-
/**
|
|
103
|
-
* Method to check the status of the slasher client.
|
|
104
|
-
* @returns Information about slasher client status: state & syncedToBlockNum.
|
|
105
|
-
*/
|
|
106
|
-
getStatus(): Promise<SlasherSyncState>;
|
|
107
|
-
/**
|
|
108
|
-
* Handles new blocks
|
|
109
|
-
* @param blocks - A list of blocks that the slasher client needs to store block hashes for
|
|
110
|
-
* @returns Empty promise.
|
|
111
|
-
*/
|
|
112
|
-
private handleLatestL2Blocks;
|
|
113
|
-
/**
|
|
114
|
-
* Handles new proven blocks by updating the proven block number
|
|
115
|
-
* @param blocks - A list of proven L2 blocks.
|
|
116
|
-
* @returns Empty promise.
|
|
117
|
-
*/
|
|
118
|
-
private handleProvenL2Blocks;
|
|
72
|
+
stop(): void;
|
|
119
73
|
private handlePruneL2Blocks;
|
|
120
|
-
private startServiceIfSynched;
|
|
121
|
-
/**
|
|
122
|
-
* Method to set the value of the current state.
|
|
123
|
-
* @param newState - New state value.
|
|
124
|
-
*/
|
|
125
|
-
private setCurrentState;
|
|
126
74
|
}
|
|
127
75
|
//# sourceMappingURL=slasher_client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slasher_client.d.ts","sourceRoot":"","sources":["../../src/slasher/slasher_client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slasher_client.d.ts","sourceRoot":"","sources":["../../src/slasher/slasher_client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EACL,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAE/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,eAAe,EAAE,UAAU,EAAsB,MAAM,yBAAyB,CAAC;AAE/F,OAAO,EAAE,KAAK,qBAAqB,EAA+D,MAAM,MAAM,CAAC;AAE/G;;GAEG;AACH,oBAAY,kBAAkB;IAC5B,IAAI,IAAA;IACJ,OAAO,IAAA;IACP,OAAO,IAAA;CACR;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,KAAK,EAAE,kBAAkB,CAAC;IAC1B;;OAEG;IACH,eAAe,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAQD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAWzC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;IAErB,OAAO,CAAC,GAAG;IAbb,OAAO,CAAC,WAAW,CAAoB;IAEvC,SAAS,CAAC,oBAAoB,CAAC,EAAE,qBAAqB,CAAC,OAAO,eAAe,EAAE,gBAAgB,CAAC,CAAa;IAK7G,OAAO,CAAC,cAAc,CAAc;gBAG1B,MAAM,EAAE,aAAa,GAAG,iBAAiB,GAAG,cAAc,EAC1D,aAAa,EAAE,yBAAyB,EAChD,SAAS,GAAE,eAAsC,EACzC,GAAG,yCAA0B;IAwBhC,KAAK;IAMC,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IA8B1E,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAavE;;;OAGG;IACI,IAAI;IAOX,OAAO,CAAC,mBAAmB;CAgB5B"}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
1
|
import { createEthereumChain } from '@aztec/ethereum';
|
|
3
2
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
4
|
import { SlashFactoryAbi } from '@aztec/l1-artifacts';
|
|
6
|
-
import {
|
|
5
|
+
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
7
6
|
import { WithTracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
8
7
|
import { createPublicClient, fallback, getAddress, getContract, http } from 'viem';
|
|
9
8
|
/**
|
|
10
9
|
* Enum defining the possible states of the Slasher client.
|
|
11
10
|
*/ export var SlasherClientState = /*#__PURE__*/ function(SlasherClientState) {
|
|
12
11
|
SlasherClientState[SlasherClientState["IDLE"] = 0] = "IDLE";
|
|
13
|
-
SlasherClientState[SlasherClientState["
|
|
14
|
-
SlasherClientState[SlasherClientState["
|
|
15
|
-
SlasherClientState[SlasherClientState["STOPPED"] = 3] = "STOPPED";
|
|
12
|
+
SlasherClientState[SlasherClientState["RUNNING"] = 1] = "RUNNING";
|
|
13
|
+
SlasherClientState[SlasherClientState["STOPPED"] = 2] = "STOPPED";
|
|
16
14
|
return SlasherClientState;
|
|
17
15
|
}({});
|
|
18
16
|
/**
|
|
@@ -41,33 +39,16 @@ import { createPublicClient, fallback, getAddress, getContract, http } from 'vie
|
|
|
41
39
|
* slashing only the first, because the "lifetime" of the second would have passed after that vote
|
|
42
40
|
*/ export class SlasherClient extends WithTracer {
|
|
43
41
|
config;
|
|
44
|
-
store;
|
|
45
42
|
l2BlockSource;
|
|
46
43
|
log;
|
|
47
|
-
currentState;
|
|
48
|
-
syncPromise;
|
|
49
|
-
syncResolve;
|
|
50
|
-
latestBlockNumberAtStart;
|
|
51
|
-
provenBlockNumberAtStart;
|
|
52
|
-
synchedBlockHashes;
|
|
53
|
-
synchedLatestBlockNumber;
|
|
54
|
-
synchedProvenBlockNumber;
|
|
55
|
-
blockStream;
|
|
56
44
|
slashEvents;
|
|
57
45
|
slashFactoryContract;
|
|
58
46
|
// The amount to slash for a prune.
|
|
59
47
|
// Note that we set it to 0, such that no actual slashing will happen, but the event will be fired,
|
|
60
48
|
// showing that the slashing mechanism is working.
|
|
61
49
|
slashingAmount;
|
|
62
|
-
constructor(config,
|
|
63
|
-
super(telemetry, 'slasher'), this.config = config, this.
|
|
64
|
-
this.blockStream = new L2BlockStream(l2BlockSource, this, this, createLogger('slasher:block_stream'), {
|
|
65
|
-
batchSize: config.blockRequestBatchSize,
|
|
66
|
-
pollIntervalMS: config.blockCheckIntervalMS
|
|
67
|
-
});
|
|
68
|
-
this.synchedBlockHashes = store.openMap('slasher_block_hashes');
|
|
69
|
-
this.synchedLatestBlockNumber = store.openSingleton('slasher_last_l2_block');
|
|
70
|
-
this.synchedProvenBlockNumber = store.openSingleton('slasher_last_proven_l2_block');
|
|
50
|
+
constructor(config, l2BlockSource, telemetry = getTelemetryClient(), log = createLogger('slasher')){
|
|
51
|
+
super(telemetry, 'slasher'), this.config = config, this.l2BlockSource = l2BlockSource, this.log = log, this.slashEvents = [], this.slashFactoryContract = undefined, this.slashingAmount = 0n;
|
|
71
52
|
if (config.l1Contracts.slashFactoryAddress && config.l1Contracts.slashFactoryAddress !== EthAddress.ZERO) {
|
|
72
53
|
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
73
54
|
const publicClient = createPublicClient({
|
|
@@ -85,6 +66,10 @@ import { createPublicClient, fallback, getAddress, getContract, http } from 'vie
|
|
|
85
66
|
}
|
|
86
67
|
this.log.info(`Slasher client initialized`);
|
|
87
68
|
}
|
|
69
|
+
start() {
|
|
70
|
+
this.log.info('Starting Slasher client...');
|
|
71
|
+
this.l2BlockSource.on(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
|
|
72
|
+
}
|
|
88
73
|
// This is where we should put a bunch of the improvements mentioned earlier.
|
|
89
74
|
async getSlashPayload(slotNumber) {
|
|
90
75
|
if (!this.slashFactoryContract) {
|
|
@@ -109,175 +94,30 @@ import { createPublicClient, fallback, getAddress, getContract, http } from 'vie
|
|
|
109
94
|
}
|
|
110
95
|
return EthAddress.fromString(payloadAddress);
|
|
111
96
|
}
|
|
112
|
-
|
|
113
|
-
return this.synchedBlockHashes.getAsync(number);
|
|
114
|
-
}
|
|
115
|
-
async getL2Tips() {
|
|
116
|
-
const latestBlockNumber = await this.getSyncedLatestBlockNum();
|
|
117
|
-
let latestBlockHash;
|
|
118
|
-
const provenBlockNumber = await this.getSyncedProvenBlockNum();
|
|
119
|
-
let provenBlockHash;
|
|
120
|
-
if (latestBlockNumber > 0) {
|
|
121
|
-
latestBlockHash = await this.synchedBlockHashes.getAsync(latestBlockNumber);
|
|
122
|
-
if (typeof latestBlockHash === 'undefined') {
|
|
123
|
-
this.log.warn(`Block hash for latest block ${latestBlockNumber} not found`);
|
|
124
|
-
throw new Error();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (provenBlockNumber > 0) {
|
|
128
|
-
provenBlockHash = await this.synchedBlockHashes.getAsync(provenBlockNumber);
|
|
129
|
-
if (typeof provenBlockHash === 'undefined') {
|
|
130
|
-
this.log.warn(`Block hash for proven block ${provenBlockNumber} not found`);
|
|
131
|
-
throw new Error();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return Promise.resolve({
|
|
135
|
-
latest: {
|
|
136
|
-
hash: latestBlockHash,
|
|
137
|
-
number: latestBlockNumber
|
|
138
|
-
},
|
|
139
|
-
proven: {
|
|
140
|
-
hash: provenBlockHash,
|
|
141
|
-
number: provenBlockNumber
|
|
142
|
-
},
|
|
143
|
-
finalized: {
|
|
144
|
-
hash: provenBlockHash,
|
|
145
|
-
number: provenBlockNumber
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
async handleBlockStreamEvent(event) {
|
|
97
|
+
handleBlockStreamEvent(event) {
|
|
150
98
|
this.log.debug(`Handling block stream event ${event.type}`);
|
|
151
99
|
switch(event.type){
|
|
152
|
-
case
|
|
153
|
-
|
|
154
|
-
break;
|
|
155
|
-
case 'chain-finalized':
|
|
156
|
-
break;
|
|
157
|
-
case 'chain-proven':
|
|
158
|
-
{
|
|
159
|
-
const from = await this.getSyncedProvenBlockNum() + 1;
|
|
160
|
-
const limit = event.blockNumber - from + 1;
|
|
161
|
-
await this.handleProvenL2Blocks(await this.l2BlockSource.getBlocks(from, limit));
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
case 'chain-pruned':
|
|
165
|
-
await this.handlePruneL2Blocks(event.blockNumber);
|
|
100
|
+
case L2BlockSourceEvents.L2PruneDetected:
|
|
101
|
+
this.handlePruneL2Blocks(event);
|
|
166
102
|
break;
|
|
167
103
|
default:
|
|
168
104
|
{
|
|
169
|
-
const _ = event;
|
|
170
105
|
break;
|
|
171
106
|
}
|
|
172
107
|
}
|
|
173
|
-
|
|
174
|
-
async start() {
|
|
175
|
-
if (this.currentState === 3) {
|
|
176
|
-
throw new Error('Slasher already stopped');
|
|
177
|
-
}
|
|
178
|
-
if (this.currentState !== 0) {
|
|
179
|
-
return this.syncPromise;
|
|
180
|
-
}
|
|
181
|
-
// get the current latest block numbers
|
|
182
|
-
this.latestBlockNumberAtStart = await this.l2BlockSource.getBlockNumber();
|
|
183
|
-
this.provenBlockNumberAtStart = await this.l2BlockSource.getProvenBlockNumber();
|
|
184
|
-
const syncedLatestBlock = await this.getSyncedLatestBlockNum() + 1;
|
|
185
|
-
const syncedProvenBlock = await this.getSyncedProvenBlockNum() + 1;
|
|
186
|
-
// if there are blocks to be retrieved, go to a synching state
|
|
187
|
-
if (syncedLatestBlock <= this.latestBlockNumberAtStart || syncedProvenBlock <= this.provenBlockNumberAtStart) {
|
|
188
|
-
this.setCurrentState(1);
|
|
189
|
-
this.syncPromise = new Promise((resolve)=>{
|
|
190
|
-
this.syncResolve = resolve;
|
|
191
|
-
});
|
|
192
|
-
this.log.verbose(`Starting sync from ${syncedLatestBlock} (last proven ${syncedProvenBlock})`);
|
|
193
|
-
} else {
|
|
194
|
-
// if no blocks to be retrieved, go straight to running
|
|
195
|
-
this.setCurrentState(2);
|
|
196
|
-
this.syncPromise = Promise.resolve();
|
|
197
|
-
this.log.verbose(`Block ${syncedLatestBlock} (proven ${syncedProvenBlock}) already beyond current block`);
|
|
198
|
-
}
|
|
199
|
-
this.blockStream.start();
|
|
200
|
-
this.log.verbose(`Started block downloader from block ${syncedLatestBlock}`);
|
|
201
|
-
return this.syncPromise;
|
|
108
|
+
return Promise.resolve();
|
|
202
109
|
}
|
|
203
110
|
/**
|
|
204
111
|
* Allows consumers to stop the instance of the slasher client.
|
|
205
112
|
* 'ready' will now return 'false' and the running promise that keeps the client synced is interrupted.
|
|
206
|
-
*/
|
|
113
|
+
*/ stop() {
|
|
207
114
|
this.log.debug('Stopping Slasher client...');
|
|
208
|
-
|
|
209
|
-
this.log.debug('Stopped block downloader');
|
|
210
|
-
await this.store.close();
|
|
211
|
-
this.log.debug('Stopped slasher store');
|
|
212
|
-
this.setCurrentState(3);
|
|
115
|
+
this.l2BlockSource.removeListener(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
|
|
213
116
|
this.log.info('Slasher client stopped.');
|
|
214
117
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
*/ isReady() {
|
|
219
|
-
return this.currentState === 2;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Public function to check the latest block number that the slasher client is synced to.
|
|
223
|
-
* @returns Block number of latest L2 Block we've synced with.
|
|
224
|
-
*/ async getSyncedLatestBlockNum() {
|
|
225
|
-
return await this.synchedLatestBlockNumber.getAsync() ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Public function to check the latest proven block number that the slasher client is synced to.
|
|
229
|
-
* @returns Block number of latest proven L2 Block we've synced with.
|
|
230
|
-
*/ async getSyncedProvenBlockNum() {
|
|
231
|
-
return await this.synchedProvenBlockNumber.getAsync() ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Method to check the status of the slasher client.
|
|
235
|
-
* @returns Information about slasher client status: state & syncedToBlockNum.
|
|
236
|
-
*/ async getStatus() {
|
|
237
|
-
const blockNumber = await this.getSyncedLatestBlockNum();
|
|
238
|
-
const blockHash = blockNumber == 0 ? '' : await this.l2BlockSource.getBlockHeader(blockNumber).then((header)=>header?.hash()).then((hash)=>hash?.toString());
|
|
239
|
-
return Promise.resolve({
|
|
240
|
-
state: this.currentState,
|
|
241
|
-
syncedToL2Block: {
|
|
242
|
-
number: blockNumber,
|
|
243
|
-
hash: blockHash
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Handles new blocks
|
|
249
|
-
* @param blocks - A list of blocks that the slasher client needs to store block hashes for
|
|
250
|
-
* @returns Empty promise.
|
|
251
|
-
*/ async handleLatestL2Blocks(blocks) {
|
|
252
|
-
if (!blocks.length) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
await this.store.transactionAsync(async ()=>{
|
|
256
|
-
for (const block of blocks){
|
|
257
|
-
await this.synchedBlockHashes.set(block.number, (await block.hash()).toString());
|
|
258
|
-
}
|
|
259
|
-
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
260
|
-
await this.synchedLatestBlockNumber.set(lastBlockNum);
|
|
261
|
-
});
|
|
262
|
-
await this.startServiceIfSynched();
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Handles new proven blocks by updating the proven block number
|
|
266
|
-
* @param blocks - A list of proven L2 blocks.
|
|
267
|
-
* @returns Empty promise.
|
|
268
|
-
*/ async handleProvenL2Blocks(blocks) {
|
|
269
|
-
if (!blocks.length) {
|
|
270
|
-
return Promise.resolve();
|
|
271
|
-
}
|
|
272
|
-
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
273
|
-
await this.synchedProvenBlockNumber.set(lastBlockNum);
|
|
274
|
-
this.log.debug(`Synched to proven block ${lastBlockNum}`);
|
|
275
|
-
await this.startServiceIfSynched();
|
|
276
|
-
}
|
|
277
|
-
async handlePruneL2Blocks(latestBlock) {
|
|
278
|
-
const blockHeader = await this.l2BlockSource.getBlockHeader(latestBlock);
|
|
279
|
-
const slotNumber = blockHeader ? blockHeader.globalVariables.slotNumber.toBigInt() : BigInt(0);
|
|
280
|
-
const epochNumber = slotNumber / BigInt(this.config.aztecEpochDuration);
|
|
118
|
+
// I need to get the slot number from the block that was just pruned
|
|
119
|
+
handlePruneL2Blocks(event) {
|
|
120
|
+
const { slotNumber, epochNumber } = event;
|
|
281
121
|
this.log.info(`Detected chain prune. Punishing the validators at epoch ${epochNumber}`);
|
|
282
122
|
// Set the lifetime such that we have a full round that we could vote throughout.
|
|
283
123
|
const slotsIntoRound = slotNumber % BigInt(this.config.slashingRoundSize);
|
|
@@ -288,26 +128,5 @@ import { createPublicClient, fallback, getAddress, getContract, http } from 'vie
|
|
|
288
128
|
amount: this.slashingAmount,
|
|
289
129
|
lifetime
|
|
290
130
|
});
|
|
291
|
-
await this.synchedLatestBlockNumber.set(latestBlock);
|
|
292
|
-
}
|
|
293
|
-
async startServiceIfSynched() {
|
|
294
|
-
const [latestBlock, provenBlock] = await Promise.all([
|
|
295
|
-
this.getSyncedLatestBlockNum(),
|
|
296
|
-
this.getSyncedProvenBlockNum()
|
|
297
|
-
]);
|
|
298
|
-
if (this.currentState === 1 && latestBlock >= this.latestBlockNumberAtStart && provenBlock >= this.provenBlockNumberAtStart) {
|
|
299
|
-
this.log.debug(`Synched to blocks at start`);
|
|
300
|
-
this.setCurrentState(2);
|
|
301
|
-
if (this.syncResolve !== undefined) {
|
|
302
|
-
this.syncResolve();
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Method to set the value of the current state.
|
|
308
|
-
* @param newState - New state value.
|
|
309
|
-
*/ setCurrentState(newState) {
|
|
310
|
-
this.currentState = newState;
|
|
311
|
-
this.log.debug(`Moved to state ${SlasherClientState[this.currentState]}`);
|
|
312
131
|
}
|
|
313
132
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/sequencer-client",
|
|
3
|
-
"version": "0.77.0-testnet-ignition.
|
|
3
|
+
"version": "0.77.0-testnet-ignition.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -28,36 +28,36 @@
|
|
|
28
28
|
"test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --config jest.integration.config.json"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@aztec/aztec.js": "0.77.0-testnet-ignition.
|
|
32
|
-
"@aztec/bb-prover": "0.77.0-testnet-ignition.
|
|
33
|
-
"@aztec/blob-lib": "0.77.0-testnet-ignition.
|
|
34
|
-
"@aztec/blob-sink": "0.77.0-testnet-ignition.
|
|
35
|
-
"@aztec/constants": "0.77.0-testnet-ignition.
|
|
36
|
-
"@aztec/epoch-cache": "0.77.0-testnet-ignition.
|
|
37
|
-
"@aztec/ethereum": "0.77.0-testnet-ignition.
|
|
38
|
-
"@aztec/foundation": "0.77.0-testnet-ignition.
|
|
39
|
-
"@aztec/l1-artifacts": "0.77.0-testnet-ignition.
|
|
40
|
-
"@aztec/merkle-tree": "0.77.0-testnet-ignition.
|
|
41
|
-
"@aztec/noir-contracts.js": "0.77.0-testnet-ignition.
|
|
42
|
-
"@aztec/noir-protocol-circuits-types": "0.77.0-testnet-ignition.
|
|
43
|
-
"@aztec/p2p": "0.77.0-testnet-ignition.
|
|
44
|
-
"@aztec/protocol-contracts": "0.77.0-testnet-ignition.
|
|
45
|
-
"@aztec/prover-client": "0.77.0-testnet-ignition.
|
|
46
|
-
"@aztec/simulator": "0.77.0-testnet-ignition.
|
|
47
|
-
"@aztec/stdlib": "0.77.0-testnet-ignition.
|
|
48
|
-
"@aztec/telemetry-client": "0.77.0-testnet-ignition.
|
|
49
|
-
"@aztec/validator-client": "0.77.0-testnet-ignition.
|
|
50
|
-
"@aztec/world-state": "0.77.0-testnet-ignition.
|
|
31
|
+
"@aztec/aztec.js": "0.77.0-testnet-ignition.23",
|
|
32
|
+
"@aztec/bb-prover": "0.77.0-testnet-ignition.23",
|
|
33
|
+
"@aztec/blob-lib": "0.77.0-testnet-ignition.23",
|
|
34
|
+
"@aztec/blob-sink": "0.77.0-testnet-ignition.23",
|
|
35
|
+
"@aztec/constants": "0.77.0-testnet-ignition.23",
|
|
36
|
+
"@aztec/epoch-cache": "0.77.0-testnet-ignition.23",
|
|
37
|
+
"@aztec/ethereum": "0.77.0-testnet-ignition.23",
|
|
38
|
+
"@aztec/foundation": "0.77.0-testnet-ignition.23",
|
|
39
|
+
"@aztec/l1-artifacts": "0.77.0-testnet-ignition.23",
|
|
40
|
+
"@aztec/merkle-tree": "0.77.0-testnet-ignition.23",
|
|
41
|
+
"@aztec/noir-contracts.js": "0.77.0-testnet-ignition.23",
|
|
42
|
+
"@aztec/noir-protocol-circuits-types": "0.77.0-testnet-ignition.23",
|
|
43
|
+
"@aztec/p2p": "0.77.0-testnet-ignition.23",
|
|
44
|
+
"@aztec/protocol-contracts": "0.77.0-testnet-ignition.23",
|
|
45
|
+
"@aztec/prover-client": "0.77.0-testnet-ignition.23",
|
|
46
|
+
"@aztec/simulator": "0.77.0-testnet-ignition.23",
|
|
47
|
+
"@aztec/stdlib": "0.77.0-testnet-ignition.23",
|
|
48
|
+
"@aztec/telemetry-client": "0.77.0-testnet-ignition.23",
|
|
49
|
+
"@aztec/validator-client": "0.77.0-testnet-ignition.23",
|
|
50
|
+
"@aztec/world-state": "0.77.0-testnet-ignition.23",
|
|
51
51
|
"lodash.chunk": "^4.2.0",
|
|
52
52
|
"lodash.pick": "^4.4.0",
|
|
53
53
|
"tslib": "^2.4.0",
|
|
54
54
|
"viem": "2.22.8",
|
|
55
|
-
"@aztec/noir-acvm_js": "0.77.0-testnet-ignition.
|
|
56
|
-
"@aztec/noir-types": "0.77.0-testnet-ignition.
|
|
55
|
+
"@aztec/noir-acvm_js": "0.77.0-testnet-ignition.23",
|
|
56
|
+
"@aztec/noir-types": "0.77.0-testnet-ignition.23"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@aztec/archiver": "
|
|
60
|
-
"@aztec/kv-store": "
|
|
59
|
+
"@aztec/archiver": "0.77.0-testnet-ignition.23",
|
|
60
|
+
"@aztec/kv-store": "0.77.0-testnet-ignition.23",
|
|
61
61
|
"@jest/globals": "^29.5.0",
|
|
62
62
|
"@types/jest": "^29.5.0",
|
|
63
63
|
"@types/levelup": "^5.1.2",
|
|
@@ -193,7 +193,7 @@ export class Sequencer {
|
|
|
193
193
|
this.log.debug(`Stopping sequencer`);
|
|
194
194
|
await this.validatorClient?.stop();
|
|
195
195
|
await this.runningPromise?.stop();
|
|
196
|
-
|
|
196
|
+
this.slasherClient.stop();
|
|
197
197
|
this.publisher.interrupt();
|
|
198
198
|
this.setState(SequencerState.STOPPED, 0n, true /** force */);
|
|
199
199
|
this.log.info('Stopped sequencer');
|
package/src/slasher/factory.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import type { L1ContractsConfig, L1ReaderConfig } from '@aztec/ethereum';
|
|
2
|
-
import {
|
|
3
|
-
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
4
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
5
|
-
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
6
|
-
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
2
|
+
import type { L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
7
3
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
8
4
|
|
|
9
5
|
import { SlasherClient } from './slasher_client.js';
|
|
10
6
|
import type { SlasherConfig } from './slasher_client.js';
|
|
11
7
|
|
|
12
|
-
export const createSlasherClient =
|
|
13
|
-
_config: SlasherConfig &
|
|
14
|
-
l2BlockSource:
|
|
8
|
+
export const createSlasherClient = (
|
|
9
|
+
_config: SlasherConfig & L1ContractsConfig & L1ReaderConfig,
|
|
10
|
+
l2BlockSource: L2BlockSourceEventEmitter,
|
|
15
11
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
16
|
-
deps: { store?: AztecAsyncKVStore } = {},
|
|
17
12
|
) => {
|
|
18
13
|
const config = { ..._config };
|
|
19
|
-
|
|
20
|
-
return new SlasherClient(config, store, l2BlockSource, telemetry);
|
|
14
|
+
return new SlasherClient(config, l2BlockSource, telemetry);
|
|
21
15
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
1
|
import {
|
|
3
2
|
type L1ContractsConfig,
|
|
4
3
|
type L1ReaderConfig,
|
|
@@ -7,15 +6,12 @@ import {
|
|
|
7
6
|
} from '@aztec/ethereum';
|
|
8
7
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
9
8
|
import { createLogger } from '@aztec/foundation/log';
|
|
10
|
-
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton } from '@aztec/kv-store';
|
|
11
9
|
import { SlashFactoryAbi } from '@aztec/l1-artifacts';
|
|
12
10
|
import {
|
|
13
|
-
type L2Block,
|
|
14
11
|
type L2BlockId,
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type L2Tips,
|
|
12
|
+
type L2BlockSourceEvent,
|
|
13
|
+
type L2BlockSourceEventEmitter,
|
|
14
|
+
L2BlockSourceEvents,
|
|
19
15
|
} from '@aztec/stdlib/block';
|
|
20
16
|
import { type TelemetryClient, WithTracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
21
17
|
|
|
@@ -26,7 +22,6 @@ import { type GetContractReturnType, createPublicClient, fallback, getAddress, g
|
|
|
26
22
|
*/
|
|
27
23
|
export enum SlasherClientState {
|
|
28
24
|
IDLE,
|
|
29
|
-
SYNCHING,
|
|
30
25
|
RUNNING,
|
|
31
26
|
STOPPED,
|
|
32
27
|
}
|
|
@@ -82,18 +77,6 @@ type SlashEvent = {
|
|
|
82
77
|
* slashing only the first, because the "lifetime" of the second would have passed after that vote
|
|
83
78
|
*/
|
|
84
79
|
export class SlasherClient extends WithTracer {
|
|
85
|
-
private currentState = SlasherClientState.IDLE;
|
|
86
|
-
private syncPromise = Promise.resolve();
|
|
87
|
-
private syncResolve?: () => void = undefined;
|
|
88
|
-
private latestBlockNumberAtStart = -1;
|
|
89
|
-
private provenBlockNumberAtStart = -1;
|
|
90
|
-
|
|
91
|
-
private synchedBlockHashes: AztecAsyncMap<number, string>;
|
|
92
|
-
private synchedLatestBlockNumber: AztecAsyncSingleton<number>;
|
|
93
|
-
private synchedProvenBlockNumber: AztecAsyncSingleton<number>;
|
|
94
|
-
|
|
95
|
-
private blockStream;
|
|
96
|
-
|
|
97
80
|
private slashEvents: SlashEvent[] = [];
|
|
98
81
|
|
|
99
82
|
protected slashFactoryContract?: GetContractReturnType<typeof SlashFactoryAbi, ViemPublicClient> = undefined;
|
|
@@ -105,22 +88,12 @@ export class SlasherClient extends WithTracer {
|
|
|
105
88
|
|
|
106
89
|
constructor(
|
|
107
90
|
private config: SlasherConfig & L1ContractsConfig & L1ReaderConfig,
|
|
108
|
-
private
|
|
109
|
-
private l2BlockSource: L2BlockSource,
|
|
91
|
+
private l2BlockSource: L2BlockSourceEventEmitter,
|
|
110
92
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
111
93
|
private log = createLogger('slasher'),
|
|
112
94
|
) {
|
|
113
95
|
super(telemetry, 'slasher');
|
|
114
96
|
|
|
115
|
-
this.blockStream = new L2BlockStream(l2BlockSource, this, this, createLogger('slasher:block_stream'), {
|
|
116
|
-
batchSize: config.blockRequestBatchSize,
|
|
117
|
-
pollIntervalMS: config.blockCheckIntervalMS,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
this.synchedBlockHashes = store.openMap('slasher_block_hashes');
|
|
121
|
-
this.synchedLatestBlockNumber = store.openSingleton('slasher_last_l2_block');
|
|
122
|
-
this.synchedProvenBlockNumber = store.openSingleton('slasher_last_proven_l2_block');
|
|
123
|
-
|
|
124
97
|
if (config.l1Contracts.slashFactoryAddress && config.l1Contracts.slashFactoryAddress !== EthAddress.ZERO) {
|
|
125
98
|
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
126
99
|
const publicClient = createPublicClient({
|
|
@@ -141,6 +114,11 @@ export class SlasherClient extends WithTracer {
|
|
|
141
114
|
this.log.info(`Slasher client initialized`);
|
|
142
115
|
}
|
|
143
116
|
|
|
117
|
+
public start() {
|
|
118
|
+
this.log.info('Starting Slasher client...');
|
|
119
|
+
this.l2BlockSource.on(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
|
|
120
|
+
}
|
|
121
|
+
|
|
144
122
|
// This is where we should put a bunch of the improvements mentioned earlier.
|
|
145
123
|
public async getSlashPayload(slotNumber: bigint): Promise<EthAddress | undefined> {
|
|
146
124
|
if (!this.slashFactoryContract) {
|
|
@@ -172,198 +150,32 @@ export class SlasherClient extends WithTracer {
|
|
|
172
150
|
return EthAddress.fromString(payloadAddress);
|
|
173
151
|
}
|
|
174
152
|
|
|
175
|
-
public
|
|
176
|
-
return this.synchedBlockHashes.getAsync(number);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
public async getL2Tips(): Promise<L2Tips> {
|
|
180
|
-
const latestBlockNumber = await this.getSyncedLatestBlockNum();
|
|
181
|
-
let latestBlockHash: string | undefined;
|
|
182
|
-
const provenBlockNumber = await this.getSyncedProvenBlockNum();
|
|
183
|
-
let provenBlockHash: string | undefined;
|
|
184
|
-
|
|
185
|
-
if (latestBlockNumber > 0) {
|
|
186
|
-
latestBlockHash = await this.synchedBlockHashes.getAsync(latestBlockNumber);
|
|
187
|
-
if (typeof latestBlockHash === 'undefined') {
|
|
188
|
-
this.log.warn(`Block hash for latest block ${latestBlockNumber} not found`);
|
|
189
|
-
throw new Error();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (provenBlockNumber > 0) {
|
|
194
|
-
provenBlockHash = await this.synchedBlockHashes.getAsync(provenBlockNumber);
|
|
195
|
-
if (typeof provenBlockHash === 'undefined') {
|
|
196
|
-
this.log.warn(`Block hash for proven block ${provenBlockNumber} not found`);
|
|
197
|
-
throw new Error();
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return Promise.resolve({
|
|
202
|
-
latest: { hash: latestBlockHash!, number: latestBlockNumber },
|
|
203
|
-
proven: { hash: provenBlockHash!, number: provenBlockNumber },
|
|
204
|
-
finalized: { hash: provenBlockHash!, number: provenBlockNumber },
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
153
|
+
public handleBlockStreamEvent(event: L2BlockSourceEvent): Promise<void> {
|
|
209
154
|
this.log.debug(`Handling block stream event ${event.type}`);
|
|
210
155
|
switch (event.type) {
|
|
211
|
-
case
|
|
212
|
-
|
|
213
|
-
break;
|
|
214
|
-
case 'chain-finalized':
|
|
215
|
-
// TODO (alexg): I think we can prune the block hashes map here
|
|
216
|
-
break;
|
|
217
|
-
case 'chain-proven': {
|
|
218
|
-
const from = (await this.getSyncedProvenBlockNum()) + 1;
|
|
219
|
-
const limit = event.blockNumber - from + 1;
|
|
220
|
-
await this.handleProvenL2Blocks(await this.l2BlockSource.getBlocks(from, limit));
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
case 'chain-pruned':
|
|
224
|
-
await this.handlePruneL2Blocks(event.blockNumber);
|
|
156
|
+
case L2BlockSourceEvents.L2PruneDetected:
|
|
157
|
+
this.handlePruneL2Blocks(event);
|
|
225
158
|
break;
|
|
226
159
|
default: {
|
|
227
|
-
const _: never = event;
|
|
228
160
|
break;
|
|
229
161
|
}
|
|
230
162
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
public async start() {
|
|
234
|
-
if (this.currentState === SlasherClientState.STOPPED) {
|
|
235
|
-
throw new Error('Slasher already stopped');
|
|
236
|
-
}
|
|
237
|
-
if (this.currentState !== SlasherClientState.IDLE) {
|
|
238
|
-
return this.syncPromise;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// get the current latest block numbers
|
|
242
|
-
this.latestBlockNumberAtStart = await this.l2BlockSource.getBlockNumber();
|
|
243
|
-
this.provenBlockNumberAtStart = await this.l2BlockSource.getProvenBlockNumber();
|
|
244
|
-
|
|
245
|
-
const syncedLatestBlock = (await this.getSyncedLatestBlockNum()) + 1;
|
|
246
|
-
const syncedProvenBlock = (await this.getSyncedProvenBlockNum()) + 1;
|
|
247
|
-
|
|
248
|
-
// if there are blocks to be retrieved, go to a synching state
|
|
249
|
-
if (syncedLatestBlock <= this.latestBlockNumberAtStart || syncedProvenBlock <= this.provenBlockNumberAtStart) {
|
|
250
|
-
this.setCurrentState(SlasherClientState.SYNCHING);
|
|
251
|
-
this.syncPromise = new Promise(resolve => {
|
|
252
|
-
this.syncResolve = resolve;
|
|
253
|
-
});
|
|
254
|
-
this.log.verbose(`Starting sync from ${syncedLatestBlock} (last proven ${syncedProvenBlock})`);
|
|
255
|
-
} else {
|
|
256
|
-
// if no blocks to be retrieved, go straight to running
|
|
257
|
-
this.setCurrentState(SlasherClientState.RUNNING);
|
|
258
|
-
this.syncPromise = Promise.resolve();
|
|
259
|
-
this.log.verbose(`Block ${syncedLatestBlock} (proven ${syncedProvenBlock}) already beyond current block`);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
this.blockStream.start();
|
|
263
|
-
this.log.verbose(`Started block downloader from block ${syncedLatestBlock}`);
|
|
264
|
-
|
|
265
|
-
return this.syncPromise;
|
|
163
|
+
return Promise.resolve();
|
|
266
164
|
}
|
|
267
165
|
|
|
268
166
|
/**
|
|
269
167
|
* Allows consumers to stop the instance of the slasher client.
|
|
270
168
|
* 'ready' will now return 'false' and the running promise that keeps the client synced is interrupted.
|
|
271
169
|
*/
|
|
272
|
-
public
|
|
170
|
+
public stop() {
|
|
273
171
|
this.log.debug('Stopping Slasher client...');
|
|
274
|
-
|
|
275
|
-
this.log.debug('Stopped block downloader');
|
|
276
|
-
await this.store.close();
|
|
277
|
-
this.log.debug('Stopped slasher store');
|
|
278
|
-
this.setCurrentState(SlasherClientState.STOPPED);
|
|
172
|
+
this.l2BlockSource.removeListener(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
|
|
279
173
|
this.log.info('Slasher client stopped.');
|
|
280
174
|
}
|
|
281
175
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
*/
|
|
286
|
-
public isReady() {
|
|
287
|
-
return this.currentState === SlasherClientState.RUNNING;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Public function to check the latest block number that the slasher client is synced to.
|
|
292
|
-
* @returns Block number of latest L2 Block we've synced with.
|
|
293
|
-
*/
|
|
294
|
-
public async getSyncedLatestBlockNum(): Promise<number> {
|
|
295
|
-
return (await this.synchedLatestBlockNumber.getAsync()) ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Public function to check the latest proven block number that the slasher client is synced to.
|
|
300
|
-
* @returns Block number of latest proven L2 Block we've synced with.
|
|
301
|
-
*/
|
|
302
|
-
public async getSyncedProvenBlockNum(): Promise<number> {
|
|
303
|
-
return (await this.synchedProvenBlockNumber.getAsync()) ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Method to check the status of the slasher client.
|
|
308
|
-
* @returns Information about slasher client status: state & syncedToBlockNum.
|
|
309
|
-
*/
|
|
310
|
-
public async getStatus(): Promise<SlasherSyncState> {
|
|
311
|
-
const blockNumber = await this.getSyncedLatestBlockNum();
|
|
312
|
-
const blockHash =
|
|
313
|
-
blockNumber == 0
|
|
314
|
-
? ''
|
|
315
|
-
: await this.l2BlockSource
|
|
316
|
-
.getBlockHeader(blockNumber)
|
|
317
|
-
.then(header => header?.hash())
|
|
318
|
-
.then(hash => hash?.toString());
|
|
319
|
-
return Promise.resolve({
|
|
320
|
-
state: this.currentState,
|
|
321
|
-
syncedToL2Block: { number: blockNumber, hash: blockHash },
|
|
322
|
-
} as SlasherSyncState);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Handles new blocks
|
|
327
|
-
* @param blocks - A list of blocks that the slasher client needs to store block hashes for
|
|
328
|
-
* @returns Empty promise.
|
|
329
|
-
*/
|
|
330
|
-
private async handleLatestL2Blocks(blocks: L2Block[]): Promise<void> {
|
|
331
|
-
if (!blocks.length) {
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
await this.store.transactionAsync(async () => {
|
|
336
|
-
for (const block of blocks) {
|
|
337
|
-
await this.synchedBlockHashes.set(block.number, (await block.hash()).toString());
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
341
|
-
await this.synchedLatestBlockNumber.set(lastBlockNum);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
await this.startServiceIfSynched();
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Handles new proven blocks by updating the proven block number
|
|
349
|
-
* @param blocks - A list of proven L2 blocks.
|
|
350
|
-
* @returns Empty promise.
|
|
351
|
-
*/
|
|
352
|
-
private async handleProvenL2Blocks(blocks: L2Block[]): Promise<void> {
|
|
353
|
-
if (!blocks.length) {
|
|
354
|
-
return Promise.resolve();
|
|
355
|
-
}
|
|
356
|
-
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
357
|
-
await this.synchedProvenBlockNumber.set(lastBlockNum);
|
|
358
|
-
this.log.debug(`Synched to proven block ${lastBlockNum}`);
|
|
359
|
-
|
|
360
|
-
await this.startServiceIfSynched();
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
private async handlePruneL2Blocks(latestBlock: number): Promise<void> {
|
|
364
|
-
const blockHeader = await this.l2BlockSource.getBlockHeader(latestBlock);
|
|
365
|
-
const slotNumber = blockHeader ? blockHeader.globalVariables.slotNumber.toBigInt() : BigInt(0);
|
|
366
|
-
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;
|
|
367
179
|
this.log.info(`Detected chain prune. Punishing the validators at epoch ${epochNumber}`);
|
|
368
180
|
|
|
369
181
|
// Set the lifetime such that we have a full round that we could vote throughout.
|
|
@@ -377,34 +189,5 @@ export class SlasherClient extends WithTracer {
|
|
|
377
189
|
amount: this.slashingAmount,
|
|
378
190
|
lifetime,
|
|
379
191
|
});
|
|
380
|
-
|
|
381
|
-
await this.synchedLatestBlockNumber.set(latestBlock);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
private async startServiceIfSynched() {
|
|
385
|
-
const [latestBlock, provenBlock] = await Promise.all([
|
|
386
|
-
this.getSyncedLatestBlockNum(),
|
|
387
|
-
this.getSyncedProvenBlockNum(),
|
|
388
|
-
]);
|
|
389
|
-
if (
|
|
390
|
-
this.currentState === SlasherClientState.SYNCHING &&
|
|
391
|
-
latestBlock >= this.latestBlockNumberAtStart &&
|
|
392
|
-
provenBlock >= this.provenBlockNumberAtStart
|
|
393
|
-
) {
|
|
394
|
-
this.log.debug(`Synched to blocks at start`);
|
|
395
|
-
this.setCurrentState(SlasherClientState.RUNNING);
|
|
396
|
-
if (this.syncResolve !== undefined) {
|
|
397
|
-
this.syncResolve();
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Method to set the value of the current state.
|
|
404
|
-
* @param newState - New state value.
|
|
405
|
-
*/
|
|
406
|
-
private setCurrentState(newState: SlasherClientState) {
|
|
407
|
-
this.currentState = newState;
|
|
408
|
-
this.log.debug(`Moved to state ${SlasherClientState[this.currentState]}`);
|
|
409
192
|
}
|
|
410
193
|
}
|