@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,46 +1,60 @@
|
|
|
1
|
-
|
|
1
|
+
import { Blob } from '@aztec/blob-lib';
|
|
2
2
|
import { createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
3
|
-
import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload
|
|
3
|
+
import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload } from '@aztec/circuit-types';
|
|
4
4
|
import { EthAddress } from '@aztec/circuits.js';
|
|
5
|
-
import { FormattedViemError,
|
|
5
|
+
import { FormattedViemError, RollupContract, formatViemError } from '@aztec/ethereum';
|
|
6
6
|
import { toHex } from '@aztec/foundation/bigint-buffer';
|
|
7
|
-
import { Blob } from '@aztec/foundation/blob';
|
|
8
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
9
8
|
import { Timer } from '@aztec/foundation/timer';
|
|
10
|
-
import {
|
|
9
|
+
import { ForwarderAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
11
10
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
12
11
|
import pick from 'lodash.pick';
|
|
13
|
-
import { encodeFunctionData
|
|
12
|
+
import { encodeFunctionData } from 'viem';
|
|
14
13
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
15
|
-
export var VoteType
|
|
16
|
-
(function (VoteType) {
|
|
14
|
+
export var VoteType = /*#__PURE__*/ function(VoteType) {
|
|
17
15
|
VoteType[VoteType["GOVERNANCE"] = 0] = "GOVERNANCE";
|
|
18
16
|
VoteType[VoteType["SLASHING"] = 1] = "SLASHING";
|
|
19
|
-
|
|
17
|
+
return VoteType;
|
|
18
|
+
}({});
|
|
20
19
|
export class SequencerPublisher {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
interrupted = false;
|
|
21
|
+
metrics;
|
|
22
|
+
epochCache;
|
|
23
|
+
forwarderContract;
|
|
24
|
+
governanceLog = createLogger('sequencer:publisher:governance');
|
|
25
|
+
governanceProposerAddress;
|
|
26
|
+
governancePayload = EthAddress.ZERO;
|
|
27
|
+
slashingLog = createLogger('sequencer:publisher:slashing');
|
|
28
|
+
slashingProposerAddress;
|
|
29
|
+
getSlashPayload = undefined;
|
|
30
|
+
myLastVotes = {
|
|
31
|
+
[0]: 0n,
|
|
32
|
+
[1]: 0n
|
|
33
|
+
};
|
|
34
|
+
log = createLogger('sequencer:publisher');
|
|
35
|
+
ethereumSlotDuration;
|
|
36
|
+
blobSinkClient;
|
|
37
|
+
// @note - with blobs, the below estimate seems too large.
|
|
38
|
+
// Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
|
|
39
|
+
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
40
|
+
static PROPOSE_GAS_GUESS = 12_000_000n;
|
|
41
|
+
static PROPOSE_AND_CLAIM_GAS_GUESS = this.PROPOSE_GAS_GUESS + 100_000n;
|
|
42
|
+
l1TxUtils;
|
|
43
|
+
rollupContract;
|
|
44
|
+
govProposerContract;
|
|
45
|
+
slashingProposerContract;
|
|
46
|
+
requests = [];
|
|
47
|
+
constructor(config, deps){
|
|
33
48
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
34
49
|
this.epochCache = deps.epochCache;
|
|
35
|
-
if (config.l1Contracts.governanceProposerAddress) {
|
|
36
|
-
this.governanceProposerAddress = EthAddress.fromString(config.l1Contracts.governanceProposerAddress.toString());
|
|
37
|
-
}
|
|
38
50
|
this.blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config);
|
|
39
51
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
40
52
|
this.metrics = new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
41
53
|
this.l1TxUtils = deps.l1TxUtils;
|
|
42
54
|
this.rollupContract = deps.rollupContract;
|
|
43
55
|
this.forwarderContract = deps.forwarderContract;
|
|
56
|
+
this.govProposerContract = deps.governanceProposerContract;
|
|
57
|
+
this.slashingProposerContract = deps.slashingProposerContract;
|
|
44
58
|
}
|
|
45
59
|
registerSlashPayloadGetter(callback) {
|
|
46
60
|
this.getSlashPayload = callback;
|
|
@@ -64,31 +78,32 @@ export class SequencerPublisher {
|
|
|
64
78
|
return this.epochCache.getEpochAndSlotNow().slot;
|
|
65
79
|
}
|
|
66
80
|
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
* Sends all requests that are still valid.
|
|
82
|
+
* @returns one of:
|
|
83
|
+
* - A receipt and stats if the tx succeeded
|
|
84
|
+
* - a receipt and errorMsg if it failed on L1
|
|
85
|
+
* - undefined if no valid requests are found OR the tx failed to send.
|
|
86
|
+
*/ async sendRequests() {
|
|
87
|
+
const requestsToProcess = [
|
|
88
|
+
...this.requests
|
|
89
|
+
];
|
|
75
90
|
this.requests = [];
|
|
76
91
|
if (this.interrupted) {
|
|
77
92
|
return undefined;
|
|
78
93
|
}
|
|
79
94
|
const currentL2Slot = this.getCurrentL2Slot();
|
|
80
95
|
this.log.debug(`Current L2 slot: ${currentL2Slot}`);
|
|
81
|
-
const validRequests = requestsToProcess.filter(request
|
|
96
|
+
const validRequests = requestsToProcess.filter((request)=>request.lastValidL2Slot >= currentL2Slot);
|
|
82
97
|
if (validRequests.length !== requestsToProcess.length) {
|
|
83
98
|
this.log.warn(`Some requests were expired for slot ${currentL2Slot}`, {
|
|
84
|
-
validRequests: validRequests.map(request
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
requests: requestsToProcess.map(request
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
validRequests: validRequests.map((request)=>({
|
|
100
|
+
action: request.action,
|
|
101
|
+
lastValidL2Slot: request.lastValidL2Slot
|
|
102
|
+
})),
|
|
103
|
+
requests: requestsToProcess.map((request)=>({
|
|
104
|
+
action: request.action,
|
|
105
|
+
lastValidL2Slot: request.lastValidL2Slot
|
|
106
|
+
}))
|
|
92
107
|
});
|
|
93
108
|
}
|
|
94
109
|
if (validRequests.length === 0) {
|
|
@@ -98,8 +113,8 @@ export class SequencerPublisher {
|
|
|
98
113
|
// @note - we can only have one gas config and one blob config per bundle
|
|
99
114
|
// find requests with gas and blob configs
|
|
100
115
|
// See https://github.com/AztecProtocol/aztec-packages/issues/11513
|
|
101
|
-
const gasConfigs = requestsToProcess.filter(request
|
|
102
|
-
const blobConfigs = requestsToProcess.filter(request
|
|
116
|
+
const gasConfigs = requestsToProcess.filter((request)=>request.gasConfig);
|
|
117
|
+
const blobConfigs = requestsToProcess.filter((request)=>request.blobConfig);
|
|
103
118
|
if (gasConfigs.length > 1 || blobConfigs.length > 1) {
|
|
104
119
|
throw new Error('Multiple gas or blob configs found');
|
|
105
120
|
}
|
|
@@ -107,96 +122,97 @@ export class SequencerPublisher {
|
|
|
107
122
|
const blobConfig = blobConfigs[0]?.blobConfig;
|
|
108
123
|
try {
|
|
109
124
|
this.log.debug('Forwarding transactions', {
|
|
110
|
-
validRequests: validRequests.map(request
|
|
125
|
+
validRequests: validRequests.map((request)=>request.action)
|
|
111
126
|
});
|
|
112
|
-
const result = await this.forwarderContract.forward(validRequests.map(request
|
|
127
|
+
const result = await this.forwarderContract.forward(validRequests.map((request)=>request.request), this.l1TxUtils, gasConfig, blobConfig, this.log);
|
|
113
128
|
this.callbackBundledTransactions(validRequests, result);
|
|
114
129
|
return result;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.log.error(`Failed to publish bundled transactions`, message, { metaMessages });
|
|
130
|
+
} catch (err) {
|
|
131
|
+
const viemError = formatViemError(err);
|
|
132
|
+
this.log.error(`Failed to publish bundled transactions`, viemError);
|
|
119
133
|
return undefined;
|
|
120
134
|
}
|
|
121
135
|
}
|
|
122
136
|
callbackBundledTransactions(requests, result) {
|
|
123
137
|
const success = result?.receipt.status === 'success';
|
|
124
138
|
const logger = success ? this.log.info : this.log.error;
|
|
125
|
-
for (const request of requests)
|
|
139
|
+
for (const request of requests){
|
|
126
140
|
logger(`Bundled [${request.action}] transaction [${success ? 'succeeded' : 'failed'}]`);
|
|
127
141
|
request.onResult?.(request.request, result);
|
|
128
142
|
}
|
|
129
143
|
}
|
|
130
144
|
/**
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
* @notice Will call `canProposeAtNextEthBlock` to make sure that it is possible to propose
|
|
146
|
+
* @param tipArchive - The archive to check
|
|
147
|
+
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
148
|
+
*/ canProposeAtNextEthBlock(tipArchive) {
|
|
149
|
+
const ignoredErrors = [
|
|
150
|
+
'SlotAlreadyInChain',
|
|
151
|
+
'InvalidProposer',
|
|
152
|
+
'InvalidArchive'
|
|
153
|
+
];
|
|
154
|
+
return this.rollupContract.canProposeAtNextEthBlock(tipArchive, this.getForwarderAddress().toString(), this.ethereumSlotDuration).catch((err)=>{
|
|
155
|
+
if (err instanceof FormattedViemError && ignoredErrors.find((e)=>err.message.includes(e))) {
|
|
141
156
|
this.log.debug(err.message);
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
157
|
+
} else {
|
|
144
158
|
this.log.error(err.name, err);
|
|
145
159
|
}
|
|
146
160
|
return undefined;
|
|
147
161
|
});
|
|
148
162
|
}
|
|
149
163
|
/**
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
* @returns The epoch that is currently claimable, undefined otherwise
|
|
165
|
+
*/ getClaimableEpoch() {
|
|
166
|
+
const acceptedErrors = [
|
|
167
|
+
'Rollup__NoEpochToProve',
|
|
168
|
+
'Rollup__ProofRightAlreadyClaimed'
|
|
169
|
+
];
|
|
170
|
+
return this.rollupContract.getClaimableEpoch().catch((err)=>{
|
|
171
|
+
if (acceptedErrors.find((e)=>err.message.includes(e))) {
|
|
156
172
|
return undefined;
|
|
157
173
|
}
|
|
158
174
|
throw err;
|
|
159
175
|
});
|
|
160
176
|
}
|
|
161
177
|
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return Promise.all(quotes.map(x => this.rollupContract
|
|
168
|
-
// validate throws if the quote is not valid
|
|
178
|
+
* @notice Will filter out invalid quotes according to L1
|
|
179
|
+
* @param quotes - The quotes to filter
|
|
180
|
+
* @returns The filtered quotes
|
|
181
|
+
*/ filterValidQuotes(quotes) {
|
|
182
|
+
return Promise.all(quotes.map((x)=>this.rollupContract// validate throws if the quote is not valid
|
|
169
183
|
// else returns void
|
|
170
|
-
.validateProofQuote(x.toViemArgs(), this.getForwarderAddress().toString(), this.ethereumSlotDuration)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
.validateProofQuote(x.toViemArgs(), this.getForwarderAddress().toString(), this.ethereumSlotDuration).then(()=>x).catch((err)=>{
|
|
185
|
+
this.log.error(`Failed to validate proof quote`, err, {
|
|
186
|
+
quote: x.toInspect()
|
|
187
|
+
});
|
|
188
|
+
return undefined;
|
|
189
|
+
}))).then((quotes)=>quotes.filter((q)=>!!q));
|
|
176
190
|
}
|
|
177
191
|
/**
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
async validateBlockForSubmission(header, attestationData = {
|
|
192
|
+
* @notice Will call `validateHeader` to make sure that it is possible to propose
|
|
193
|
+
*
|
|
194
|
+
* @dev Throws if unable to propose
|
|
195
|
+
*
|
|
196
|
+
* @param header - The header to propose
|
|
197
|
+
* @param digest - The digest that attestations are signing over
|
|
198
|
+
*
|
|
199
|
+
*/ async validateBlockForSubmission(header, attestationData = {
|
|
187
200
|
digest: Buffer.alloc(32),
|
|
188
|
-
signatures: []
|
|
201
|
+
signatures: []
|
|
189
202
|
}) {
|
|
190
203
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
191
|
-
const formattedSignatures = attestationData.signatures.map(attest
|
|
192
|
-
const flags = {
|
|
204
|
+
const formattedSignatures = attestationData.signatures.map((attest)=>attest.toViemSignature());
|
|
205
|
+
const flags = {
|
|
206
|
+
ignoreDA: true,
|
|
207
|
+
ignoreSignatures: formattedSignatures.length == 0
|
|
208
|
+
};
|
|
193
209
|
const args = [
|
|
194
210
|
`0x${header.toBuffer().toString('hex')}`,
|
|
195
211
|
formattedSignatures,
|
|
196
212
|
`0x${attestationData.digest.toString('hex')}`,
|
|
197
213
|
ts,
|
|
198
214
|
`0x${header.contentCommitment.blobsHash.toString('hex')}`,
|
|
199
|
-
flags
|
|
215
|
+
flags
|
|
200
216
|
];
|
|
201
217
|
await this.rollupContract.validateHeader(args, this.getForwarderAddress().toString());
|
|
202
218
|
return ts;
|
|
@@ -205,106 +221,81 @@ export class SequencerPublisher {
|
|
|
205
221
|
const committee = await this.rollupContract.getCurrentEpochCommittee();
|
|
206
222
|
return committee.map(EthAddress.fromString);
|
|
207
223
|
}
|
|
208
|
-
|
|
209
|
-
* Enqueues a castVote transaction to cast a vote for a given slot number.
|
|
210
|
-
* @param slotNumber - The slot number to cast a vote for.
|
|
211
|
-
* @param timestamp - The timestamp of the slot to cast a vote for.
|
|
212
|
-
* @param voteType - The type of vote to cast.
|
|
213
|
-
* @returns True if the vote was successfully enqueued, false otherwise.
|
|
214
|
-
*/
|
|
215
|
-
async enqueueCastVote(slotNumber, timestamp, voteType) {
|
|
216
|
-
// @todo This function can be optimized by doing some of the computations locally instead of calling the L1 contracts
|
|
224
|
+
async enqueueCastVoteHelper(slotNumber, timestamp, voteType, payload, base) {
|
|
217
225
|
if (this.myLastVotes[voteType] >= slotNumber) {
|
|
218
226
|
return false;
|
|
219
227
|
}
|
|
220
|
-
|
|
221
|
-
if (voteType === VoteType.GOVERNANCE) {
|
|
222
|
-
if (this.governancePayload.equals(EthAddress.ZERO)) {
|
|
223
|
-
return undefined;
|
|
224
|
-
}
|
|
225
|
-
if (!this.governanceProposerAddress) {
|
|
226
|
-
return undefined;
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
payload: this.governancePayload,
|
|
230
|
-
voteContractAddress: this.governanceProposerAddress,
|
|
231
|
-
logger: this.governanceLog,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
else if (voteType === VoteType.SLASHING) {
|
|
235
|
-
if (!this.getSlashPayload) {
|
|
236
|
-
return undefined;
|
|
237
|
-
}
|
|
238
|
-
const slashingProposerAddress = await this.rollupContract.getSlashingProposerAddress();
|
|
239
|
-
if (!slashingProposerAddress) {
|
|
240
|
-
return undefined;
|
|
241
|
-
}
|
|
242
|
-
const slashPayload = await this.getSlashPayload(slotNumber);
|
|
243
|
-
if (!slashPayload) {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
payload: slashPayload,
|
|
248
|
-
voteContractAddress: slashingProposerAddress,
|
|
249
|
-
logger: this.slashingLog,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
throw new Error('Invalid vote type');
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
const vConfig = await voteConfig();
|
|
257
|
-
if (!vConfig) {
|
|
228
|
+
if (payload.equals(EthAddress.ZERO)) {
|
|
258
229
|
return false;
|
|
259
230
|
}
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
address: getAddress(voteContractAddress.toString()),
|
|
263
|
-
abi: EmpireBaseAbi,
|
|
264
|
-
client: this.l1TxUtils.walletClient,
|
|
265
|
-
});
|
|
266
|
-
const [proposer, roundNumber] = await Promise.all([
|
|
231
|
+
const round = await base.computeRound(slotNumber);
|
|
232
|
+
const [proposer, roundInfo] = await Promise.all([
|
|
267
233
|
this.rollupContract.getProposerAt(timestamp),
|
|
268
|
-
|
|
234
|
+
base.getRoundInfo(this.rollupContract.address, round)
|
|
269
235
|
]);
|
|
270
236
|
if (proposer.toLowerCase() !== this.getForwarderAddress().toString().toLowerCase()) {
|
|
271
237
|
return false;
|
|
272
238
|
}
|
|
273
|
-
|
|
274
|
-
if (slotForLastVote >= slotNumber) {
|
|
239
|
+
if (roundInfo.lastVote >= slotNumber) {
|
|
275
240
|
return false;
|
|
276
241
|
}
|
|
277
242
|
const cachedLastVote = this.myLastVotes[voteType];
|
|
278
243
|
this.myLastVotes[voteType] = slotNumber;
|
|
279
244
|
this.addRequest({
|
|
280
|
-
action: voteType ===
|
|
281
|
-
request:
|
|
282
|
-
to: voteContractAddress.toString(),
|
|
283
|
-
data: encodeFunctionData({
|
|
284
|
-
abi: EmpireBaseAbi,
|
|
285
|
-
functionName: 'vote',
|
|
286
|
-
args: [payload.toString()],
|
|
287
|
-
}),
|
|
288
|
-
},
|
|
245
|
+
action: voteType === 0 ? 'governance-vote' : 'slashing-vote',
|
|
246
|
+
request: base.createVoteRequest(payload.toString()),
|
|
289
247
|
lastValidL2Slot: slotNumber,
|
|
290
|
-
onResult: (_request, result)
|
|
248
|
+
onResult: (_request, result)=>{
|
|
291
249
|
if (!result || result.receipt.status !== 'success') {
|
|
292
250
|
this.myLastVotes[voteType] = cachedLastVote;
|
|
251
|
+
} else {
|
|
252
|
+
this.log.info(`Cast [${voteType}] vote for slot ${slotNumber}`);
|
|
293
253
|
}
|
|
294
|
-
|
|
295
|
-
this.log.info(`Cast ${voteType} vote for slot ${slotNumber}`);
|
|
296
|
-
}
|
|
297
|
-
},
|
|
254
|
+
}
|
|
298
255
|
});
|
|
299
256
|
return true;
|
|
300
257
|
}
|
|
258
|
+
async getVoteConfig(slotNumber, voteType) {
|
|
259
|
+
if (voteType === 0) {
|
|
260
|
+
return {
|
|
261
|
+
payload: this.governancePayload,
|
|
262
|
+
base: this.govProposerContract
|
|
263
|
+
};
|
|
264
|
+
} else if (voteType === 1) {
|
|
265
|
+
if (!this.getSlashPayload) {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
const slashPayload = await this.getSlashPayload(slotNumber);
|
|
269
|
+
if (!slashPayload) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
payload: slashPayload,
|
|
274
|
+
base: this.slashingProposerContract
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
throw new Error('Unreachable: Invalid vote type');
|
|
278
|
+
}
|
|
301
279
|
/**
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
280
|
+
* Enqueues a castVote transaction to cast a vote for a given slot number.
|
|
281
|
+
* @param slotNumber - The slot number to cast a vote for.
|
|
282
|
+
* @param timestamp - The timestamp of the slot to cast a vote for.
|
|
283
|
+
* @param voteType - The type of vote to cast.
|
|
284
|
+
* @returns True if the vote was successfully enqueued, false otherwise.
|
|
285
|
+
*/ async enqueueCastVote(slotNumber, timestamp, voteType) {
|
|
286
|
+
const voteConfig = await this.getVoteConfig(slotNumber, voteType);
|
|
287
|
+
if (!voteConfig) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
const { payload, base } = voteConfig;
|
|
291
|
+
return this.enqueueCastVoteHelper(slotNumber, timestamp, voteType, payload, base);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Proposes a L2 block on L1.
|
|
295
|
+
*
|
|
296
|
+
* @param block - L2 block to propose.
|
|
297
|
+
* @returns True if the tx has been enqueued, throws otherwise. See #9315
|
|
298
|
+
*/ async enqueueProposeL2Block(block, attestations, txHashes, opts = {}) {
|
|
308
299
|
const consensusPayload = new ConsensusPayload(block.header, block.archive.root, txHashes ?? []);
|
|
309
300
|
const digest = await getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
|
|
310
301
|
const blobs = await Blob.getBlobs(block.body.toBlobFields());
|
|
@@ -315,7 +306,7 @@ export class SequencerPublisher {
|
|
|
315
306
|
body: block.body.toBuffer(),
|
|
316
307
|
blobs,
|
|
317
308
|
attestations,
|
|
318
|
-
txHashes: txHashes ?? []
|
|
309
|
+
txHashes: txHashes ?? []
|
|
319
310
|
};
|
|
320
311
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
321
312
|
// This means that we can avoid the simulation issues in later checks.
|
|
@@ -323,14 +314,13 @@ export class SequencerPublisher {
|
|
|
323
314
|
// make time consistency checks break.
|
|
324
315
|
const ts = await this.validateBlockForSubmission(block.header, {
|
|
325
316
|
digest: digest.toBuffer(),
|
|
326
|
-
signatures: attestations ?? []
|
|
317
|
+
signatures: attestations ?? []
|
|
327
318
|
});
|
|
328
319
|
this.log.debug(`Submitting propose transaction`);
|
|
329
320
|
await this.addProposeTx(block, proposeTxArgs, opts, ts);
|
|
330
321
|
return true;
|
|
331
322
|
}
|
|
332
|
-
/** Enqueues a claimEpochProofRight transaction to submit a chosen prover quote for the previous epoch. */
|
|
333
|
-
enqueueClaimEpochProofRight(proofQuote) {
|
|
323
|
+
/** Enqueues a claimEpochProofRight transaction to submit a chosen prover quote for the previous epoch. */ enqueueClaimEpochProofRight(proofQuote) {
|
|
334
324
|
const timer = new Timer();
|
|
335
325
|
this.addRequest({
|
|
336
326
|
action: 'claim',
|
|
@@ -339,11 +329,13 @@ export class SequencerPublisher {
|
|
|
339
329
|
data: encodeFunctionData({
|
|
340
330
|
abi: RollupAbi,
|
|
341
331
|
functionName: 'claimEpochProofRight',
|
|
342
|
-
args: [
|
|
343
|
-
|
|
332
|
+
args: [
|
|
333
|
+
proofQuote.toViemArgs()
|
|
334
|
+
]
|
|
335
|
+
})
|
|
344
336
|
},
|
|
345
337
|
lastValidL2Slot: this.getCurrentL2Slot(),
|
|
346
|
-
onResult: (_request, result)
|
|
338
|
+
onResult: (_request, result)=>{
|
|
347
339
|
if (!result) {
|
|
348
340
|
return;
|
|
349
341
|
}
|
|
@@ -355,66 +347,65 @@ export class SequencerPublisher {
|
|
|
355
347
|
transactionHash: receipt.transactionHash,
|
|
356
348
|
blobDataGas: 0n,
|
|
357
349
|
blobGasUsed: 0n,
|
|
358
|
-
...pick(stats, 'calldataGas', 'calldataSize', 'sender')
|
|
350
|
+
...pick(stats, 'calldataGas', 'calldataSize', 'sender')
|
|
359
351
|
};
|
|
360
352
|
this.log.verbose(`Submitted claim epoch proof right to L1 rollup contract`, {
|
|
361
353
|
...publishStats,
|
|
362
|
-
...proofQuote.toInspect()
|
|
354
|
+
...proofQuote.toInspect()
|
|
363
355
|
});
|
|
364
356
|
this.metrics.recordClaimEpochProofRightTx(timer.ms(), publishStats);
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
357
|
+
} else {
|
|
367
358
|
this.metrics.recordFailedTx('claimEpochProofRight');
|
|
368
359
|
// TODO: Get the error message from the reverted tx
|
|
369
360
|
this.log.error(`Claim epoch proof right tx reverted`, {
|
|
370
361
|
txHash: receipt.transactionHash,
|
|
371
|
-
...proofQuote.toInspect()
|
|
362
|
+
...proofQuote.toInspect()
|
|
372
363
|
});
|
|
373
364
|
}
|
|
374
|
-
}
|
|
365
|
+
}
|
|
375
366
|
});
|
|
376
367
|
return true;
|
|
377
368
|
}
|
|
378
369
|
/**
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
interrupt() {
|
|
370
|
+
* Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
|
|
371
|
+
* Be warned, the call may return false even if the tx subsequently gets successfully mined.
|
|
372
|
+
* In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
|
|
373
|
+
* A call to `restart` is required before you can continue publishing.
|
|
374
|
+
*/ interrupt() {
|
|
385
375
|
this.interrupted = true;
|
|
386
376
|
this.l1TxUtils.interrupt();
|
|
387
377
|
}
|
|
388
|
-
/** Restarts the publisher after calling `interrupt`. */
|
|
389
|
-
restart() {
|
|
378
|
+
/** Restarts the publisher after calling `interrupt`. */ restart() {
|
|
390
379
|
this.interrupted = false;
|
|
391
380
|
this.l1TxUtils.restart();
|
|
392
381
|
}
|
|
393
382
|
async prepareProposeTx(encodedData, timestamp) {
|
|
394
383
|
const kzg = Blob.getViemKzgInstance();
|
|
395
384
|
const blobInput = Blob.getEthBlobEvaluationInputs(encodedData.blobs);
|
|
396
|
-
this.log.debug('Validating blob input', {
|
|
397
|
-
|
|
398
|
-
|
|
385
|
+
this.log.debug('Validating blob input', {
|
|
386
|
+
blobInput
|
|
387
|
+
});
|
|
388
|
+
const blobEvaluationGas = await this.l1TxUtils.estimateGas(this.l1TxUtils.walletClient.account, {
|
|
399
389
|
to: this.rollupContract.address,
|
|
400
390
|
data: encodeFunctionData({
|
|
401
391
|
abi: RollupAbi,
|
|
402
392
|
functionName: 'validateBlobs',
|
|
403
|
-
args: [
|
|
404
|
-
|
|
393
|
+
args: [
|
|
394
|
+
blobInput
|
|
395
|
+
]
|
|
396
|
+
})
|
|
405
397
|
}, {}, {
|
|
406
|
-
blobs: encodedData.blobs.map(b
|
|
407
|
-
kzg
|
|
408
|
-
})
|
|
409
|
-
.catch(err => {
|
|
398
|
+
blobs: encodedData.blobs.map((b)=>b.data),
|
|
399
|
+
kzg
|
|
400
|
+
}).catch((err)=>{
|
|
410
401
|
const { message, metaMessages } = formatViemError(err);
|
|
411
|
-
this.log.error(`Failed to validate blobs`, message, {
|
|
402
|
+
this.log.error(`Failed to validate blobs`, message, {
|
|
403
|
+
metaMessages
|
|
404
|
+
});
|
|
412
405
|
throw new Error('Failed to validate blobs');
|
|
413
406
|
});
|
|
414
|
-
const attestations = encodedData.attestations
|
|
415
|
-
|
|
416
|
-
: [];
|
|
417
|
-
const txHashes = encodedData.txHashes ? encodedData.txHashes.map(txHash => txHash.toString()) : [];
|
|
407
|
+
const attestations = encodedData.attestations ? encodedData.attestations.map((attest)=>attest.toViemSignature()) : [];
|
|
408
|
+
const txHashes = encodedData.txHashes ? encodedData.txHashes.map((txHash)=>txHash.toString()) : [];
|
|
418
409
|
const args = [
|
|
419
410
|
{
|
|
420
411
|
header: `0x${encodedData.header.toString('hex')}`,
|
|
@@ -422,79 +413,92 @@ export class SequencerPublisher {
|
|
|
422
413
|
oracleInput: {
|
|
423
414
|
// We are currently not modifying these. See #9963
|
|
424
415
|
feeAssetPriceModifier: 0n,
|
|
425
|
-
provingCostModifier: 0n
|
|
416
|
+
provingCostModifier: 0n
|
|
426
417
|
},
|
|
427
418
|
blockHash: `0x${encodedData.blockHash.toString('hex')}`,
|
|
428
|
-
txHashes
|
|
419
|
+
txHashes
|
|
429
420
|
},
|
|
430
421
|
attestations,
|
|
431
422
|
// TODO(#9101): Extract blobs from beacon chain => calldata will only contain what's needed to verify blob and body input can be removed
|
|
432
423
|
`0x${encodedData.body.toString('hex')}`,
|
|
433
|
-
blobInput
|
|
424
|
+
blobInput
|
|
434
425
|
];
|
|
435
426
|
const rollupData = encodeFunctionData({
|
|
436
427
|
abi: RollupAbi,
|
|
437
428
|
functionName: 'propose',
|
|
438
|
-
args
|
|
429
|
+
args
|
|
439
430
|
});
|
|
440
431
|
const forwarderData = encodeFunctionData({
|
|
441
432
|
abi: ForwarderAbi,
|
|
442
433
|
functionName: 'forward',
|
|
443
|
-
args: [
|
|
434
|
+
args: [
|
|
435
|
+
[
|
|
436
|
+
this.rollupContract.address
|
|
437
|
+
],
|
|
438
|
+
[
|
|
439
|
+
rollupData
|
|
440
|
+
]
|
|
441
|
+
]
|
|
444
442
|
});
|
|
445
|
-
const simulationResult = await this.l1TxUtils
|
|
446
|
-
.simulateGasUsed({
|
|
443
|
+
const simulationResult = await this.l1TxUtils.simulateGasUsed({
|
|
447
444
|
to: this.getForwarderAddress().toString(),
|
|
448
445
|
data: forwarderData,
|
|
449
|
-
gas:
|
|
446
|
+
gas: SequencerPublisher.PROPOSE_GAS_GUESS
|
|
450
447
|
}, {
|
|
451
448
|
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
452
449
|
time: timestamp + 1n,
|
|
453
450
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit
|
|
454
|
-
gasLimit:
|
|
451
|
+
gasLimit: SequencerPublisher.PROPOSE_GAS_GUESS * 2n
|
|
455
452
|
}, [
|
|
456
453
|
{
|
|
457
454
|
address: this.rollupContract.address,
|
|
458
455
|
// @note we override checkBlob to false since blobs are not part simulate()
|
|
459
456
|
stateDiff: [
|
|
460
457
|
{
|
|
461
|
-
slot: toHex(
|
|
462
|
-
value: toHex(0n, true)
|
|
463
|
-
}
|
|
464
|
-
]
|
|
465
|
-
}
|
|
458
|
+
slot: toHex(RollupContract.checkBlobStorageSlot, true),
|
|
459
|
+
value: toHex(0n, true)
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
}
|
|
466
463
|
], {
|
|
467
464
|
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
468
|
-
fallbackGasEstimate:
|
|
469
|
-
})
|
|
470
|
-
.catch(err => {
|
|
465
|
+
fallbackGasEstimate: SequencerPublisher.PROPOSE_GAS_GUESS
|
|
466
|
+
}).catch((err)=>{
|
|
471
467
|
const { message, metaMessages } = formatViemError(err);
|
|
472
|
-
this.log.error(`Failed to simulate gas used`, message, {
|
|
468
|
+
this.log.error(`Failed to simulate gas used`, message, {
|
|
469
|
+
metaMessages
|
|
470
|
+
});
|
|
473
471
|
throw new Error('Failed to simulate gas used');
|
|
474
472
|
});
|
|
475
|
-
return {
|
|
473
|
+
return {
|
|
474
|
+
args,
|
|
475
|
+
blobEvaluationGas,
|
|
476
|
+
rollupData,
|
|
477
|
+
simulationResult
|
|
478
|
+
};
|
|
476
479
|
}
|
|
477
480
|
async addProposeTx(block, encodedData, opts = {}, timestamp) {
|
|
478
481
|
const timer = new Timer();
|
|
479
482
|
const kzg = Blob.getViemKzgInstance();
|
|
480
483
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, timestamp);
|
|
481
484
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
485
|
+
const blockHash = await block.hash();
|
|
482
486
|
return this.addRequest({
|
|
483
487
|
action: 'propose',
|
|
484
488
|
request: {
|
|
485
489
|
to: this.rollupContract.address,
|
|
486
|
-
data: rollupData
|
|
490
|
+
data: rollupData
|
|
487
491
|
},
|
|
488
492
|
lastValidL2Slot: block.header.globalVariables.slotNumber.toBigInt(),
|
|
489
493
|
gasConfig: {
|
|
490
494
|
...opts,
|
|
491
|
-
gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas)
|
|
495
|
+
gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas)
|
|
492
496
|
},
|
|
493
497
|
blobConfig: {
|
|
494
|
-
blobs: encodedData.blobs.map(b
|
|
495
|
-
kzg
|
|
498
|
+
blobs: encodedData.blobs.map((b)=>b.data),
|
|
499
|
+
kzg
|
|
496
500
|
},
|
|
497
|
-
onResult: (request, result)
|
|
501
|
+
onResult: (request, result)=>{
|
|
498
502
|
if (!result) {
|
|
499
503
|
return;
|
|
500
504
|
}
|
|
@@ -512,44 +516,38 @@ export class SequencerPublisher {
|
|
|
512
516
|
...block.getStats(),
|
|
513
517
|
eventName: 'rollup-published-to-l1',
|
|
514
518
|
blobCount: encodedData.blobs.length,
|
|
515
|
-
inclusionBlocks
|
|
519
|
+
inclusionBlocks
|
|
516
520
|
};
|
|
517
|
-
this.log.verbose(`Published L2 block to L1 rollup contract`, {
|
|
521
|
+
this.log.verbose(`Published L2 block to L1 rollup contract`, {
|
|
522
|
+
...stats,
|
|
523
|
+
...block.getStats()
|
|
524
|
+
});
|
|
518
525
|
this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
|
|
519
526
|
// Send the blobs to the blob sink
|
|
520
|
-
this.sendBlobsToBlobSink(receipt.blockHash, encodedData.blobs).catch(_err
|
|
527
|
+
this.sendBlobsToBlobSink(receipt.blockHash, encodedData.blobs).catch((_err)=>{
|
|
521
528
|
this.log.error('Failed to send blobs to blob sink');
|
|
522
529
|
});
|
|
523
530
|
return true;
|
|
524
|
-
}
|
|
525
|
-
else {
|
|
531
|
+
} else {
|
|
526
532
|
this.metrics.recordFailedTx('process');
|
|
527
533
|
this.log.error(`Rollup process tx reverted. ${errorMsg ?? 'No error message'}`, undefined, {
|
|
528
534
|
...block.getStats(),
|
|
529
535
|
txHash: receipt.transactionHash,
|
|
530
|
-
blockHash
|
|
531
|
-
slotNumber: block.header.globalVariables.slotNumber.toBigInt()
|
|
536
|
+
blockHash,
|
|
537
|
+
slotNumber: block.header.globalVariables.slotNumber.toBigInt()
|
|
532
538
|
});
|
|
533
539
|
}
|
|
534
|
-
}
|
|
540
|
+
}
|
|
535
541
|
});
|
|
536
542
|
}
|
|
537
543
|
/**
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
sendBlobsToBlobSink(blockHash, blobs) {
|
|
544
|
+
* Send blobs to the blob sink
|
|
545
|
+
*
|
|
546
|
+
* If a blob sink url is configured, then we send blobs to the blob sink
|
|
547
|
+
* - for now we use the blockHash as the identifier for the blobs;
|
|
548
|
+
* In the future this will move to be the beacon block id - which takes a bit more work
|
|
549
|
+
* to calculate and will need to be mocked in e2e tests
|
|
550
|
+
*/ sendBlobsToBlobSink(blockHash, blobs) {
|
|
546
551
|
return this.blobSinkClient.sendBlobsToBlobSink(blockHash, blobs);
|
|
547
552
|
}
|
|
548
553
|
}
|
|
549
|
-
_a = SequencerPublisher;
|
|
550
|
-
// @note - with blobs, the below estimate seems too large.
|
|
551
|
-
// Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
|
|
552
|
-
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
553
|
-
SequencerPublisher.PROPOSE_GAS_GUESS = 12000000n;
|
|
554
|
-
SequencerPublisher.PROPOSE_AND_CLAIM_GAS_GUESS = _a.PROPOSE_GAS_GUESS + 100000n;
|
|
555
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2VyLXB1Ymxpc2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wdWJsaXNoZXIvc2VxdWVuY2VyLXB1Ymxpc2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxFQUFnQyxvQkFBb0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzdGLE9BQU8sRUFDTCxnQkFBZ0IsRUFHaEIsd0JBQXdCLEVBRXhCLHlCQUF5QixHQUMxQixNQUFNLHNCQUFzQixDQUFDO0FBRTlCLE9BQU8sRUFBb0IsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFbEUsT0FBTyxFQUNMLGtCQUFrQixFQVVsQixlQUFlLEdBQ2hCLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3hELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUU5QyxPQUFPLEVBQWUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDbEUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2hELE9BQU8sRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzdFLE9BQU8sRUFBd0Isa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUVuRixPQUFPLElBQUksTUFBTSxhQUFhLENBQUM7QUFDL0IsT0FBTyxFQUEyQixrQkFBa0IsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRzVGLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBb0I3RSxNQUFNLENBQU4sSUFBWSxRQUdYO0FBSEQsV0FBWSxRQUFRO0lBQ2xCLG1EQUFVLENBQUE7SUFDViwrQ0FBUSxDQUFBO0FBQ1YsQ0FBQyxFQUhXLFFBQVEsS0FBUixRQUFRLFFBR25CO0FBaUJELE1B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
|