@aztec/p2p 0.0.1-commit.8f9871590 → 0.0.1-commit.9117c5f5a
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 +6 -6
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +18 -28
- package/dest/client/interface.d.ts +10 -19
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +7 -18
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +41 -72
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +6 -7
- package/dest/config.d.ts +4 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +0 -5
- package/dest/errors/tx-pool.error.d.ts +8 -0
- package/dest/errors/tx-pool.error.d.ts.map +1 -0
- package/dest/errors/tx-pool.error.js +9 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +9 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +18 -9
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +3 -3
- 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 +12 -4
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +48 -5
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +8 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +5 -5
- 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 +12 -6
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +4 -4
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +14 -4
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +2 -2
- 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/instrumentation.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +16 -6
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +3 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +29 -5
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +71 -6
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +5 -2
- 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 +21 -12
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +6 -3
- 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 +6 -5
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +11 -5
- 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 +274 -149
- 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/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/services/dummy_service.d.ts +4 -4
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +4 -4
- package/dest/services/encoding.d.ts +2 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +9 -8
- package/dest/services/gossipsub/topic_score_params.d.ts +18 -6
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +32 -10
- package/dest/services/libp2p/libp2p_service.d.ts +16 -13
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +69 -81
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -3
- 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 +19 -46
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +2 -6
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +10 -13
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +25 -46
- 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 +5 -3
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.d.ts +1 -1
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +39 -33
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.js +4 -2
- package/dest/services/tx_collection/file_store_tx_source.d.ts +15 -6
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +47 -16
- package/dest/services/tx_collection/instrumentation.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
- package/dest/services/tx_collection/instrumentation.js +2 -1
- package/dest/services/tx_collection/missing_txs_tracker.d.ts +32 -0
- package/dest/services/tx_collection/missing_txs_tracker.d.ts.map +1 -0
- package/dest/services/tx_collection/missing_txs_tracker.js +27 -0
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +7 -6
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +5 -4
- package/dest/services/tx_collection/slow_tx_collection.d.ts +2 -2
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +10 -8
- package/dest/services/tx_collection/tx_collection.d.ts +5 -4
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.d.ts +6 -5
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +13 -22
- package/dest/services/tx_collection/tx_source.d.ts +8 -3
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +19 -2
- package/dest/services/tx_file_store/tx_file_store.js +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 +4 -4
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +8 -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 +6 -3
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +1 -1
- package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -2
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +11 -11
- package/dest/util.d.ts +2 -2
- package/dest/util.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +27 -48
- package/src/client/interface.ts +11 -20
- package/src/client/p2p_client.ts +47 -104
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +19 -10
- package/src/config.ts +2 -10
- package/src/errors/tx-pool.error.ts +12 -0
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +11 -0
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +21 -8
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +18 -4
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +4 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +49 -4
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +2 -2
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +5 -5
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +12 -9
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +24 -6
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +15 -6
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +96 -10
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +25 -14
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +12 -7
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +303 -144
- 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/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/services/dummy_service.ts +6 -6
- package/src/services/encoding.ts +7 -7
- package/src/services/gossipsub/README.md +29 -14
- package/src/services/gossipsub/topic_score_params.ts +49 -13
- package/src/services/libp2p/libp2p_service.ts +80 -90
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +20 -48
- package/src/services/reqresp/batch-tx-requester/interface.ts +1 -5
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +23 -71
- 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 +11 -2
- package/src/services/tx_collection/fast_tx_collection.ts +51 -30
- package/src/services/tx_collection/file_store_tx_collection.ts +7 -3
- package/src/services/tx_collection/file_store_tx_source.ts +61 -17
- package/src/services/tx_collection/instrumentation.ts +7 -1
- package/src/services/tx_collection/missing_txs_tracker.ts +52 -0
- package/src/services/tx_collection/proposal_tx_collector.ts +8 -7
- package/src/services/tx_collection/slow_tx_collection.ts +8 -9
- package/src/services/tx_collection/tx_collection.ts +4 -3
- package/src/services/tx_collection/tx_collection_sink.ts +15 -29
- package/src/services/tx_collection/tx_source.ts +22 -3
- package/src/services/tx_file_store/tx_file_store.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 +13 -6
- package/src/test-helpers/reqresp-nodes.ts +2 -5
- package/src/test-helpers/testbench-utils.ts +3 -3
- package/src/testbench/p2p_client_testbench_worker.ts +20 -17
- package/src/util.ts +7 -1
|
@@ -9,6 +9,7 @@ import type { L2Block, L2BlockId, L2BlockSource } from '@aztec/stdlib/block';
|
|
|
9
9
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
10
10
|
import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
11
11
|
import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
|
|
12
|
+
import type { TelemetryClient } from '@aztec/telemetry-client';
|
|
12
13
|
|
|
13
14
|
import { TxArchive } from './archive/index.js';
|
|
14
15
|
import { DeletedPool } from './deleted_pool.js';
|
|
@@ -22,8 +23,12 @@ import {
|
|
|
22
23
|
LowPriorityPreAddRule,
|
|
23
24
|
NullifierConflictRule,
|
|
24
25
|
type PoolOperations,
|
|
26
|
+
type PreAddContext,
|
|
25
27
|
type PreAddPoolAccess,
|
|
28
|
+
TxPoolRejectionCode,
|
|
29
|
+
type TxPoolRejectionError,
|
|
26
30
|
} from './eviction/index.js';
|
|
31
|
+
import { TxPoolV2Instrumentation } from './instrumentation.js';
|
|
27
32
|
import {
|
|
28
33
|
type AddTxsResult,
|
|
29
34
|
DEFAULT_TX_POOL_V2_CONFIG,
|
|
@@ -66,6 +71,8 @@ export class TxPoolV2Impl {
|
|
|
66
71
|
#deletedPool: DeletedPool;
|
|
67
72
|
#evictionManager: EvictionManager;
|
|
68
73
|
#dateProvider: DateProvider;
|
|
74
|
+
#instrumentation: TxPoolV2Instrumentation;
|
|
75
|
+
#evictedTxHashes: Set<string> = new Set();
|
|
69
76
|
#log: Logger;
|
|
70
77
|
#callbacks: TxPoolV2Callbacks;
|
|
71
78
|
|
|
@@ -74,6 +81,7 @@ export class TxPoolV2Impl {
|
|
|
74
81
|
archiveStore: AztecAsyncKVStore,
|
|
75
82
|
deps: TxPoolV2Dependencies,
|
|
76
83
|
callbacks: TxPoolV2Callbacks,
|
|
84
|
+
telemetry: TelemetryClient,
|
|
77
85
|
config: Partial<TxPoolV2Config> = {},
|
|
78
86
|
dateProvider: DateProvider,
|
|
79
87
|
log: Logger,
|
|
@@ -89,6 +97,7 @@ export class TxPoolV2Impl {
|
|
|
89
97
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
90
98
|
this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
|
|
91
99
|
this.#dateProvider = dateProvider;
|
|
100
|
+
this.#instrumentation = new TxPoolV2Instrumentation(telemetry, () => this.#indices.getTotalMetadataBytes());
|
|
92
101
|
this.#log = log;
|
|
93
102
|
this.#callbacks = callbacks;
|
|
94
103
|
|
|
@@ -171,13 +180,40 @@ export class TxPoolV2Impl {
|
|
|
171
180
|
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, { txHashes: toDelete });
|
|
172
181
|
}
|
|
173
182
|
|
|
174
|
-
async addPendingTxs(txs: Tx[], opts: { source?: string }): Promise<AddTxsResult> {
|
|
183
|
+
async addPendingTxs(txs: Tx[], opts: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult> {
|
|
175
184
|
const accepted: TxHash[] = [];
|
|
176
185
|
const ignored: TxHash[] = [];
|
|
177
186
|
const rejected: TxHash[] = [];
|
|
187
|
+
const errors = new Map<string, TxPoolRejectionError>();
|
|
178
188
|
const acceptedPending = new Set<string>();
|
|
179
189
|
|
|
190
|
+
// Phase 1: Pre-compute all throwable I/O outside the transaction.
|
|
191
|
+
// If any pre-computation throws, the entire call fails before mutations happen.
|
|
192
|
+
const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
|
|
193
|
+
|
|
194
|
+
const validator = await this.#createTxValidator();
|
|
195
|
+
|
|
196
|
+
for (const tx of txs) {
|
|
197
|
+
const txHash = tx.getTxHash();
|
|
198
|
+
const txHashStr = txHash.toString();
|
|
199
|
+
|
|
200
|
+
const meta = await buildTxMetaData(tx);
|
|
201
|
+
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
202
|
+
|
|
203
|
+
// Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
|
|
204
|
+
let isValid = true;
|
|
205
|
+
if (!minedBlockId) {
|
|
206
|
+
isValid = await this.#validateMeta(meta, validator);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
precomputed.set(txHashStr, { meta, minedBlockId, isValid });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Phase 2: Apply mutations inside the transaction using only pre-computed results,
|
|
213
|
+
// in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
|
|
180
214
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
215
|
+
const preAddContext: PreAddContext | undefined =
|
|
216
|
+
opts.feeComparisonOnly !== undefined ? { feeComparisonOnly: opts.feeComparisonOnly } : undefined;
|
|
181
217
|
|
|
182
218
|
await this.#store.transactionAsync(async () => {
|
|
183
219
|
for (const tx of txs) {
|
|
@@ -190,30 +226,46 @@ export class TxPoolV2Impl {
|
|
|
190
226
|
continue;
|
|
191
227
|
}
|
|
192
228
|
|
|
193
|
-
|
|
194
|
-
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
229
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
195
230
|
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
196
231
|
|
|
197
232
|
if (minedBlockId) {
|
|
198
233
|
// Already mined - add directly (protection already set if pre-protected)
|
|
199
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
234
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
200
235
|
accepted.push(txHash);
|
|
201
236
|
} else if (preProtectedSlot !== undefined) {
|
|
202
237
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
203
|
-
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
238
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
204
239
|
accepted.push(txHash);
|
|
240
|
+
} else if (!isValid) {
|
|
241
|
+
// Failed pre-computed validation
|
|
242
|
+
rejected.push(txHash);
|
|
205
243
|
} else {
|
|
206
|
-
// Regular pending tx -
|
|
207
|
-
const result = await this.#tryAddRegularPendingTx(
|
|
244
|
+
// Regular pending tx - run pre-add rules using pre-computed metadata
|
|
245
|
+
const result = await this.#tryAddRegularPendingTx(
|
|
246
|
+
tx,
|
|
247
|
+
meta,
|
|
248
|
+
opts,
|
|
249
|
+
poolAccess,
|
|
250
|
+
acceptedPending,
|
|
251
|
+
ignored,
|
|
252
|
+
errors,
|
|
253
|
+
preAddContext,
|
|
254
|
+
);
|
|
208
255
|
if (result.status === 'accepted') {
|
|
209
256
|
acceptedPending.add(txHashStr);
|
|
210
|
-
} else if (result.status === 'rejected') {
|
|
211
|
-
rejected.push(txHash);
|
|
212
257
|
} else {
|
|
213
258
|
ignored.push(txHash);
|
|
214
259
|
}
|
|
215
260
|
}
|
|
216
261
|
}
|
|
262
|
+
|
|
263
|
+
// Run post-add eviction rules for pending txs (inside transaction for atomicity)
|
|
264
|
+
if (acceptedPending.size > 0) {
|
|
265
|
+
const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
|
|
266
|
+
const uniqueFeePayers = new Set<string>(feePayers);
|
|
267
|
+
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
268
|
+
}
|
|
217
269
|
});
|
|
218
270
|
|
|
219
271
|
// Build final accepted list for pending txs (excludes intra-batch evictions)
|
|
@@ -221,61 +273,80 @@ export class TxPoolV2Impl {
|
|
|
221
273
|
accepted.push(TxHash.fromString(txHashStr));
|
|
222
274
|
}
|
|
223
275
|
|
|
224
|
-
//
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
276
|
+
// Record metrics
|
|
277
|
+
if (ignored.length > 0) {
|
|
278
|
+
this.#instrumentation.recordIgnored(ignored.length);
|
|
279
|
+
}
|
|
280
|
+
if (rejected.length > 0) {
|
|
281
|
+
this.#instrumentation.recordRejected(rejected.length);
|
|
229
282
|
}
|
|
230
283
|
|
|
231
|
-
return { accepted, ignored, rejected };
|
|
284
|
+
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
232
285
|
}
|
|
233
286
|
|
|
234
|
-
/**
|
|
287
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
235
288
|
async #tryAddRegularPendingTx(
|
|
236
289
|
tx: Tx,
|
|
290
|
+
precomputedMeta: TxMetaData,
|
|
237
291
|
opts: { source?: string },
|
|
238
292
|
poolAccess: PreAddPoolAccess,
|
|
239
293
|
acceptedPending: Set<string>,
|
|
240
294
|
ignored: TxHash[],
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// Build metadata and validate using metadata
|
|
246
|
-
const meta = await buildTxMetaData(tx);
|
|
247
|
-
if (!(await this.#validateMeta(meta))) {
|
|
248
|
-
return { status: 'rejected' };
|
|
249
|
-
}
|
|
295
|
+
errors: Map<string, TxPoolRejectionError>,
|
|
296
|
+
preAddContext?: PreAddContext,
|
|
297
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
298
|
+
const txHashStr = tx.getTxHash().toString();
|
|
250
299
|
|
|
251
300
|
// Run pre-add rules
|
|
252
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(
|
|
301
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
253
302
|
|
|
254
303
|
if (preAddResult.shouldIgnore) {
|
|
255
|
-
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason}`);
|
|
304
|
+
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
|
|
305
|
+
if (preAddResult.reason && preAddResult.reason.code !== TxPoolRejectionCode.INTERNAL_ERROR) {
|
|
306
|
+
errors.set(txHashStr, preAddResult.reason);
|
|
307
|
+
}
|
|
256
308
|
return { status: 'ignored' };
|
|
257
309
|
}
|
|
258
310
|
|
|
259
|
-
// Evict conflicts
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
311
|
+
// Evict conflicts, grouped by rule name for metrics
|
|
312
|
+
if (preAddResult.evictions && preAddResult.evictions.length > 0) {
|
|
313
|
+
const byReason = new Map<string, string[]>();
|
|
314
|
+
for (const { txHash: evictHash, reason } of preAddResult.evictions) {
|
|
315
|
+
const group = byReason.get(reason);
|
|
316
|
+
if (group) {
|
|
317
|
+
group.push(evictHash);
|
|
318
|
+
} else {
|
|
319
|
+
byReason.set(reason, [evictHash]);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
for (const [reason, hashes] of byReason) {
|
|
323
|
+
await this.#evictTxs(hashes, reason);
|
|
324
|
+
}
|
|
325
|
+
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
326
|
+
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
|
|
327
|
+
evictedTxHash: evictHashStr,
|
|
328
|
+
replacementTxHash: txHashStr,
|
|
329
|
+
});
|
|
330
|
+
if (acceptedPending.has(evictHashStr)) {
|
|
331
|
+
// Evicted tx was from this batch - mark as ignored in result
|
|
332
|
+
acceptedPending.delete(evictHashStr);
|
|
333
|
+
ignored.push(TxHash.fromString(evictHashStr));
|
|
334
|
+
}
|
|
270
335
|
}
|
|
271
336
|
}
|
|
272
337
|
|
|
338
|
+
// Randomly drop the transaction for testing purposes (report as accepted so it propagates)
|
|
339
|
+
if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
|
|
340
|
+
this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
|
|
341
|
+
return { status: 'accepted' };
|
|
342
|
+
}
|
|
343
|
+
|
|
273
344
|
// Add the transaction
|
|
274
|
-
await this.#addTx(tx, 'pending', opts);
|
|
345
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
275
346
|
return { status: 'accepted' };
|
|
276
347
|
}
|
|
277
348
|
|
|
278
|
-
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
349
|
+
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
279
350
|
const txHashStr = tx.getTxHash().toString();
|
|
280
351
|
|
|
281
352
|
// Check if already in pool
|
|
@@ -283,14 +354,8 @@ export class TxPoolV2Impl {
|
|
|
283
354
|
return 'ignored';
|
|
284
355
|
}
|
|
285
356
|
|
|
286
|
-
// Build metadata and
|
|
357
|
+
// Build metadata and check pre-add rules
|
|
287
358
|
const meta = await buildTxMetaData(tx);
|
|
288
|
-
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
289
|
-
if (validationResult !== true) {
|
|
290
|
-
return 'rejected';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Use pre-add rules
|
|
294
359
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
295
360
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
296
361
|
|
|
@@ -327,23 +392,58 @@ export class TxPoolV2Impl {
|
|
|
327
392
|
});
|
|
328
393
|
}
|
|
329
394
|
|
|
330
|
-
protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
|
|
395
|
+
async protectTxs(txHashes: TxHash[], block: BlockHeader): Promise<TxHash[]> {
|
|
331
396
|
const slotNumber = block.globalVariables.slotNumber;
|
|
332
397
|
const missing: TxHash[] = [];
|
|
398
|
+
let softDeletedHits = 0;
|
|
399
|
+
let missingPreviouslyEvicted = 0;
|
|
333
400
|
|
|
334
|
-
|
|
335
|
-
const
|
|
401
|
+
await this.#store.transactionAsync(async () => {
|
|
402
|
+
for (const txHash of txHashes) {
|
|
403
|
+
const txHashStr = txHash.toString();
|
|
336
404
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
405
|
+
if (this.#indices.has(txHashStr)) {
|
|
406
|
+
// Update protection for existing tx
|
|
407
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
408
|
+
} else if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
409
|
+
// Resurrect soft-deleted tx as protected
|
|
410
|
+
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
411
|
+
if (buffer) {
|
|
412
|
+
const tx = Tx.fromBuffer(buffer);
|
|
413
|
+
await this.#addTx(tx, { protected: slotNumber });
|
|
414
|
+
softDeletedHits++;
|
|
415
|
+
} else {
|
|
416
|
+
// Data missing despite soft-delete flag — treat as truly missing
|
|
417
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
418
|
+
missing.push(txHash);
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
// Truly missing — pre-record protection for tx we don't have yet
|
|
422
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
423
|
+
missing.push(txHash);
|
|
424
|
+
if (this.#evictedTxHashes.has(txHashStr)) {
|
|
425
|
+
missingPreviouslyEvicted++;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
344
428
|
}
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Record metrics
|
|
432
|
+
if (softDeletedHits > 0) {
|
|
433
|
+
this.#instrumentation.recordSoftDeletedHits(softDeletedHits);
|
|
434
|
+
}
|
|
435
|
+
if (missing.length > 0) {
|
|
436
|
+
this.#log.debug(`protectTxs missing tx hashes: ${missing.map(h => h.toString()).join(', ')}`);
|
|
437
|
+
this.#instrumentation.recordMissingOnProtect(missing.length);
|
|
438
|
+
}
|
|
439
|
+
if (missingPreviouslyEvicted > 0) {
|
|
440
|
+
this.#instrumentation.recordMissingPreviouslyEvicted(missingPreviouslyEvicted);
|
|
345
441
|
}
|
|
346
442
|
|
|
443
|
+
this.#log.info(
|
|
444
|
+
`Protected ${txHashes.length} txs, missing: ${missing.length}, soft-deleted hits: ${softDeletedHits}`,
|
|
445
|
+
);
|
|
446
|
+
|
|
347
447
|
return missing;
|
|
348
448
|
}
|
|
349
449
|
|
|
@@ -387,57 +487,63 @@ export class TxPoolV2Impl {
|
|
|
387
487
|
}
|
|
388
488
|
}
|
|
389
489
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
490
|
+
await this.#store.transactionAsync(async () => {
|
|
491
|
+
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
492
|
+
for (const meta of found) {
|
|
493
|
+
this.#indices.markAsMined(meta, blockId);
|
|
494
|
+
await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
|
|
495
|
+
}
|
|
395
496
|
|
|
396
|
-
|
|
397
|
-
|
|
497
|
+
// Step 5: Run post-event eviction rules (inside transaction for atomicity)
|
|
498
|
+
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
499
|
+
});
|
|
398
500
|
|
|
399
501
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
400
502
|
}
|
|
401
503
|
|
|
402
504
|
async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
|
|
403
|
-
|
|
404
|
-
|
|
505
|
+
await this.#store.transactionAsync(async () => {
|
|
506
|
+
// Step 0: Clean up slot-deleted txs from previous slots
|
|
507
|
+
await this.#deletedPool.cleanupSlotDeleted(slotNumber);
|
|
405
508
|
|
|
406
|
-
|
|
407
|
-
|
|
509
|
+
// Step 1: Find expired protected txs
|
|
510
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
408
511
|
|
|
409
|
-
|
|
410
|
-
|
|
512
|
+
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
513
|
+
this.#indices.clearProtection(expiredProtected);
|
|
411
514
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
515
|
+
// Step 3: Filter to only txs that have metadata and are not mined
|
|
516
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
517
|
+
if (txsToRestore.length === 0) {
|
|
518
|
+
this.#log.debug(`Preparing for slot ${slotNumber}, no txs to unprotect`);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
417
521
|
|
|
418
|
-
|
|
522
|
+
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
419
523
|
|
|
420
|
-
|
|
421
|
-
|
|
524
|
+
// Step 4: Validate for pending pool
|
|
525
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
422
526
|
|
|
423
|
-
|
|
424
|
-
|
|
527
|
+
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
528
|
+
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
425
529
|
|
|
426
|
-
|
|
427
|
-
|
|
530
|
+
// Step 6: Delete invalid txs and evict conflict losers
|
|
531
|
+
await this.#deleteTxsBatch(invalid);
|
|
532
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
428
533
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
534
|
+
// Step 7: Run eviction rules (enforce pool size limit)
|
|
535
|
+
if (added.length > 0) {
|
|
536
|
+
const feePayers = added.map(meta => meta.feePayer);
|
|
537
|
+
const uniqueFeePayers = new Set<string>(feePayers);
|
|
538
|
+
await this.#evictionManager.evictAfterNewTxs(
|
|
539
|
+
added.map(m => m.txHash),
|
|
540
|
+
[...uniqueFeePayers],
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
438
544
|
}
|
|
439
545
|
|
|
440
|
-
async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
|
|
546
|
+
async handlePrunedBlocks(latestBlock: L2BlockId, options?: { deleteAllTxs?: boolean }): Promise<void> {
|
|
441
547
|
// Step 1: Find transactions mined after the prune point
|
|
442
548
|
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
443
549
|
if (txsToUnmine.length === 0) {
|
|
@@ -447,46 +553,60 @@ export class TxPoolV2Impl {
|
|
|
447
553
|
|
|
448
554
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
449
555
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
556
|
+
await this.#store.transactionAsync(async () => {
|
|
557
|
+
// Step 2: Mark ALL un-mined txs with their original mined block number
|
|
558
|
+
// This ensures they get soft-deleted if removed later, and only hard-deleted
|
|
559
|
+
// when their original mined block is finalized
|
|
560
|
+
await this.#deletedPool.markFromPrunedBlock(
|
|
561
|
+
txsToUnmine.map(m => ({
|
|
562
|
+
txHash: m.txHash,
|
|
563
|
+
minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
|
|
564
|
+
})),
|
|
565
|
+
);
|
|
459
566
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
567
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
568
|
+
for (const meta of txsToUnmine) {
|
|
569
|
+
this.#indices.markAsUnmined(meta);
|
|
570
|
+
}
|
|
464
571
|
|
|
465
|
-
|
|
466
|
-
|
|
572
|
+
// If deleteAllTxs is set (epoch prune), delete all un-mined txs and return early
|
|
573
|
+
if (options?.deleteAllTxs) {
|
|
574
|
+
const allTxHashes = txsToUnmine.map(m => m.txHash);
|
|
575
|
+
await this.#deleteTxsBatch(allTxHashes);
|
|
576
|
+
this.#log.info(
|
|
577
|
+
`Handled prune to block ${latestBlock.number} with deleteAllTxs: deleted ${allTxHashes.length} txs`,
|
|
578
|
+
);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
467
581
|
|
|
468
|
-
|
|
469
|
-
|
|
582
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
583
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
470
584
|
|
|
471
|
-
|
|
472
|
-
|
|
585
|
+
// Step 5: Validate for pending pool
|
|
586
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
473
587
|
|
|
474
|
-
|
|
475
|
-
|
|
588
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
589
|
+
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
476
590
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
);
|
|
591
|
+
// Step 7: Delete invalid txs and evict conflict losers
|
|
592
|
+
await this.#deleteTxsBatch(invalid);
|
|
593
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
481
594
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
595
|
+
this.#log.info(
|
|
596
|
+
`Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`,
|
|
597
|
+
{ txHashesRestored: valid.map(m => m.txHash), txHashesInvalid: invalid, txHashesEvicted: toEvict },
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
// Step 8: Run eviction rules for ALL pending txs (not just restored ones)
|
|
601
|
+
// This handles cases like existing pending txs with invalid fee payer balances
|
|
602
|
+
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
603
|
+
});
|
|
485
604
|
}
|
|
486
605
|
|
|
487
606
|
async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
|
|
488
|
-
|
|
489
|
-
|
|
607
|
+
await this.#store.transactionAsync(async () => {
|
|
608
|
+
await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
|
|
609
|
+
});
|
|
490
610
|
|
|
491
611
|
this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
|
|
492
612
|
}
|
|
@@ -497,27 +617,29 @@ export class TxPoolV2Impl {
|
|
|
497
617
|
// Step 1: Find mined txs at or before finalized block
|
|
498
618
|
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
499
619
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
620
|
+
await this.#store.transactionAsync(async () => {
|
|
621
|
+
// Step 2: Collect mined txs for archiving (before deletion)
|
|
622
|
+
const txsToArchive: Tx[] = [];
|
|
623
|
+
if (this.#archive.isEnabled()) {
|
|
624
|
+
for (const txHashStr of minedTxsToFinalize) {
|
|
625
|
+
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
626
|
+
if (buffer) {
|
|
627
|
+
txsToArchive.push(Tx.fromBuffer(buffer));
|
|
628
|
+
}
|
|
507
629
|
}
|
|
508
630
|
}
|
|
509
|
-
}
|
|
510
631
|
|
|
511
|
-
|
|
512
|
-
|
|
632
|
+
// Step 3: Delete mined txs from active pool
|
|
633
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
513
634
|
|
|
514
|
-
|
|
515
|
-
|
|
635
|
+
// Step 4: Finalize soft-deleted txs
|
|
636
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
516
637
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
638
|
+
// Step 5: Archive mined txs
|
|
639
|
+
if (txsToArchive.length > 0) {
|
|
640
|
+
await this.#archive.archiveTxs(txsToArchive);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
521
643
|
|
|
522
644
|
if (minedTxsToFinalize.length > 0) {
|
|
523
645
|
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
|
|
@@ -637,8 +759,17 @@ export class TxPoolV2Impl {
|
|
|
637
759
|
|
|
638
760
|
// === Metrics ===
|
|
639
761
|
|
|
640
|
-
countTxs(): {
|
|
641
|
-
|
|
762
|
+
countTxs(): {
|
|
763
|
+
pending: number;
|
|
764
|
+
protected: number;
|
|
765
|
+
mined: number;
|
|
766
|
+
softDeleted: number;
|
|
767
|
+
totalMetadataBytes: number;
|
|
768
|
+
} {
|
|
769
|
+
return {
|
|
770
|
+
...this.#indices.countTxs(),
|
|
771
|
+
softDeleted: this.#deletedPool.getSoftDeletedCount(),
|
|
772
|
+
};
|
|
642
773
|
}
|
|
643
774
|
|
|
644
775
|
// ============================================================================
|
|
@@ -653,9 +784,10 @@ export class TxPoolV2Impl {
|
|
|
653
784
|
tx: Tx,
|
|
654
785
|
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
655
786
|
opts: { source?: string } = {},
|
|
787
|
+
precomputedMeta?: TxMetaData,
|
|
656
788
|
): Promise<TxMetaData> {
|
|
657
789
|
const txHashStr = tx.getTxHash().toString();
|
|
658
|
-
const meta = await buildTxMetaData(tx);
|
|
790
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
659
791
|
meta.receivedAt = this.#dateProvider.now();
|
|
660
792
|
|
|
661
793
|
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
@@ -672,9 +804,11 @@ export class TxPoolV2Impl {
|
|
|
672
804
|
}
|
|
673
805
|
|
|
674
806
|
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
675
|
-
this.#log.
|
|
807
|
+
this.#log.debug(`Added tx ${txHashStr} as ${stateStr}`, {
|
|
676
808
|
eventName: 'tx-added-to-pool',
|
|
809
|
+
txHash: txHashStr,
|
|
677
810
|
state: stateStr,
|
|
811
|
+
source: opts.source,
|
|
678
812
|
});
|
|
679
813
|
|
|
680
814
|
return meta;
|
|
@@ -702,6 +836,29 @@ export class TxPoolV2Impl {
|
|
|
702
836
|
}
|
|
703
837
|
}
|
|
704
838
|
|
|
839
|
+
/** Evicts transactions: records eviction metric with reason, caches hashes, then deletes. */
|
|
840
|
+
async #evictTxs(txHashes: string[], reason: string): Promise<void> {
|
|
841
|
+
if (txHashes.length === 0) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
this.#instrumentation.recordEvictions(txHashes.length, reason);
|
|
845
|
+
for (const txHashStr of txHashes) {
|
|
846
|
+
this.#log.debug(`Evicting tx ${txHashStr}`, { txHash: txHashStr, reason });
|
|
847
|
+
this.#addToEvictedCache(txHashStr);
|
|
848
|
+
}
|
|
849
|
+
await this.#deleteTxsBatch(txHashes);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/** Adds a tx hash to the bounded evicted cache, evicting the oldest entry if at capacity. */
|
|
853
|
+
#addToEvictedCache(txHashStr: string): void {
|
|
854
|
+
if (this.#evictedTxHashes.size >= this.#config.evictedTxCacheSize) {
|
|
855
|
+
// FIFO eviction: remove the first (oldest) entry
|
|
856
|
+
const oldest = this.#evictedTxHashes.values().next().value!;
|
|
857
|
+
this.#evictedTxHashes.delete(oldest);
|
|
858
|
+
}
|
|
859
|
+
this.#evictedTxHashes.add(txHashStr);
|
|
860
|
+
}
|
|
861
|
+
|
|
705
862
|
// ============================================================================
|
|
706
863
|
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
707
864
|
// ============================================================================
|
|
@@ -857,7 +1014,9 @@ export class TxPoolV2Impl {
|
|
|
857
1014
|
if (preAddResult.shouldIgnore) {
|
|
858
1015
|
// Transaction rejected - mark for deletion from DB
|
|
859
1016
|
rejected.push(meta.txHash);
|
|
860
|
-
this.#log.debug(
|
|
1017
|
+
this.#log.debug(
|
|
1018
|
+
`Rejected tx ${meta.txHash} during rebuild: ${preAddResult.reason?.message ?? 'unknown reason'}`,
|
|
1019
|
+
);
|
|
861
1020
|
continue;
|
|
862
1021
|
}
|
|
863
1022
|
|
|
@@ -893,7 +1052,7 @@ export class TxPoolV2Impl {
|
|
|
893
1052
|
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
894
1053
|
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
895
1054
|
getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
|
|
896
|
-
deleteTxs: (txHashes: string[]) => this.#
|
|
1055
|
+
deleteTxs: (txHashes: string[], reason?: string) => this.#evictTxs(txHashes, reason ?? 'unknown'),
|
|
897
1056
|
};
|
|
898
1057
|
}
|
|
899
1058
|
|