@aztec/simulator 0.69.0 → 0.69.1
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/avm/errors.d.ts +13 -1
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +19 -1
- package/dest/avm/fixtures/index.js +2 -2
- package/dest/avm/opcodes/multi_scalar_mul.js +4 -4
- package/dest/client/client_execution_context.d.ts +0 -1
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +2 -5
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +5 -1
- package/dest/public/fixtures/index.d.ts +1 -1
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +2 -2
- package/dest/public/public_processor.d.ts +16 -2
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +112 -26
- package/dest/public/public_tx_context.js +2 -2
- package/package.json +9 -9
- package/src/avm/errors.ts +21 -1
- package/src/avm/fixtures/index.ts +1 -1
- package/src/avm/opcodes/multi_scalar_mul.ts +3 -3
- package/src/client/client_execution_context.ts +1 -5
- package/src/client/view_data_oracle.ts +4 -0
- package/src/public/fixtures/index.ts +1 -1
- package/src/public/public_processor.ts +132 -33
- package/src/public/public_tx_context.ts +1 -1
|
@@ -136,29 +136,132 @@ export class PublicProcessor implements Traceable {
|
|
|
136
136
|
* @returns The list of processed txs with their circuit simulation outputs.
|
|
137
137
|
*/
|
|
138
138
|
public async process(
|
|
139
|
-
txs: Tx
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
txs: Iterable<Tx>,
|
|
140
|
+
limits: {
|
|
141
|
+
maxTransactions?: number;
|
|
142
|
+
maxBlockSize?: number;
|
|
143
|
+
maxBlockGas?: Gas;
|
|
144
|
+
deadline?: Date;
|
|
145
|
+
} = {},
|
|
146
|
+
validators: {
|
|
147
|
+
preprocessValidator?: TxValidator<Tx>;
|
|
148
|
+
postprocessValidator?: TxValidator<ProcessedTx>;
|
|
149
|
+
nullifierCache?: { addNullifiers: (nullifiers: Buffer[]) => void };
|
|
150
|
+
} = {},
|
|
143
151
|
): Promise<[ProcessedTx[], FailedTx[], NestedProcessReturnValues[]]> {
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
const { maxTransactions, maxBlockSize, deadline, maxBlockGas } = limits;
|
|
153
|
+
const { preprocessValidator, postprocessValidator, nullifierCache } = validators;
|
|
146
154
|
const result: ProcessedTx[] = [];
|
|
147
155
|
const failed: FailedTx[] = [];
|
|
148
|
-
let returns: NestedProcessReturnValues[] = [];
|
|
149
|
-
let totalGas = new Gas(0, 0);
|
|
150
156
|
const timer = new Timer();
|
|
151
157
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
let totalSizeInBytes = 0;
|
|
159
|
+
let returns: NestedProcessReturnValues[] = [];
|
|
160
|
+
let totalPublicGas = new Gas(0, 0);
|
|
161
|
+
let totalBlockGas = new Gas(0, 0);
|
|
162
|
+
|
|
163
|
+
for (const origTx of txs) {
|
|
164
|
+
// Only process up to the max tx limit
|
|
165
|
+
if (maxTransactions !== undefined && result.length >= maxTransactions) {
|
|
166
|
+
this.log.debug(`Stopping tx processing due to reaching the max tx limit.`);
|
|
155
167
|
break;
|
|
156
168
|
}
|
|
169
|
+
|
|
170
|
+
// Bail if we've hit the deadline
|
|
171
|
+
if (deadline && this.dateProvider.now() > +deadline) {
|
|
172
|
+
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Skip this tx if it'd exceed max block size
|
|
177
|
+
const txHash = origTx.getTxHash().toString();
|
|
178
|
+
const preTxSizeInBytes = origTx.getEstimatedPrivateTxEffectsSize();
|
|
179
|
+
if (maxBlockSize !== undefined && totalSizeInBytes + preTxSizeInBytes > maxBlockSize) {
|
|
180
|
+
this.log.warn(`Skipping processing of tx ${txHash} sized ${preTxSizeInBytes} bytes due to block size limit`, {
|
|
181
|
+
txHash,
|
|
182
|
+
sizeInBytes: preTxSizeInBytes,
|
|
183
|
+
totalSizeInBytes,
|
|
184
|
+
maxBlockSize,
|
|
185
|
+
});
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Skip this tx if its gas limit would exceed the block gas limit
|
|
190
|
+
const txGasLimit = origTx.data.constants.txContext.gasSettings.gasLimits;
|
|
191
|
+
if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
192
|
+
this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
|
|
193
|
+
txHash,
|
|
194
|
+
txGasLimit,
|
|
195
|
+
totalBlockGas,
|
|
196
|
+
maxBlockGas,
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// The processor modifies the tx objects in place, so we need to clone them.
|
|
202
|
+
const tx = Tx.clone(origTx);
|
|
203
|
+
|
|
204
|
+
// We validate the tx before processing it, to avoid unnecessary work.
|
|
205
|
+
if (preprocessValidator) {
|
|
206
|
+
const result = await preprocessValidator.validateTx(tx);
|
|
207
|
+
if (result.result === 'invalid') {
|
|
208
|
+
const reason = result.reason.join(', ');
|
|
209
|
+
this.log.warn(`Rejecting tx ${tx.getTxHash().toString()} due to pre-process validation fail: ${reason}`);
|
|
210
|
+
failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
|
|
211
|
+
returns.push(new NestedProcessReturnValues([]));
|
|
212
|
+
continue;
|
|
213
|
+
} else if (result.result === 'skipped') {
|
|
214
|
+
const reason = result.reason.join(', ');
|
|
215
|
+
this.log.warn(`Skipping tx ${tx.getTxHash().toString()} due to pre-process validation: ${reason}`);
|
|
216
|
+
returns.push(new NestedProcessReturnValues([]));
|
|
217
|
+
continue;
|
|
218
|
+
} else {
|
|
219
|
+
this.log.trace(`Tx ${tx.getTxHash().toString()} is valid before processing.`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
157
223
|
try {
|
|
158
|
-
const [processedTx, returnValues] = await this.processTx(tx,
|
|
224
|
+
const [processedTx, returnValues] = await this.processTx(tx, deadline);
|
|
225
|
+
|
|
226
|
+
// If the actual size of this tx would exceed block size, skip it
|
|
227
|
+
const txSize = processedTx.txEffect.getDASize();
|
|
228
|
+
if (maxBlockSize !== undefined && totalSizeInBytes + txSize > maxBlockSize) {
|
|
229
|
+
this.log.warn(`Skipping processed tx ${txHash} sized ${txSize} due to max block size.`, {
|
|
230
|
+
txHash,
|
|
231
|
+
sizeInBytes: txSize,
|
|
232
|
+
totalSizeInBytes,
|
|
233
|
+
maxBlockSize,
|
|
234
|
+
});
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Re-validate the transaction
|
|
239
|
+
if (postprocessValidator) {
|
|
240
|
+
// Only accept processed transactions that are not double-spends,
|
|
241
|
+
// public functions emitting nullifiers would pass earlier check but fail here.
|
|
242
|
+
// Note that we're checking all nullifiers generated in the private execution twice,
|
|
243
|
+
// we could store the ones already checked and skip them here as an optimization.
|
|
244
|
+
// TODO(palla/txs): Can we get into this case? AVM validates this. We should be able to remove it.
|
|
245
|
+
const result = await postprocessValidator.validateTx(processedTx);
|
|
246
|
+
if (result.result !== 'valid') {
|
|
247
|
+
const reason = result.reason.join(', ');
|
|
248
|
+
this.log.error(`Rejecting tx ${processedTx.hash} after processing: ${reason}.`);
|
|
249
|
+
failed.push({ tx, error: new Error(`Tx failed post-process validation: ${reason}`) });
|
|
250
|
+
continue;
|
|
251
|
+
} else {
|
|
252
|
+
this.log.trace(`Tx ${tx.getTxHash().toString()} is valid post processing.`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Otherwise, commit tx state for the next tx to be processed
|
|
257
|
+
await this.commitTxState(processedTx);
|
|
258
|
+
nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
|
|
159
259
|
result.push(processedTx);
|
|
160
260
|
returns = returns.concat(returnValues);
|
|
161
|
-
|
|
261
|
+
|
|
262
|
+
totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
|
|
263
|
+
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
264
|
+
totalSizeInBytes += txSize;
|
|
162
265
|
} catch (err: any) {
|
|
163
266
|
if (err?.name === 'PublicProcessorTimeoutError') {
|
|
164
267
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
@@ -173,18 +276,22 @@ export class PublicProcessor implements Traceable {
|
|
|
173
276
|
}
|
|
174
277
|
|
|
175
278
|
const duration = timer.s();
|
|
176
|
-
const rate = duration > 0 ?
|
|
177
|
-
this.metrics.recordAllTxs(
|
|
279
|
+
const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
|
|
280
|
+
this.metrics.recordAllTxs(totalPublicGas, rate);
|
|
281
|
+
|
|
282
|
+
this.log.info(`Processed ${result.length} succesful txs and ${failed.length} txs in ${duration}ms`, {
|
|
283
|
+
duration,
|
|
284
|
+
rate,
|
|
285
|
+
totalPublicGas,
|
|
286
|
+
totalBlockGas,
|
|
287
|
+
totalSizeInBytes,
|
|
288
|
+
});
|
|
178
289
|
|
|
179
290
|
return [result, failed, returns];
|
|
180
291
|
}
|
|
181
292
|
|
|
182
293
|
@trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.tryGetTxHash()?.toString() }))
|
|
183
|
-
private async processTx(
|
|
184
|
-
tx: Tx,
|
|
185
|
-
txValidator?: TxValidator<ProcessedTx>,
|
|
186
|
-
deadline?: Date,
|
|
187
|
-
): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
|
|
294
|
+
private async processTx(tx: Tx, deadline?: Date): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
|
|
188
295
|
const [time, [processedTx, returnValues]] = await elapsed(() => this.processTxWithinDeadline(tx, deadline));
|
|
189
296
|
|
|
190
297
|
this.log.verbose(
|
|
@@ -208,20 +315,14 @@ export class PublicProcessor implements Traceable {
|
|
|
208
315
|
},
|
|
209
316
|
);
|
|
210
317
|
|
|
318
|
+
return [processedTx, returnValues ?? []];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private async commitTxState(processedTx: ProcessedTx, txValidator?: TxValidator<ProcessedTx>): Promise<void> {
|
|
211
322
|
// Commit the state updates from this transaction
|
|
323
|
+
// TODO(palla/txs): It seems like this doesn't do anything...?
|
|
212
324
|
await this.worldStateDB.commit();
|
|
213
325
|
|
|
214
|
-
// Re-validate the transaction
|
|
215
|
-
if (txValidator) {
|
|
216
|
-
// Only accept processed transactions that are not double-spends,
|
|
217
|
-
// public functions emitting nullifiers would pass earlier check but fail here.
|
|
218
|
-
// Note that we're checking all nullifiers generated in the private execution twice,
|
|
219
|
-
// we could store the ones already checked and skip them here as an optimization.
|
|
220
|
-
const [_, invalid] = await txValidator.validateTxs([processedTx]);
|
|
221
|
-
if (invalid.length) {
|
|
222
|
-
throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
326
|
// Update the state so that the next tx in the loop has the correct .startState
|
|
226
327
|
// NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
|
|
227
328
|
// a) had only 1 tx with public calls per block, so this loop had len 1
|
|
@@ -255,8 +356,6 @@ export class PublicProcessor implements Traceable {
|
|
|
255
356
|
);
|
|
256
357
|
const treeInsertionEnd = process.hrtime.bigint();
|
|
257
358
|
this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
|
|
258
|
-
|
|
259
|
-
return [processedTx, returnValues ?? []];
|
|
260
359
|
}
|
|
261
360
|
|
|
262
361
|
/** Processes the given tx within deadline. Returns timeout if deadline is hit. */
|
|
@@ -459,5 +459,5 @@ function fetchTxHash(nonRevertibleAccumulatedData: PrivateToPublicAccumulatedDat
|
|
|
459
459
|
if (!firstNullifier || firstNullifier.isZero()) {
|
|
460
460
|
throw new Error(`Cannot get tx hash since first nullifier is missing`);
|
|
461
461
|
}
|
|
462
|
-
return new TxHash(firstNullifier
|
|
462
|
+
return new TxHash(firstNullifier);
|
|
463
463
|
}
|