@aztec/sequencer-client 0.73.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2
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.js +55 -55
- package/dest/config.js +40 -42
- package/dest/global_variable_builder/global_builder.js +39 -26
- package/dest/global_variable_builder/index.js +0 -1
- package/dest/index.js +0 -1
- package/dest/publisher/config.js +49 -50
- package/dest/publisher/index.js +0 -1
- package/dest/publisher/sequencer-publisher-metrics.js +32 -23
- package/dest/publisher/sequencer-publisher.js +251 -253
- package/dest/sequencer/allowed.js +5 -12
- 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.js +648 -607
- package/dest/sequencer/timetable.js +33 -19
- package/dest/sequencer/utils.js +22 -32
- package/dest/slasher/factory.js +6 -5
- package/dest/slasher/index.js +0 -1
- package/dest/slasher/slasher_client.js +128 -122
- package/dest/test/index.js +4 -1
- package/dest/tx_validator/archive_cache.js +7 -7
- package/dest/tx_validator/gas_validator.js +76 -64
- package/dest/tx_validator/nullifier_cache.js +8 -8
- package/dest/tx_validator/phases_validator.js +27 -19
- package/dest/tx_validator/test_utils.js +1 -2
- package/dest/tx_validator/tx_validator_factory.js +5 -5
- package/package.json +23 -22
- package/src/client/sequencer-client.ts +23 -2
- package/src/config.ts +1 -1
- package/src/publisher/sequencer-publisher.ts +71 -91
- package/src/sequencer/sequencer.ts +29 -13
- package/src/slasher/factory.ts +3 -3
- package/src/slasher/slasher_client.ts +40 -31
- package/dest/client/index.d.ts +0 -2
- package/dest/client/index.d.ts.map +0 -1
- package/dest/client/sequencer-client.d.ts +0 -68
- package/dest/client/sequencer-client.d.ts.map +0 -1
- package/dest/config.d.ts +0 -37
- package/dest/config.d.ts.map +0 -1
- package/dest/global_variable_builder/global_builder.d.ts +0 -29
- package/dest/global_variable_builder/global_builder.d.ts.map +0 -1
- package/dest/global_variable_builder/index.d.ts +0 -2
- package/dest/global_variable_builder/index.d.ts.map +0 -1
- package/dest/index.d.ts +0 -8
- package/dest/index.d.ts.map +0 -1
- package/dest/publisher/config.d.ts +0 -38
- package/dest/publisher/config.d.ts.map +0 -1
- package/dest/publisher/index.d.ts +0 -2
- package/dest/publisher/index.d.ts.map +0 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +0 -24
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +0 -1
- package/dest/publisher/sequencer-publisher.d.ts +0 -158
- package/dest/publisher/sequencer-publisher.d.ts.map +0 -1
- package/dest/sequencer/allowed.d.ts +0 -3
- package/dest/sequencer/allowed.d.ts.map +0 -1
- package/dest/sequencer/config.d.ts +0 -2
- package/dest/sequencer/config.d.ts.map +0 -1
- package/dest/sequencer/index.d.ts +0 -4
- package/dest/sequencer/index.d.ts.map +0 -1
- package/dest/sequencer/metrics.d.ts +0 -24
- package/dest/sequencer/metrics.d.ts.map +0 -1
- package/dest/sequencer/sequencer.d.ts +0 -172
- package/dest/sequencer/sequencer.d.ts.map +0 -1
- package/dest/sequencer/timetable.d.ts +0 -38
- package/dest/sequencer/timetable.d.ts.map +0 -1
- package/dest/sequencer/utils.d.ts +0 -47
- package/dest/sequencer/utils.d.ts.map +0 -1
- package/dest/slasher/factory.d.ts +0 -11
- package/dest/slasher/factory.d.ts.map +0 -1
- package/dest/slasher/index.d.ts +0 -3
- package/dest/slasher/index.d.ts.map +0 -1
- package/dest/slasher/slasher_client.d.ts +0 -127
- package/dest/slasher/slasher_client.d.ts.map +0 -1
- package/dest/test/index.d.ts +0 -17
- package/dest/test/index.d.ts.map +0 -1
- package/dest/tx_validator/archive_cache.d.ts +0 -14
- package/dest/tx_validator/archive_cache.d.ts.map +0 -1
- package/dest/tx_validator/gas_validator.d.ts +0 -12
- package/dest/tx_validator/gas_validator.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.d.ts +0 -16
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/phases_validator.d.ts +0 -11
- package/dest/tx_validator/phases_validator.d.ts.map +0 -1
- package/dest/tx_validator/test_utils.d.ts +0 -21
- package/dest/tx_validator/test_utils.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -16
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { L2BlockStream
|
|
1
|
+
import { L2BlockStream } from '@aztec/circuit-types';
|
|
2
2
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js/constants';
|
|
3
3
|
import { createEthereumChain } from '@aztec/ethereum';
|
|
4
4
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { SlashFactoryAbi } from '@aztec/l1-artifacts';
|
|
7
7
|
import { WithTracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
8
|
-
import { createPublicClient, getAddress, getContract, http
|
|
8
|
+
import { createPublicClient, getAddress, getContract, http } from 'viem';
|
|
9
9
|
/**
|
|
10
10
|
* Enum defining the possible states of the Slasher client.
|
|
11
|
-
*/
|
|
12
|
-
export var SlasherClientState;
|
|
13
|
-
(function (SlasherClientState) {
|
|
11
|
+
*/ export var SlasherClientState = /*#__PURE__*/ function(SlasherClientState) {
|
|
14
12
|
SlasherClientState[SlasherClientState["IDLE"] = 0] = "IDLE";
|
|
15
13
|
SlasherClientState[SlasherClientState["SYNCHING"] = 1] = "SYNCHING";
|
|
16
14
|
SlasherClientState[SlasherClientState["RUNNING"] = 2] = "RUNNING";
|
|
17
15
|
SlasherClientState[SlasherClientState["STOPPED"] = 3] = "STOPPED";
|
|
18
|
-
|
|
16
|
+
return SlasherClientState;
|
|
17
|
+
}({});
|
|
19
18
|
/**
|
|
20
19
|
* @notice A Hypomeiones slasher client implementation
|
|
21
20
|
*
|
|
@@ -40,28 +39,31 @@ export var SlasherClientState;
|
|
|
40
39
|
* - Decide how to deal with multiple slashing events in the same round.
|
|
41
40
|
* - This could be that multiple epochs are pruned in the same round, but with the current naive implementation we could end up
|
|
42
41
|
* slashing only the first, because the "lifetime" of the second would have passed after that vote
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
42
|
+
*/ export class SlasherClient extends WithTracer {
|
|
43
|
+
config;
|
|
44
|
+
store;
|
|
45
|
+
l2BlockSource;
|
|
46
|
+
log;
|
|
47
|
+
currentState;
|
|
48
|
+
syncPromise;
|
|
49
|
+
syncResolve;
|
|
50
|
+
latestBlockNumberAtStart;
|
|
51
|
+
provenBlockNumberAtStart;
|
|
52
|
+
synchedBlockHashes;
|
|
53
|
+
synchedLatestBlockNumber;
|
|
54
|
+
synchedProvenBlockNumber;
|
|
55
|
+
blockStream;
|
|
56
|
+
slashEvents;
|
|
57
|
+
slashFactoryContract;
|
|
58
|
+
// The amount to slash for a prune.
|
|
59
|
+
// Note that we set it to 0, such that no actual slashing will happen, but the event will be fired,
|
|
60
|
+
// showing that the slashing mechanism is working.
|
|
61
|
+
slashingAmount;
|
|
62
|
+
constructor(config, store, l2BlockSource, telemetry = getTelemetryClient(), log = createLogger('slasher')){
|
|
63
|
+
super(telemetry, 'slasher'), this.config = config, this.store = store, this.l2BlockSource = l2BlockSource, this.log = log, this.currentState = 0, this.syncPromise = Promise.resolve(), this.syncResolve = undefined, this.latestBlockNumberAtStart = -1, this.provenBlockNumberAtStart = -1, this.slashEvents = [], this.slashFactoryContract = undefined, this.slashingAmount = 0n;
|
|
62
64
|
this.blockStream = new L2BlockStream(l2BlockSource, this, this, createLogger('slasher:block_stream'), {
|
|
63
65
|
batchSize: config.blockRequestBatchSize,
|
|
64
|
-
pollIntervalMS: config.blockCheckIntervalMS
|
|
66
|
+
pollIntervalMS: config.blockCheckIntervalMS
|
|
65
67
|
});
|
|
66
68
|
this.synchedBlockHashes = store.openMap('slasher_block_hashes');
|
|
67
69
|
this.synchedLatestBlockNumber = store.openSingleton('slasher_last_l2_block');
|
|
@@ -71,15 +73,14 @@ export class SlasherClient extends WithTracer {
|
|
|
71
73
|
const publicClient = createPublicClient({
|
|
72
74
|
chain: chain.chainInfo,
|
|
73
75
|
transport: http(chain.rpcUrl),
|
|
74
|
-
pollingInterval: config.viemPollingIntervalMS
|
|
76
|
+
pollingInterval: config.viemPollingIntervalMS
|
|
75
77
|
});
|
|
76
78
|
this.slashFactoryContract = getContract({
|
|
77
79
|
address: getAddress(config.l1Contracts.slashFactoryAddress.toString()),
|
|
78
80
|
abi: SlashFactoryAbi,
|
|
79
|
-
client: publicClient
|
|
81
|
+
client: publicClient
|
|
80
82
|
});
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
+
} else {
|
|
83
84
|
this.log.warn('No slash factory address found, slashing will not be enabled');
|
|
84
85
|
}
|
|
85
86
|
this.log.info(`Slasher client initialized`);
|
|
@@ -91,7 +92,7 @@ export class SlasherClient extends WithTracer {
|
|
|
91
92
|
}
|
|
92
93
|
// As long as the slot is greater than the lifetime, we want to keep deleting the first element
|
|
93
94
|
// since it will not make sense to include anymore.
|
|
94
|
-
while
|
|
95
|
+
while(this.slashEvents.length > 0 && this.slashEvents[0].lifetime < slotNumber){
|
|
95
96
|
this.slashEvents.shift();
|
|
96
97
|
}
|
|
97
98
|
if (this.slashEvents.length == 0) {
|
|
@@ -100,7 +101,7 @@ export class SlasherClient extends WithTracer {
|
|
|
100
101
|
const slashEvent = this.slashEvents[0];
|
|
101
102
|
const [payloadAddress, isDeployed] = await this.slashFactoryContract.read.getAddressAndIsDeployed([
|
|
102
103
|
slashEvent.epoch,
|
|
103
|
-
slashEvent.amount
|
|
104
|
+
slashEvent.amount
|
|
104
105
|
]);
|
|
105
106
|
if (!isDeployed) {
|
|
106
107
|
// The proposal cannot be executed until it is deployed
|
|
@@ -109,80 +110,89 @@ export class SlasherClient extends WithTracer {
|
|
|
109
110
|
return EthAddress.fromString(payloadAddress);
|
|
110
111
|
}
|
|
111
112
|
getL2BlockHash(number) {
|
|
112
|
-
return
|
|
113
|
+
return this.synchedBlockHashes.getAsync(number);
|
|
113
114
|
}
|
|
114
|
-
getL2Tips() {
|
|
115
|
-
const latestBlockNumber = this.getSyncedLatestBlockNum();
|
|
115
|
+
async getL2Tips() {
|
|
116
|
+
const latestBlockNumber = await this.getSyncedLatestBlockNum();
|
|
116
117
|
let latestBlockHash;
|
|
117
|
-
const provenBlockNumber = this.getSyncedProvenBlockNum();
|
|
118
|
+
const provenBlockNumber = await this.getSyncedProvenBlockNum();
|
|
118
119
|
let provenBlockHash;
|
|
119
120
|
if (latestBlockNumber > 0) {
|
|
120
|
-
latestBlockHash = this.synchedBlockHashes.
|
|
121
|
+
latestBlockHash = await this.synchedBlockHashes.getAsync(latestBlockNumber);
|
|
121
122
|
if (typeof latestBlockHash === 'undefined') {
|
|
122
123
|
this.log.warn(`Block hash for latest block ${latestBlockNumber} not found`);
|
|
123
124
|
throw new Error();
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
if (provenBlockNumber > 0) {
|
|
127
|
-
provenBlockHash = this.synchedBlockHashes.
|
|
128
|
+
provenBlockHash = await this.synchedBlockHashes.getAsync(provenBlockNumber);
|
|
128
129
|
if (typeof provenBlockHash === 'undefined') {
|
|
129
130
|
this.log.warn(`Block hash for proven block ${provenBlockNumber} not found`);
|
|
130
131
|
throw new Error();
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
return Promise.resolve({
|
|
134
|
-
latest: {
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
147
|
});
|
|
138
148
|
}
|
|
139
149
|
async handleBlockStreamEvent(event) {
|
|
140
150
|
this.log.debug(`Handling block stream event ${event.type}`);
|
|
141
|
-
switch
|
|
151
|
+
switch(event.type){
|
|
142
152
|
case 'blocks-added':
|
|
143
153
|
await this.handleLatestL2Blocks(event.blocks);
|
|
144
154
|
break;
|
|
145
155
|
case 'chain-finalized':
|
|
146
|
-
// TODO (alexg): I think we can prune the block hashes map here
|
|
147
156
|
break;
|
|
148
|
-
case 'chain-proven':
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
+
}
|
|
154
164
|
case 'chain-pruned':
|
|
155
165
|
await this.handlePruneL2Blocks(event.blockNumber);
|
|
156
166
|
break;
|
|
157
|
-
default:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
default:
|
|
168
|
+
{
|
|
169
|
+
const _ = event;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
161
172
|
}
|
|
162
173
|
}
|
|
163
174
|
async start() {
|
|
164
|
-
if (this.currentState ===
|
|
175
|
+
if (this.currentState === 3) {
|
|
165
176
|
throw new Error('Slasher already stopped');
|
|
166
177
|
}
|
|
167
|
-
if (this.currentState !==
|
|
178
|
+
if (this.currentState !== 0) {
|
|
168
179
|
return this.syncPromise;
|
|
169
180
|
}
|
|
170
181
|
// get the current latest block numbers
|
|
171
182
|
this.latestBlockNumberAtStart = await this.l2BlockSource.getBlockNumber();
|
|
172
183
|
this.provenBlockNumberAtStart = await this.l2BlockSource.getProvenBlockNumber();
|
|
173
|
-
const syncedLatestBlock = this.getSyncedLatestBlockNum() + 1;
|
|
174
|
-
const syncedProvenBlock = this.getSyncedProvenBlockNum() + 1;
|
|
184
|
+
const syncedLatestBlock = await this.getSyncedLatestBlockNum() + 1;
|
|
185
|
+
const syncedProvenBlock = await this.getSyncedProvenBlockNum() + 1;
|
|
175
186
|
// if there are blocks to be retrieved, go to a synching state
|
|
176
187
|
if (syncedLatestBlock <= this.latestBlockNumberAtStart || syncedProvenBlock <= this.provenBlockNumberAtStart) {
|
|
177
|
-
this.setCurrentState(
|
|
178
|
-
this.syncPromise = new Promise(resolve
|
|
188
|
+
this.setCurrentState(1);
|
|
189
|
+
this.syncPromise = new Promise((resolve)=>{
|
|
179
190
|
this.syncResolve = resolve;
|
|
180
191
|
});
|
|
181
192
|
this.log.verbose(`Starting sync from ${syncedLatestBlock} (last proven ${syncedProvenBlock})`);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
193
|
+
} else {
|
|
184
194
|
// if no blocks to be retrieved, go straight to running
|
|
185
|
-
this.setCurrentState(
|
|
195
|
+
this.setCurrentState(2);
|
|
186
196
|
this.syncPromise = Promise.resolve();
|
|
187
197
|
this.log.verbose(`Block ${syncedLatestBlock} (proven ${syncedProvenBlock}) already beyond current block`);
|
|
188
198
|
}
|
|
@@ -191,82 +201,78 @@ export class SlasherClient extends WithTracer {
|
|
|
191
201
|
return this.syncPromise;
|
|
192
202
|
}
|
|
193
203
|
/**
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
async stop() {
|
|
204
|
+
* Allows consumers to stop the instance of the slasher client.
|
|
205
|
+
* 'ready' will now return 'false' and the running promise that keeps the client synced is interrupted.
|
|
206
|
+
*/ async stop() {
|
|
198
207
|
this.log.debug('Stopping Slasher client...');
|
|
199
208
|
await this.blockStream.stop();
|
|
200
209
|
this.log.debug('Stopped block downloader');
|
|
201
|
-
this.
|
|
210
|
+
await this.store.close();
|
|
211
|
+
this.log.debug('Stopped slasher store');
|
|
212
|
+
this.setCurrentState(3);
|
|
202
213
|
this.log.info('Slasher client stopped.');
|
|
203
214
|
}
|
|
204
215
|
/**
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return this.currentState === SlasherClientState.RUNNING;
|
|
216
|
+
* Public function to check if the slasher client is fully synced and ready to receive txs.
|
|
217
|
+
* @returns True if the slasher client is ready to receive txs.
|
|
218
|
+
*/ isReady() {
|
|
219
|
+
return this.currentState === 2;
|
|
210
220
|
}
|
|
211
221
|
/**
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return this.synchedLatestBlockNumber.get() ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
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;
|
|
217
226
|
}
|
|
218
227
|
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return this.synchedProvenBlockNumber.get() ?? INITIAL_L2_BLOCK_NUM - 1;
|
|
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;
|
|
224
232
|
}
|
|
225
233
|
/**
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
const blockHash = blockNumber == 0
|
|
232
|
-
? ''
|
|
233
|
-
: await this.l2BlockSource
|
|
234
|
-
.getBlockHeader(blockNumber)
|
|
235
|
-
.then(header => header?.hash())
|
|
236
|
-
.then(hash => hash?.toString());
|
|
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());
|
|
237
239
|
return Promise.resolve({
|
|
238
240
|
state: this.currentState,
|
|
239
|
-
syncedToL2Block: {
|
|
241
|
+
syncedToL2Block: {
|
|
242
|
+
number: blockNumber,
|
|
243
|
+
hash: blockHash
|
|
244
|
+
}
|
|
240
245
|
});
|
|
241
246
|
}
|
|
242
247
|
/**
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
async handleLatestL2Blocks(blocks) {
|
|
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) {
|
|
248
252
|
if (!blocks.length) {
|
|
249
|
-
return
|
|
253
|
+
return;
|
|
250
254
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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();
|
|
256
263
|
}
|
|
257
264
|
/**
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
async handleProvenL2Blocks(blocks) {
|
|
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) {
|
|
263
269
|
if (!blocks.length) {
|
|
264
270
|
return Promise.resolve();
|
|
265
271
|
}
|
|
266
272
|
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
267
273
|
await this.synchedProvenBlockNumber.set(lastBlockNum);
|
|
268
274
|
this.log.debug(`Synched to proven block ${lastBlockNum}`);
|
|
269
|
-
this.startServiceIfSynched();
|
|
275
|
+
await this.startServiceIfSynched();
|
|
270
276
|
}
|
|
271
277
|
async handlePruneL2Blocks(latestBlock) {
|
|
272
278
|
const blockHeader = await this.l2BlockSource.getBlockHeader(latestBlock);
|
|
@@ -280,28 +286,28 @@ export class SlasherClient extends WithTracer {
|
|
|
280
286
|
this.slashEvents.push({
|
|
281
287
|
epoch: epochNumber,
|
|
282
288
|
amount: this.slashingAmount,
|
|
283
|
-
lifetime
|
|
289
|
+
lifetime
|
|
284
290
|
});
|
|
285
291
|
await this.synchedLatestBlockNumber.set(latestBlock);
|
|
286
292
|
}
|
|
287
|
-
startServiceIfSynched() {
|
|
288
|
-
|
|
289
|
-
this.getSyncedLatestBlockNum()
|
|
290
|
-
this.getSyncedProvenBlockNum()
|
|
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) {
|
|
291
299
|
this.log.debug(`Synched to blocks at start`);
|
|
292
|
-
this.setCurrentState(
|
|
300
|
+
this.setCurrentState(2);
|
|
293
301
|
if (this.syncResolve !== undefined) {
|
|
294
302
|
this.syncResolve();
|
|
295
303
|
}
|
|
296
304
|
}
|
|
297
305
|
}
|
|
298
306
|
/**
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
setCurrentState(newState) {
|
|
307
|
+
* Method to set the value of the current state.
|
|
308
|
+
* @param newState - New state value.
|
|
309
|
+
*/ setCurrentState(newState) {
|
|
303
310
|
this.currentState = newState;
|
|
304
311
|
this.log.debug(`Moved to state ${SlasherClientState[this.currentState]}`);
|
|
305
312
|
}
|
|
306
313
|
}
|
|
307
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2xhc2hlcl9jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2xhc2hlci9zbGFzaGVyX2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBSUwsYUFBYSxHQUdkLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDcEUsT0FBTyxFQUErQyxtQkFBbUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ25HLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUMzRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFckQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3RELE9BQU8sRUFBd0IsVUFBVSxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFL0YsT0FBTyxFQUtMLGtCQUFrQixFQUNsQixVQUFVLEVBQ1YsV0FBVyxFQUNYLElBQUksR0FDTCxNQUFNLE1BQU0sQ0FBQztBQUVkOztHQUVHO0FBQ0gsTUFBTSxDQUFOLElBQVksa0JBS1g7QUFMRCxXQUFZLGtCQUFrQjtJQUM1QiwyREFBSSxDQUFBO0lBQ0osbUVBQVEsQ0FBQTtJQUNSLGlFQUFPLENBQUE7SUFDUCxpRUFBTyxDQUFBO0FBQ1QsQ0FBQyxFQUxXLGtCQUFrQixLQUFsQixrQkFBa0IsUUFLN0I7QUEyQkQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUNILE1BQU0sT0FBTyxhQUFjLFNBQVEsVUFBVTtJQXVCM0MsWUFDVSxNQUEwRCxFQUMxRCxLQUFtQixFQUNuQixhQUE0QixFQUNwQyxZQUE2QixrQkFBa0IsRUFBRSxFQUN6QyxNQUFNLFlBQVksQ0FBQyxTQUFTLENBQUM7UUFFckMsS0FBSyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQztRQU5wQixXQUFNLEdBQU4s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
|
package/dest/test/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { SequencerClient } from '../client/sequencer-client.js';
|
|
2
2
|
import { Sequencer } from '../sequencer/sequencer.js';
|
|
3
3
|
class TestSequencer_ extends Sequencer {
|
|
4
|
+
publicProcessorFactory;
|
|
5
|
+
timetable;
|
|
6
|
+
publisher;
|
|
4
7
|
}
|
|
5
8
|
class TestSequencerClient_ extends SequencerClient {
|
|
9
|
+
sequencer;
|
|
6
10
|
}
|
|
7
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFaEUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBR3RELE1BQU0sY0FBZSxTQUFRLFNBQVM7Q0FJckM7QUFJRCxNQUFNLG9CQUFxQixTQUFRLGVBQWU7Q0FFakQifQ==
|
|
@@ -2,21 +2,21 @@ import { MerkleTreeId } from '@aztec/circuit-types';
|
|
|
2
2
|
/**
|
|
3
3
|
* Implements an archive source by checking a DB and an in-memory collection.
|
|
4
4
|
* Intended for validating transactions as they are added to a block.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
*/ export class ArchiveCache {
|
|
6
|
+
db;
|
|
7
|
+
archives;
|
|
8
|
+
constructor(db){
|
|
8
9
|
this.db = db;
|
|
9
10
|
this.archives = new Map();
|
|
10
11
|
}
|
|
11
12
|
async getArchiveIndices(archives) {
|
|
12
|
-
const toCheckDb = archives.filter(n
|
|
13
|
+
const toCheckDb = archives.filter((n)=>!this.archives.has(n.toString()));
|
|
13
14
|
const dbHits = await this.db.findLeafIndices(MerkleTreeId.ARCHIVE, toCheckDb);
|
|
14
|
-
dbHits.forEach((x, index)
|
|
15
|
+
dbHits.forEach((x, index)=>{
|
|
15
16
|
if (x !== undefined) {
|
|
16
17
|
this.archives.set(toCheckDb[index].toString(), x);
|
|
17
18
|
}
|
|
18
19
|
});
|
|
19
|
-
return archives.map(n
|
|
20
|
+
return archives.map((n)=>this.archives.get(n.toString()));
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXJjaGl2ZV9jYWNoZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvYXJjaGl2ZV9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFpQyxNQUFNLHNCQUFzQixDQUFDO0FBSW5GOzs7R0FHRztBQUNILE1BQU0sT0FBTyxZQUFZO0lBR3ZCLFlBQW9CLEVBQTRCO1FBQTVCLE9BQUUsR0FBRixFQUFFLENBQTBCO1FBQzlDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDNUMsQ0FBQztJQUVNLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxRQUFjO1FBQzNDLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDekUsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDLGVBQWUsQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQzlFLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLEVBQUU7WUFDMUIsSUFBSSxDQUFDLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3BCLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNwRCxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFSCxPQUFPLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzVELENBQUM7Q0FDRiJ9
|
|
@@ -1,78 +1,90 @@
|
|
|
1
|
-
var _GasTxValidator_instances, _GasTxValidator_log, _GasTxValidator_publicDataSource, _GasTxValidator_feeJuiceAddress, _GasTxValidator_enforceFees, _GasTxValidator_gasFees, _GasTxValidator_shouldSkip, _GasTxValidator_validateTxFee;
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
1
|
import { TxExecutionPhase } from '@aztec/circuit-types';
|
|
4
2
|
import { Fr, FunctionSelector } from '@aztec/circuits.js';
|
|
5
3
|
import { U128 } from '@aztec/foundation/abi';
|
|
6
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
7
5
|
import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator/server';
|
|
8
6
|
export class GasTxValidator {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
__classPrivateFieldSet(this, _GasTxValidator_gasFees, gasFees, "f");
|
|
7
|
+
#log = createLogger('sequencer:tx_validator:tx_gas');
|
|
8
|
+
#publicDataSource;
|
|
9
|
+
#feeJuiceAddress;
|
|
10
|
+
#enforceFees;
|
|
11
|
+
#gasFees;
|
|
12
|
+
constructor(publicDataSource, feeJuiceAddress, enforceFees, gasFees){
|
|
13
|
+
this.#publicDataSource = publicDataSource;
|
|
14
|
+
this.#feeJuiceAddress = feeJuiceAddress;
|
|
15
|
+
this.#enforceFees = enforceFees;
|
|
16
|
+
this.#gasFees = gasFees;
|
|
20
17
|
}
|
|
21
18
|
validateTx(tx) {
|
|
22
|
-
if (
|
|
23
|
-
return Promise.resolve({
|
|
19
|
+
if (this.#shouldSkip(tx)) {
|
|
20
|
+
return Promise.resolve({
|
|
21
|
+
result: 'skipped',
|
|
22
|
+
reason: [
|
|
23
|
+
'Insufficient fee per gas'
|
|
24
|
+
]
|
|
25
|
+
});
|
|
24
26
|
}
|
|
25
|
-
return
|
|
27
|
+
return this.#validateTxFee(tx);
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Check whether the tx's max fees are valid for the current block, and skip if not.
|
|
31
|
+
* We skip instead of invalidating since the tx may become elligible later.
|
|
32
|
+
* Note that circuits check max fees even if fee payer is unset, so we
|
|
33
|
+
* keep this validation even if the tx does not pay fees.
|
|
34
|
+
*/ #shouldSkip(tx) {
|
|
35
|
+
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
36
|
+
// Skip the tx if its max fees are not enough for the current block's gas fees.
|
|
37
|
+
const maxFeesPerGas = gasSettings.maxFeesPerGas;
|
|
38
|
+
const notEnoughMaxFees = maxFeesPerGas.feePerDaGas.lt(this.#gasFees.feePerDaGas) || maxFeesPerGas.feePerL2Gas.lt(this.#gasFees.feePerL2Gas);
|
|
39
|
+
if (notEnoughMaxFees) {
|
|
40
|
+
this.#log.warn(`Skipping transaction ${tx.getTxHash()} due to insufficient fee per gas`);
|
|
41
|
+
}
|
|
42
|
+
return notEnoughMaxFees;
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
async #validateTxFee(tx) {
|
|
45
|
+
const feePayer = tx.data.feePayer;
|
|
46
|
+
// TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx
|
|
47
|
+
if (feePayer.isZero()) {
|
|
48
|
+
if (this.#enforceFees) {
|
|
49
|
+
this.#log.warn(`Rejecting transaction ${tx.getTxHash()} due to missing fee payer`);
|
|
50
|
+
return {
|
|
51
|
+
result: 'invalid',
|
|
52
|
+
reason: [
|
|
53
|
+
'Missing fee payer'
|
|
54
|
+
]
|
|
55
|
+
};
|
|
56
|
+
} else {
|
|
57
|
+
return {
|
|
58
|
+
result: 'valid'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
45
61
|
}
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
// Compute the maximum fee that this tx may pay, based on its gasLimits and maxFeePerGas
|
|
63
|
+
const feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit();
|
|
64
|
+
// Read current balance of the feePayer
|
|
65
|
+
const initialBalance = await this.#publicDataSource.storageRead(this.#feeJuiceAddress, await computeFeePayerBalanceStorageSlot(feePayer));
|
|
66
|
+
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
67
|
+
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
68
|
+
const increasePublicBalanceSelector = await FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))');
|
|
69
|
+
const claimFunctionCall = setupFns.find((fn)=>fn.callContext.contractAddress.equals(this.#feeJuiceAddress) && fn.callContext.msgSender.equals(this.#feeJuiceAddress) && fn.args.length > 2 && // Public functions get routed through the dispatch function, whose first argument is the target function selector.
|
|
70
|
+
fn.args[0].equals(increasePublicBalanceSelector.toField()) && fn.args[1].equals(feePayer.toField()) && !fn.callContext.isStaticCall);
|
|
71
|
+
// `amount` in the claim function call arguments occupies 2 fields as it is represented as U128.
|
|
72
|
+
const balance = claimFunctionCall ? initialBalance.add(new Fr(U128.fromFields(claimFunctionCall.args.slice(2, 4)).toInteger())) : initialBalance;
|
|
73
|
+
if (balance.lt(feeLimit)) {
|
|
74
|
+
this.#log.warn(`Rejecting transaction due to not enough fee payer balance`, {
|
|
75
|
+
feePayer,
|
|
76
|
+
balance: balance.toBigInt(),
|
|
77
|
+
feeLimit: feeLimit.toBigInt()
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
result: 'invalid',
|
|
81
|
+
reason: [
|
|
82
|
+
'Insufficient fee payer balance'
|
|
83
|
+
]
|
|
84
|
+
};
|
|
48
85
|
}
|
|
86
|
+
return {
|
|
87
|
+
result: 'valid'
|
|
88
|
+
};
|
|
49
89
|
}
|
|
50
|
-
|
|
51
|
-
const feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit();
|
|
52
|
-
// Read current balance of the feePayer
|
|
53
|
-
const initialBalance = await __classPrivateFieldGet(this, _GasTxValidator_publicDataSource, "f").storageRead(__classPrivateFieldGet(this, _GasTxValidator_feeJuiceAddress, "f"), await computeFeePayerBalanceStorageSlot(feePayer));
|
|
54
|
-
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
55
|
-
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
56
|
-
const increasePublicBalanceSelector = await FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))');
|
|
57
|
-
const claimFunctionCall = setupFns.find(fn => fn.callContext.contractAddress.equals(__classPrivateFieldGet(this, _GasTxValidator_feeJuiceAddress, "f")) &&
|
|
58
|
-
fn.callContext.msgSender.equals(__classPrivateFieldGet(this, _GasTxValidator_feeJuiceAddress, "f")) &&
|
|
59
|
-
fn.args.length > 2 &&
|
|
60
|
-
// Public functions get routed through the dispatch function, whose first argument is the target function selector.
|
|
61
|
-
fn.args[0].equals(increasePublicBalanceSelector.toField()) &&
|
|
62
|
-
fn.args[1].equals(feePayer.toField()) &&
|
|
63
|
-
!fn.callContext.isStaticCall);
|
|
64
|
-
// `amount` in the claim function call arguments occupies 2 fields as it is represented as U128.
|
|
65
|
-
const balance = claimFunctionCall
|
|
66
|
-
? initialBalance.add(new Fr(U128.fromFields(claimFunctionCall.args.slice(2, 4)).toInteger()))
|
|
67
|
-
: initialBalance;
|
|
68
|
-
if (balance.lt(feeLimit)) {
|
|
69
|
-
__classPrivateFieldGet(this, _GasTxValidator_log, "f").warn(`Rejecting transaction due to not enough fee payer balance`, {
|
|
70
|
-
feePayer,
|
|
71
|
-
balance: balance.toBigInt(),
|
|
72
|
-
feeLimit: feeLimit.toBigInt(),
|
|
73
|
-
});
|
|
74
|
-
return { result: 'invalid', reason: ['Insufficient fee payer balance'] };
|
|
75
|
-
}
|
|
76
|
-
return { result: 'valid' };
|
|
77
|
-
};
|
|
78
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FzX3ZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvZ2FzX3ZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE9BQU8sRUFBVyxnQkFBZ0IsRUFBNkMsTUFBTSxzQkFBc0IsQ0FBQztBQUM1RyxPQUFPLEVBQXFCLEVBQUUsRUFBRSxnQkFBZ0IsRUFBZ0IsTUFBTSxvQkFBb0IsQ0FBQztBQUMzRixPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDN0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3JELE9BQU8sRUFBRSxpQ0FBaUMsRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBT3pHLE1BQU0sT0FBTyxjQUFjO0lBT3pCLFlBQ0UsZ0JBQW1DLEVBQ25DLGVBQTZCLEVBQzdCLFdBQW9CLEVBQ3BCLE9BQWdCOztRQVZsQiw4QkFBTyxZQUFZLENBQUMsK0JBQStCLENBQUMsRUFBQztRQUNyRCxtREFBcUM7UUFDckMsa0RBQStCO1FBQy9CLDhDQUFzQjtRQUN0QiwwQ0FBa0I7UUFRaEIsdUJBQUEsSUFBSSxvQ0FBcUIsZ0JBQWdCLE1BQUEsQ0FBQztRQUMxQyx1QkFBQSxJQUFJLG1DQUFvQixlQUFlLE1BQUEsQ0FBQztRQUN4Qyx1QkFBQSxJQUFJLCtCQUFnQixXQUFXLE1BQUEsQ0FBQztRQUNoQyx1QkFBQSxJQUFJLDJCQUFZLE9BQU8sTUFBQSxDQUFDO0lBQzFCLENBQUM7SUFFRCxVQUFVLENBQUMsRUFBTTtRQUNmLElBQUksdUJBQUEsSUFBSSw2REFBWSxNQUFoQixJQUFJLEVBQWEsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN6QixPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxDQUFDLDBCQUEwQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3RG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
|
|
90
|
+
}
|