@aztec/p2p 0.0.1-commit.4eabbdb → 0.0.1-commit.5358163d3
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/factory.d.ts +4 -5
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +15 -26
- package/dest/client/interface.d.ts +6 -13
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +5 -13
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +3 -58
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
- package/dest/config.d.ts +10 -14
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +25 -35
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +5 -1
- package/dest/mem_pools/instrumentation.d.ts +4 -2
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +16 -14
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +2 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +10 -6
- package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +7 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +26 -4
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +48 -7
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +9 -10
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +3 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +3 -2
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +175 -145
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +10 -0
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +166 -0
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +25 -10
- package/dest/msg_validators/tx_validator/factory.d.ts +114 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +219 -58
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +58 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +73 -36
- package/dest/msg_validators/tx_validator/index.d.ts +2 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +25 -24
- package/dest/services/dummy_service.d.ts +2 -3
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +1 -4
- package/dest/services/encoding.d.ts +2 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +7 -7
- package/dest/services/libp2p/libp2p_service.d.ts +15 -13
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +72 -83
- package/dest/services/peer-manager/metrics.d.ts +3 -1
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +6 -0
- package/dest/services/peer-manager/peer_manager.d.ts +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +2 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +14 -37
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +17 -11
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +49 -15
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
- package/dest/services/reqresp/reqresp.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +2 -1
- package/dest/services/service.d.ts +2 -2
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_provider.d.ts +3 -3
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +4 -4
- package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +1 -2
- package/dest/test-helpers/mock-pubsub.d.ts +2 -3
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +2 -2
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +2 -2
- package/dest/test-helpers/testbench-utils.d.ts +2 -2
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +5 -5
- package/package.json +14 -14
- package/src/client/factory.ts +22 -46
- package/src/client/interface.ts +5 -19
- package/src/client/p2p_client.ts +4 -88
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -2
- package/src/config.ts +36 -42
- package/src/mem_pools/attestation_pool/attestation_pool.ts +5 -4
- package/src/mem_pools/instrumentation.ts +17 -13
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +2 -2
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +10 -6
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +7 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +65 -10
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +4 -1
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +184 -148
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +1 -1
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +1 -1
- package/src/msg_validators/proposal_validator/proposal_validator.ts +15 -1
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +144 -1
- package/src/msg_validators/tx_validator/README.md +115 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -3
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +27 -13
- package/src/msg_validators/tx_validator/factory.ts +353 -77
- package/src/msg_validators/tx_validator/gas_validator.ts +90 -27
- package/src/msg_validators/tx_validator/index.ts +1 -0
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +25 -29
- package/src/services/dummy_service.ts +1 -5
- package/src/services/encoding.ts +5 -6
- package/src/services/libp2p/libp2p_service.ts +84 -92
- package/src/services/peer-manager/metrics.ts +7 -0
- package/src/services/peer-manager/peer_manager.ts +2 -1
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +14 -42
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +63 -24
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
- package/src/services/reqresp/reqresp.ts +3 -1
- package/src/services/service.ts +1 -1
- package/src/services/tx_provider.ts +2 -2
- package/src/test-helpers/make-test-p2p-clients.ts +0 -2
- package/src/test-helpers/mock-pubsub.ts +3 -6
- package/src/test-helpers/reqresp-nodes.ts +2 -5
- package/src/test-helpers/testbench-utils.ts +1 -1
- package/src/testbench/p2p_client_testbench_worker.ts +2 -6
|
@@ -45,6 +45,7 @@ import { TxPoolIndices } from './tx_pool_indices.js';
|
|
|
45
45
|
export interface TxPoolV2Callbacks {
|
|
46
46
|
onTxsAdded: (txs: Tx[], opts: { source?: string }) => void;
|
|
47
47
|
onTxsRemoved: (txHashes: string[] | bigint[]) => void;
|
|
48
|
+
onTxsMined: (txHashes: string[]) => void;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
/**
|
|
@@ -187,6 +188,30 @@ export class TxPoolV2Impl {
|
|
|
187
188
|
const errors = new Map<string, TxPoolRejectionError>();
|
|
188
189
|
const acceptedPending = new Set<string>();
|
|
189
190
|
|
|
191
|
+
// Phase 1: Pre-compute all throwable I/O outside the transaction.
|
|
192
|
+
// If any pre-computation throws, the entire call fails before mutations happen.
|
|
193
|
+
const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
|
|
194
|
+
|
|
195
|
+
const validator = await this.#createTxValidator();
|
|
196
|
+
|
|
197
|
+
for (const tx of txs) {
|
|
198
|
+
const txHash = tx.getTxHash();
|
|
199
|
+
const txHashStr = txHash.toString();
|
|
200
|
+
|
|
201
|
+
const meta = await buildTxMetaData(tx);
|
|
202
|
+
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
203
|
+
|
|
204
|
+
// Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
|
|
205
|
+
let isValid = true;
|
|
206
|
+
if (!minedBlockId) {
|
|
207
|
+
isValid = await this.#validateMeta(meta, validator);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
precomputed.set(txHashStr, { meta, minedBlockId, isValid });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Phase 2: Apply mutations inside the transaction using only pre-computed results,
|
|
214
|
+
// in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
|
|
190
215
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
191
216
|
const preAddContext: PreAddContext | undefined =
|
|
192
217
|
opts.feeComparisonOnly !== undefined ? { feeComparisonOnly: opts.feeComparisonOnly } : undefined;
|
|
@@ -202,22 +227,25 @@ export class TxPoolV2Impl {
|
|
|
202
227
|
continue;
|
|
203
228
|
}
|
|
204
229
|
|
|
205
|
-
|
|
206
|
-
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
230
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
207
231
|
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
208
232
|
|
|
209
233
|
if (minedBlockId) {
|
|
210
234
|
// Already mined - add directly (protection already set if pre-protected)
|
|
211
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
235
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
212
236
|
accepted.push(txHash);
|
|
213
237
|
} else if (preProtectedSlot !== undefined) {
|
|
214
238
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
215
|
-
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
239
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
216
240
|
accepted.push(txHash);
|
|
241
|
+
} else if (!isValid) {
|
|
242
|
+
// Failed pre-computed validation
|
|
243
|
+
rejected.push(txHash);
|
|
217
244
|
} else {
|
|
218
|
-
// Regular pending tx -
|
|
245
|
+
// Regular pending tx - run pre-add rules using pre-computed metadata
|
|
219
246
|
const result = await this.#tryAddRegularPendingTx(
|
|
220
247
|
tx,
|
|
248
|
+
meta,
|
|
221
249
|
opts,
|
|
222
250
|
poolAccess,
|
|
223
251
|
acceptedPending,
|
|
@@ -227,13 +255,18 @@ export class TxPoolV2Impl {
|
|
|
227
255
|
);
|
|
228
256
|
if (result.status === 'accepted') {
|
|
229
257
|
acceptedPending.add(txHashStr);
|
|
230
|
-
} else if (result.status === 'rejected') {
|
|
231
|
-
rejected.push(txHash);
|
|
232
258
|
} else {
|
|
233
259
|
ignored.push(txHash);
|
|
234
260
|
}
|
|
235
261
|
}
|
|
236
262
|
}
|
|
263
|
+
|
|
264
|
+
// Run post-add eviction rules for pending txs (inside transaction for atomicity)
|
|
265
|
+
if (acceptedPending.size > 0) {
|
|
266
|
+
const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
|
|
267
|
+
const uniqueFeePayers = new Set<string>(feePayers);
|
|
268
|
+
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
269
|
+
}
|
|
237
270
|
});
|
|
238
271
|
|
|
239
272
|
// Build final accepted list for pending txs (excludes intra-batch evictions)
|
|
@@ -249,37 +282,24 @@ export class TxPoolV2Impl {
|
|
|
249
282
|
this.#instrumentation.recordRejected(rejected.length);
|
|
250
283
|
}
|
|
251
284
|
|
|
252
|
-
// Run post-add eviction rules for pending txs
|
|
253
|
-
if (acceptedPending.size > 0) {
|
|
254
|
-
const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
|
|
255
|
-
const uniqueFeePayers = new Set<string>(feePayers);
|
|
256
|
-
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
285
|
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
260
286
|
}
|
|
261
287
|
|
|
262
|
-
/**
|
|
288
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
263
289
|
async #tryAddRegularPendingTx(
|
|
264
290
|
tx: Tx,
|
|
291
|
+
precomputedMeta: TxMetaData,
|
|
265
292
|
opts: { source?: string },
|
|
266
293
|
poolAccess: PreAddPoolAccess,
|
|
267
294
|
acceptedPending: Set<string>,
|
|
268
295
|
ignored: TxHash[],
|
|
269
296
|
errors: Map<string, TxPoolRejectionError>,
|
|
270
297
|
preAddContext?: PreAddContext,
|
|
271
|
-
): Promise<{ status: 'accepted' | 'ignored'
|
|
272
|
-
const
|
|
273
|
-
const txHashStr = txHash.toString();
|
|
274
|
-
|
|
275
|
-
// Build metadata and validate using metadata
|
|
276
|
-
const meta = await buildTxMetaData(tx);
|
|
277
|
-
if (!(await this.#validateMeta(meta))) {
|
|
278
|
-
return { status: 'rejected' };
|
|
279
|
-
}
|
|
298
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
299
|
+
const txHashStr = tx.getTxHash().toString();
|
|
280
300
|
|
|
281
301
|
// Run pre-add rules
|
|
282
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(
|
|
302
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
283
303
|
|
|
284
304
|
if (preAddResult.shouldIgnore) {
|
|
285
305
|
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
|
|
@@ -316,12 +336,18 @@ export class TxPoolV2Impl {
|
|
|
316
336
|
}
|
|
317
337
|
}
|
|
318
338
|
|
|
339
|
+
// Randomly drop the transaction for testing purposes (report as accepted so it propagates)
|
|
340
|
+
if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
|
|
341
|
+
this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
|
|
342
|
+
return { status: 'accepted' };
|
|
343
|
+
}
|
|
344
|
+
|
|
319
345
|
// Add the transaction
|
|
320
|
-
await this.#addTx(tx, 'pending', opts);
|
|
346
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
321
347
|
return { status: 'accepted' };
|
|
322
348
|
}
|
|
323
349
|
|
|
324
|
-
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
350
|
+
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
325
351
|
const txHashStr = tx.getTxHash().toString();
|
|
326
352
|
|
|
327
353
|
// Check if already in pool
|
|
@@ -329,14 +355,8 @@ export class TxPoolV2Impl {
|
|
|
329
355
|
return 'ignored';
|
|
330
356
|
}
|
|
331
357
|
|
|
332
|
-
// Build metadata and
|
|
358
|
+
// Build metadata and check pre-add rules
|
|
333
359
|
const meta = await buildTxMetaData(tx);
|
|
334
|
-
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
335
|
-
if (validationResult !== true) {
|
|
336
|
-
return 'rejected';
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Use pre-add rules
|
|
340
360
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
341
361
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
342
362
|
|
|
@@ -379,33 +399,35 @@ export class TxPoolV2Impl {
|
|
|
379
399
|
let softDeletedHits = 0;
|
|
380
400
|
let missingPreviouslyEvicted = 0;
|
|
381
401
|
|
|
382
|
-
|
|
383
|
-
const
|
|
402
|
+
await this.#store.transactionAsync(async () => {
|
|
403
|
+
for (const txHash of txHashes) {
|
|
404
|
+
const txHashStr = txHash.toString();
|
|
384
405
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
406
|
+
if (this.#indices.has(txHashStr)) {
|
|
407
|
+
// Update protection for existing tx
|
|
408
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
409
|
+
} else if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
410
|
+
// Resurrect soft-deleted tx as protected
|
|
411
|
+
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
412
|
+
if (buffer) {
|
|
413
|
+
const tx = Tx.fromBuffer(buffer);
|
|
414
|
+
await this.#addTx(tx, { protected: slotNumber });
|
|
415
|
+
softDeletedHits++;
|
|
416
|
+
} else {
|
|
417
|
+
// Data missing despite soft-delete flag — treat as truly missing
|
|
418
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
419
|
+
missing.push(txHash);
|
|
420
|
+
}
|
|
395
421
|
} else {
|
|
396
|
-
//
|
|
422
|
+
// Truly missing — pre-record protection for tx we don't have yet
|
|
397
423
|
this.#indices.setProtection(txHashStr, slotNumber);
|
|
398
424
|
missing.push(txHash);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
this.#indices.setProtection(txHashStr, slotNumber);
|
|
403
|
-
missing.push(txHash);
|
|
404
|
-
if (this.#evictedTxHashes.has(txHashStr)) {
|
|
405
|
-
missingPreviouslyEvicted++;
|
|
425
|
+
if (this.#evictedTxHashes.has(txHashStr)) {
|
|
426
|
+
missingPreviouslyEvicted++;
|
|
427
|
+
}
|
|
406
428
|
}
|
|
407
429
|
}
|
|
408
|
-
}
|
|
430
|
+
});
|
|
409
431
|
|
|
410
432
|
// Record metrics
|
|
411
433
|
if (softDeletedHits > 0) {
|
|
@@ -466,56 +488,64 @@ export class TxPoolV2Impl {
|
|
|
466
488
|
}
|
|
467
489
|
}
|
|
468
490
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
491
|
+
await this.#store.transactionAsync(async () => {
|
|
492
|
+
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
493
|
+
for (const meta of found) {
|
|
494
|
+
this.#indices.markAsMined(meta, blockId);
|
|
495
|
+
await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
|
|
496
|
+
}
|
|
474
497
|
|
|
475
|
-
|
|
476
|
-
|
|
498
|
+
// Step 5: Run post-event eviction rules (inside transaction for atomicity)
|
|
499
|
+
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (found.length > 0) {
|
|
503
|
+
this.#callbacks.onTxsMined(found.map(m => m.txHash));
|
|
504
|
+
}
|
|
477
505
|
|
|
478
506
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
479
507
|
}
|
|
480
508
|
|
|
481
509
|
async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
|
|
482
|
-
|
|
483
|
-
|
|
510
|
+
await this.#store.transactionAsync(async () => {
|
|
511
|
+
// Step 0: Clean up slot-deleted txs from previous slots
|
|
512
|
+
await this.#deletedPool.cleanupSlotDeleted(slotNumber);
|
|
484
513
|
|
|
485
|
-
|
|
486
|
-
|
|
514
|
+
// Step 1: Find expired protected txs
|
|
515
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
487
516
|
|
|
488
|
-
|
|
489
|
-
|
|
517
|
+
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
518
|
+
this.#indices.clearProtection(expiredProtected);
|
|
490
519
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
520
|
+
// Step 3: Filter to only txs that have metadata and are not mined
|
|
521
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
522
|
+
if (txsToRestore.length === 0) {
|
|
523
|
+
this.#log.debug(`Preparing for slot ${slotNumber}, no txs to unprotect`);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
497
526
|
|
|
498
|
-
|
|
527
|
+
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
499
528
|
|
|
500
|
-
|
|
501
|
-
|
|
529
|
+
// Step 4: Validate for pending pool
|
|
530
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
502
531
|
|
|
503
|
-
|
|
504
|
-
|
|
532
|
+
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
533
|
+
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
505
534
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
535
|
+
// Step 6: Delete invalid txs and evict conflict losers
|
|
536
|
+
await this.#deleteTxsBatch(invalid);
|
|
537
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
509
538
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
539
|
+
// Step 7: Run eviction rules (enforce pool size limit)
|
|
540
|
+
if (added.length > 0) {
|
|
541
|
+
const feePayers = added.map(meta => meta.feePayer);
|
|
542
|
+
const uniqueFeePayers = new Set<string>(feePayers);
|
|
543
|
+
await this.#evictionManager.evictAfterNewTxs(
|
|
544
|
+
added.map(m => m.txHash),
|
|
545
|
+
[...uniqueFeePayers],
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
519
549
|
}
|
|
520
550
|
|
|
521
551
|
async handlePrunedBlocks(latestBlock: L2BlockId, options?: { deleteAllTxs?: boolean }): Promise<void> {
|
|
@@ -528,57 +558,60 @@ export class TxPoolV2Impl {
|
|
|
528
558
|
|
|
529
559
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
530
560
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
561
|
+
await this.#store.transactionAsync(async () => {
|
|
562
|
+
// Step 2: Mark ALL un-mined txs with their original mined block number
|
|
563
|
+
// This ensures they get soft-deleted if removed later, and only hard-deleted
|
|
564
|
+
// when their original mined block is finalized
|
|
565
|
+
await this.#deletedPool.markFromPrunedBlock(
|
|
566
|
+
txsToUnmine.map(m => ({
|
|
567
|
+
txHash: m.txHash,
|
|
568
|
+
minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
|
|
569
|
+
})),
|
|
570
|
+
);
|
|
540
571
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
572
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
573
|
+
for (const meta of txsToUnmine) {
|
|
574
|
+
this.#indices.markAsUnmined(meta);
|
|
575
|
+
}
|
|
545
576
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
577
|
+
// If deleteAllTxs is set (epoch prune), delete all un-mined txs and return early
|
|
578
|
+
if (options?.deleteAllTxs) {
|
|
579
|
+
const allTxHashes = txsToUnmine.map(m => m.txHash);
|
|
580
|
+
await this.#deleteTxsBatch(allTxHashes);
|
|
581
|
+
this.#log.info(
|
|
582
|
+
`Handled prune to block ${latestBlock.number} with deleteAllTxs: deleted ${allTxHashes.length} txs`,
|
|
583
|
+
);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
555
586
|
|
|
556
|
-
|
|
557
|
-
|
|
587
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
588
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
558
589
|
|
|
559
|
-
|
|
560
|
-
|
|
590
|
+
// Step 5: Validate for pending pool
|
|
591
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
561
592
|
|
|
562
|
-
|
|
563
|
-
|
|
593
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
594
|
+
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
564
595
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
596
|
+
// Step 7: Delete invalid txs and evict conflict losers
|
|
597
|
+
await this.#deleteTxsBatch(invalid);
|
|
598
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
568
599
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
600
|
+
this.#log.info(
|
|
601
|
+
`Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`,
|
|
602
|
+
{ txHashesRestored: valid.map(m => m.txHash), txHashesInvalid: invalid, txHashesEvicted: toEvict },
|
|
603
|
+
);
|
|
573
604
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
605
|
+
// Step 8: Run eviction rules for ALL pending txs (not just restored ones)
|
|
606
|
+
// This handles cases like existing pending txs with invalid fee payer balances
|
|
607
|
+
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
608
|
+
});
|
|
577
609
|
}
|
|
578
610
|
|
|
579
611
|
async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
|
|
580
|
-
|
|
581
|
-
|
|
612
|
+
await this.#store.transactionAsync(async () => {
|
|
613
|
+
await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
|
|
614
|
+
});
|
|
582
615
|
|
|
583
616
|
this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
|
|
584
617
|
}
|
|
@@ -589,27 +622,29 @@ export class TxPoolV2Impl {
|
|
|
589
622
|
// Step 1: Find mined txs at or before finalized block
|
|
590
623
|
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
591
624
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
625
|
+
await this.#store.transactionAsync(async () => {
|
|
626
|
+
// Step 2: Collect mined txs for archiving (before deletion)
|
|
627
|
+
const txsToArchive: Tx[] = [];
|
|
628
|
+
if (this.#archive.isEnabled()) {
|
|
629
|
+
for (const txHashStr of minedTxsToFinalize) {
|
|
630
|
+
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
631
|
+
if (buffer) {
|
|
632
|
+
txsToArchive.push(Tx.fromBuffer(buffer));
|
|
633
|
+
}
|
|
599
634
|
}
|
|
600
635
|
}
|
|
601
|
-
}
|
|
602
636
|
|
|
603
|
-
|
|
604
|
-
|
|
637
|
+
// Step 3: Delete mined txs from active pool
|
|
638
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
605
639
|
|
|
606
|
-
|
|
607
|
-
|
|
640
|
+
// Step 4: Finalize soft-deleted txs
|
|
641
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
608
642
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
643
|
+
// Step 5: Archive mined txs
|
|
644
|
+
if (txsToArchive.length > 0) {
|
|
645
|
+
await this.#archive.archiveTxs(txsToArchive);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
613
648
|
|
|
614
649
|
if (minedTxsToFinalize.length > 0) {
|
|
615
650
|
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
|
|
@@ -754,9 +789,10 @@ export class TxPoolV2Impl {
|
|
|
754
789
|
tx: Tx,
|
|
755
790
|
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
756
791
|
opts: { source?: string } = {},
|
|
792
|
+
precomputedMeta?: TxMetaData,
|
|
757
793
|
): Promise<TxMetaData> {
|
|
758
794
|
const txHashStr = tx.getTxHash().toString();
|
|
759
|
-
const meta = await buildTxMetaData(tx);
|
|
795
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
760
796
|
meta.receivedAt = this.#dateProvider.now();
|
|
761
797
|
|
|
762
798
|
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
@@ -4,7 +4,7 @@ import type { BlockProposal, P2PValidator } from '@aztec/stdlib/p2p';
|
|
|
4
4
|
import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
5
5
|
|
|
6
6
|
export class BlockProposalValidator extends ProposalValidator<BlockProposal> implements P2PValidator<BlockProposal> {
|
|
7
|
-
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
|
|
7
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
|
|
8
8
|
super(epochCache, opts, 'p2p:block_proposal_validator');
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -7,7 +7,7 @@ export class CheckpointProposalValidator
|
|
|
7
7
|
extends ProposalValidator<CheckpointProposal>
|
|
8
8
|
implements P2PValidator<CheckpointProposal>
|
|
9
9
|
{
|
|
10
|
-
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
|
|
10
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
|
|
11
11
|
super(epochCache, opts, 'p2p:checkpoint_proposal_validator');
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -9,10 +9,16 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
|
|
|
9
9
|
protected epochCache: EpochCacheInterface;
|
|
10
10
|
protected logger: Logger;
|
|
11
11
|
protected txsPermitted: boolean;
|
|
12
|
+
protected maxTxsPerBlock?: number;
|
|
12
13
|
|
|
13
|
-
constructor(
|
|
14
|
+
constructor(
|
|
15
|
+
epochCache: EpochCacheInterface,
|
|
16
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
|
|
17
|
+
loggerName: string,
|
|
18
|
+
) {
|
|
14
19
|
this.epochCache = epochCache;
|
|
15
20
|
this.txsPermitted = opts.txsPermitted;
|
|
21
|
+
this.maxTxsPerBlock = opts.maxTxsPerBlock;
|
|
16
22
|
this.logger = createLogger(loggerName);
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -47,6 +53,14 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
|
|
|
47
53
|
return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
// Max txs per block check
|
|
57
|
+
if (this.maxTxsPerBlock !== undefined && proposal.txHashes.length > this.maxTxsPerBlock) {
|
|
58
|
+
this.logger.warn(
|
|
59
|
+
`Penalizing peer for proposal with ${proposal.txHashes.length} transaction(s) when max is ${this.maxTxsPerBlock}`,
|
|
60
|
+
);
|
|
61
|
+
return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
// Embedded txs must be listed in txHashes
|
|
51
65
|
const hashSet = new Set(proposal.txHashes.map(h => h.toString()));
|
|
52
66
|
const missingTxHashes =
|