@aztec/p2p 0.0.1-commit.858058eac → 0.0.1-commit.85d7d01
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 +23 -30
- package/dest/client/interface.d.ts +14 -19
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +9 -18
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +52 -72
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +6 -7
- package/dest/config.d.ts +13 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +5 -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/attestation_pool/attestation_pool.d.ts +4 -2
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +5 -0
- package/dest/mem_pools/attestation_pool/mocks.d.ts +2 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +2 -2
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +3 -3
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +30 -13
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +91 -20
- 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.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +5 -2
- 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 +7 -5
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +7 -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 +14 -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 +20 -6
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +4 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +34 -8
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +76 -10
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +12 -3
- 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 +36 -14
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +9 -4
- 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 +11 -6
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +13 -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 +297 -143
- 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/msg_validators/tx_validator/timestamp_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.js +6 -6
- 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/config.d.ts +13 -1
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +30 -0
- 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 +38 -29
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.js +126 -77
- package/dest/services/tx_collection/file_store_tx_source.d.ts +16 -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 +49 -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 +5 -3
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +17 -12
- package/dest/services/tx_collection/tx_collection.d.ts +9 -6
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +26 -10
- 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.d.ts +3 -2
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_file_store/tx_file_store.js +9 -6
- 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 +8 -3
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +7 -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 +13 -13
- 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 +39 -48
- package/src/client/interface.ts +17 -20
- package/src/client/p2p_client.ts +60 -104
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +19 -10
- package/src/config.ts +10 -10
- package/src/errors/tx-pool.error.ts +12 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +8 -0
- package/src/mem_pools/attestation_pool/mocks.ts +2 -1
- package/src/mem_pools/tx_pool/README.md +1 -1
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/README.md +43 -27
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +109 -22
- 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 +5 -2
- 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 +5 -5
- 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 +14 -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 +21 -6
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +107 -17
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +43 -16
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +18 -7
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +326 -138
- 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/msg_validators/tx_validator/timestamp_validator.ts +7 -7
- 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/config.ts +42 -0
- package/src/services/tx_collection/fast_tx_collection.ts +51 -30
- package/src/services/tx_collection/file_store_tx_collection.ts +143 -93
- package/src/services/tx_collection/file_store_tx_source.ts +64 -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 +17 -13
- package/src/services/tx_collection/tx_collection.ts +45 -14
- 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 +6 -4
- 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 +11 -3
- package/src/testbench/p2p_client_testbench_worker.ts +22 -19
- package/src/util.ts +7 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
import type { DateProvider } from '@aztec/foundation/timer';
|
|
3
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
4
5
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
5
6
|
import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
@@ -8,6 +9,7 @@ import type { L2Block, L2BlockId, L2BlockSource } from '@aztec/stdlib/block';
|
|
|
8
9
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
9
10
|
import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
10
11
|
import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
|
|
12
|
+
import type { TelemetryClient } from '@aztec/telemetry-client';
|
|
11
13
|
|
|
12
14
|
import { TxArchive } from './archive/index.js';
|
|
13
15
|
import { DeletedPool } from './deleted_pool.js';
|
|
@@ -21,8 +23,12 @@ import {
|
|
|
21
23
|
LowPriorityPreAddRule,
|
|
22
24
|
NullifierConflictRule,
|
|
23
25
|
type PoolOperations,
|
|
26
|
+
type PreAddContext,
|
|
24
27
|
type PreAddPoolAccess,
|
|
28
|
+
TxPoolRejectionCode,
|
|
29
|
+
type TxPoolRejectionError,
|
|
25
30
|
} from './eviction/index.js';
|
|
31
|
+
import { TxPoolV2Instrumentation } from './instrumentation.js';
|
|
26
32
|
import {
|
|
27
33
|
type AddTxsResult,
|
|
28
34
|
DEFAULT_TX_POOL_V2_CONFIG,
|
|
@@ -64,6 +70,9 @@ export class TxPoolV2Impl {
|
|
|
64
70
|
#archive: TxArchive;
|
|
65
71
|
#deletedPool: DeletedPool;
|
|
66
72
|
#evictionManager: EvictionManager;
|
|
73
|
+
#dateProvider: DateProvider;
|
|
74
|
+
#instrumentation: TxPoolV2Instrumentation;
|
|
75
|
+
#evictedTxHashes: Set<string> = new Set();
|
|
67
76
|
#log: Logger;
|
|
68
77
|
#callbacks: TxPoolV2Callbacks;
|
|
69
78
|
|
|
@@ -72,7 +81,9 @@ export class TxPoolV2Impl {
|
|
|
72
81
|
archiveStore: AztecAsyncKVStore,
|
|
73
82
|
deps: TxPoolV2Dependencies,
|
|
74
83
|
callbacks: TxPoolV2Callbacks,
|
|
84
|
+
telemetry: TelemetryClient,
|
|
75
85
|
config: Partial<TxPoolV2Config> = {},
|
|
86
|
+
dateProvider: DateProvider,
|
|
76
87
|
log: Logger,
|
|
77
88
|
) {
|
|
78
89
|
this.#store = store;
|
|
@@ -85,6 +96,8 @@ export class TxPoolV2Impl {
|
|
|
85
96
|
this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
|
|
86
97
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
87
98
|
this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
|
|
99
|
+
this.#dateProvider = dateProvider;
|
|
100
|
+
this.#instrumentation = new TxPoolV2Instrumentation(telemetry, () => this.#indices.getTotalMetadataBytes());
|
|
88
101
|
this.#log = log;
|
|
89
102
|
this.#callbacks = callbacks;
|
|
90
103
|
|
|
@@ -164,16 +177,43 @@ export class TxPoolV2Impl {
|
|
|
164
177
|
await this.#txsDB.delete(txHashStr);
|
|
165
178
|
}
|
|
166
179
|
});
|
|
167
|
-
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup
|
|
180
|
+
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, { txHashes: toDelete });
|
|
168
181
|
}
|
|
169
182
|
|
|
170
|
-
async addPendingTxs(txs: Tx[], opts: { source?: string }): Promise<AddTxsResult> {
|
|
183
|
+
async addPendingTxs(txs: Tx[], opts: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult> {
|
|
171
184
|
const accepted: TxHash[] = [];
|
|
172
185
|
const ignored: TxHash[] = [];
|
|
173
186
|
const rejected: TxHash[] = [];
|
|
187
|
+
const errors = new Map<string, TxPoolRejectionError>();
|
|
174
188
|
const acceptedPending = new Set<string>();
|
|
175
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.
|
|
176
214
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
215
|
+
const preAddContext: PreAddContext | undefined =
|
|
216
|
+
opts.feeComparisonOnly !== undefined ? { feeComparisonOnly: opts.feeComparisonOnly } : undefined;
|
|
177
217
|
|
|
178
218
|
await this.#store.transactionAsync(async () => {
|
|
179
219
|
for (const tx of txs) {
|
|
@@ -186,30 +226,46 @@ export class TxPoolV2Impl {
|
|
|
186
226
|
continue;
|
|
187
227
|
}
|
|
188
228
|
|
|
189
|
-
|
|
190
|
-
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
229
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
191
230
|
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
192
231
|
|
|
193
232
|
if (minedBlockId) {
|
|
194
233
|
// Already mined - add directly (protection already set if pre-protected)
|
|
195
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
234
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
196
235
|
accepted.push(txHash);
|
|
197
236
|
} else if (preProtectedSlot !== undefined) {
|
|
198
237
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
199
|
-
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
238
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
200
239
|
accepted.push(txHash);
|
|
240
|
+
} else if (!isValid) {
|
|
241
|
+
// Failed pre-computed validation
|
|
242
|
+
rejected.push(txHash);
|
|
201
243
|
} else {
|
|
202
|
-
// Regular pending tx -
|
|
203
|
-
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
|
+
);
|
|
204
255
|
if (result.status === 'accepted') {
|
|
205
256
|
acceptedPending.add(txHashStr);
|
|
206
|
-
} else if (result.status === 'rejected') {
|
|
207
|
-
rejected.push(txHash);
|
|
208
257
|
} else {
|
|
209
258
|
ignored.push(txHash);
|
|
210
259
|
}
|
|
211
260
|
}
|
|
212
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
|
+
}
|
|
213
269
|
});
|
|
214
270
|
|
|
215
271
|
// Build final accepted list for pending txs (excludes intra-batch evictions)
|
|
@@ -217,58 +273,80 @@ export class TxPoolV2Impl {
|
|
|
217
273
|
accepted.push(TxHash.fromString(txHashStr));
|
|
218
274
|
}
|
|
219
275
|
|
|
220
|
-
//
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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);
|
|
225
282
|
}
|
|
226
283
|
|
|
227
|
-
return { accepted, ignored, rejected };
|
|
284
|
+
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
228
285
|
}
|
|
229
286
|
|
|
230
|
-
/**
|
|
287
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
231
288
|
async #tryAddRegularPendingTx(
|
|
232
289
|
tx: Tx,
|
|
290
|
+
precomputedMeta: TxMetaData,
|
|
233
291
|
opts: { source?: string },
|
|
234
292
|
poolAccess: PreAddPoolAccess,
|
|
235
293
|
acceptedPending: Set<string>,
|
|
236
294
|
ignored: TxHash[],
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// Build metadata and validate using metadata
|
|
242
|
-
const meta = await buildTxMetaData(tx);
|
|
243
|
-
if (!(await this.#validateMeta(meta))) {
|
|
244
|
-
return { status: 'rejected' };
|
|
245
|
-
}
|
|
295
|
+
errors: Map<string, TxPoolRejectionError>,
|
|
296
|
+
preAddContext?: PreAddContext,
|
|
297
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
298
|
+
const txHashStr = tx.getTxHash().toString();
|
|
246
299
|
|
|
247
300
|
// Run pre-add rules
|
|
248
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(
|
|
301
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
249
302
|
|
|
250
303
|
if (preAddResult.shouldIgnore) {
|
|
251
|
-
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
|
+
}
|
|
252
308
|
return { status: 'ignored' };
|
|
253
309
|
}
|
|
254
310
|
|
|
255
|
-
// Evict conflicts
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
+
}
|
|
263
335
|
}
|
|
264
336
|
}
|
|
265
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
|
+
|
|
266
344
|
// Add the transaction
|
|
267
|
-
await this.#addTx(tx, 'pending', opts);
|
|
345
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
268
346
|
return { status: 'accepted' };
|
|
269
347
|
}
|
|
270
348
|
|
|
271
|
-
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
349
|
+
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
272
350
|
const txHashStr = tx.getTxHash().toString();
|
|
273
351
|
|
|
274
352
|
// Check if already in pool
|
|
@@ -276,14 +354,8 @@ export class TxPoolV2Impl {
|
|
|
276
354
|
return 'ignored';
|
|
277
355
|
}
|
|
278
356
|
|
|
279
|
-
// Build metadata and
|
|
357
|
+
// Build metadata and check pre-add rules
|
|
280
358
|
const meta = await buildTxMetaData(tx);
|
|
281
|
-
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
282
|
-
if (validationResult !== true) {
|
|
283
|
-
return 'rejected';
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Use pre-add rules
|
|
287
359
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
288
360
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
289
361
|
|
|
@@ -320,22 +392,57 @@ export class TxPoolV2Impl {
|
|
|
320
392
|
});
|
|
321
393
|
}
|
|
322
394
|
|
|
323
|
-
protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
|
|
395
|
+
async protectTxs(txHashes: TxHash[], block: BlockHeader): Promise<TxHash[]> {
|
|
324
396
|
const slotNumber = block.globalVariables.slotNumber;
|
|
325
397
|
const missing: TxHash[] = [];
|
|
398
|
+
let softDeletedHits = 0;
|
|
399
|
+
let missingPreviouslyEvicted = 0;
|
|
326
400
|
|
|
327
|
-
|
|
328
|
-
const
|
|
401
|
+
await this.#store.transactionAsync(async () => {
|
|
402
|
+
for (const txHash of txHashes) {
|
|
403
|
+
const txHashStr = txHash.toString();
|
|
329
404
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
+
}
|
|
337
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);
|
|
338
438
|
}
|
|
439
|
+
if (missingPreviouslyEvicted > 0) {
|
|
440
|
+
this.#instrumentation.recordMissingPreviouslyEvicted(missingPreviouslyEvicted);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
this.#log.info(
|
|
444
|
+
`Protected ${txHashes.length} txs, missing: ${missing.length}, soft-deleted hits: ${softDeletedHits}`,
|
|
445
|
+
);
|
|
339
446
|
|
|
340
447
|
return missing;
|
|
341
448
|
}
|
|
@@ -380,54 +487,63 @@ export class TxPoolV2Impl {
|
|
|
380
487
|
}
|
|
381
488
|
}
|
|
382
489
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
+
}
|
|
388
496
|
|
|
389
|
-
|
|
390
|
-
|
|
497
|
+
// Step 5: Run post-event eviction rules (inside transaction for atomicity)
|
|
498
|
+
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
499
|
+
});
|
|
391
500
|
|
|
392
501
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
393
502
|
}
|
|
394
503
|
|
|
395
504
|
async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
|
|
396
|
-
|
|
397
|
-
|
|
505
|
+
await this.#store.transactionAsync(async () => {
|
|
506
|
+
// Step 0: Clean up slot-deleted txs from previous slots
|
|
507
|
+
await this.#deletedPool.cleanupSlotDeleted(slotNumber);
|
|
398
508
|
|
|
399
|
-
|
|
400
|
-
|
|
509
|
+
// Step 1: Find expired protected txs
|
|
510
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
401
511
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if (txsToRestore.length === 0) {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
512
|
+
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
513
|
+
this.#indices.clearProtection(expiredProtected);
|
|
407
514
|
|
|
408
|
-
|
|
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
|
+
}
|
|
409
521
|
|
|
410
|
-
|
|
411
|
-
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
522
|
+
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
412
523
|
|
|
413
|
-
|
|
414
|
-
|
|
524
|
+
// Step 4: Validate for pending pool
|
|
525
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
415
526
|
|
|
416
|
-
|
|
417
|
-
|
|
527
|
+
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
528
|
+
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
418
529
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
530
|
+
// Step 6: Delete invalid txs and evict conflict losers
|
|
531
|
+
await this.#deleteTxsBatch(invalid);
|
|
532
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
533
|
+
|
|
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
|
+
});
|
|
428
544
|
}
|
|
429
545
|
|
|
430
|
-
async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
|
|
546
|
+
async handlePrunedBlocks(latestBlock: L2BlockId, options?: { deleteAllTxs?: boolean }): Promise<void> {
|
|
431
547
|
// Step 1: Find transactions mined after the prune point
|
|
432
548
|
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
433
549
|
if (txsToUnmine.length === 0) {
|
|
@@ -437,43 +553,62 @@ export class TxPoolV2Impl {
|
|
|
437
553
|
|
|
438
554
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
439
555
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
+
);
|
|
449
566
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
567
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
568
|
+
for (const meta of txsToUnmine) {
|
|
569
|
+
this.#indices.markAsUnmined(meta);
|
|
570
|
+
}
|
|
454
571
|
|
|
455
|
-
|
|
456
|
-
|
|
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
|
+
}
|
|
581
|
+
|
|
582
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
583
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
457
584
|
|
|
458
|
-
|
|
459
|
-
|
|
585
|
+
// Step 5: Validate for pending pool
|
|
586
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
460
587
|
|
|
461
|
-
|
|
462
|
-
|
|
588
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
589
|
+
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
463
590
|
|
|
464
|
-
|
|
465
|
-
|
|
591
|
+
// Step 7: Delete invalid txs and evict conflict losers
|
|
592
|
+
await this.#deleteTxsBatch(invalid);
|
|
593
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
466
594
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
+
});
|
|
470
604
|
}
|
|
471
605
|
|
|
472
606
|
async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
|
|
473
|
-
|
|
474
|
-
|
|
607
|
+
await this.#store.transactionAsync(async () => {
|
|
608
|
+
await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
|
|
609
|
+
});
|
|
475
610
|
|
|
476
|
-
this.#log.info(`Deleted ${txHashes.length} failed txs
|
|
611
|
+
this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
|
|
477
612
|
}
|
|
478
613
|
|
|
479
614
|
async handleFinalizedBlock(block: BlockHeader): Promise<void> {
|
|
@@ -482,30 +617,34 @@ export class TxPoolV2Impl {
|
|
|
482
617
|
// Step 1: Find mined txs at or before finalized block
|
|
483
618
|
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
484
619
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
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
|
+
}
|
|
492
629
|
}
|
|
493
630
|
}
|
|
494
|
-
}
|
|
495
631
|
|
|
496
|
-
|
|
497
|
-
|
|
632
|
+
// Step 3: Delete mined txs from active pool
|
|
633
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
498
634
|
|
|
499
|
-
|
|
500
|
-
|
|
635
|
+
// Step 4: Finalize soft-deleted txs
|
|
636
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
501
637
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
638
|
+
// Step 5: Archive mined txs
|
|
639
|
+
if (txsToArchive.length > 0) {
|
|
640
|
+
await this.#archive.archiveTxs(txsToArchive);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
506
643
|
|
|
507
644
|
if (minedTxsToFinalize.length > 0) {
|
|
508
|
-
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}
|
|
645
|
+
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
|
|
646
|
+
txHashes: minedTxsToFinalize,
|
|
647
|
+
});
|
|
509
648
|
}
|
|
510
649
|
}
|
|
511
650
|
|
|
@@ -549,6 +688,13 @@ export class TxPoolV2Impl {
|
|
|
549
688
|
return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
550
689
|
}
|
|
551
690
|
|
|
691
|
+
getEligiblePendingTxHashes(): TxHash[] {
|
|
692
|
+
const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
|
|
693
|
+
return [...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)].map(hash =>
|
|
694
|
+
TxHash.fromString(hash),
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
|
|
552
698
|
getPendingTxCount(): number {
|
|
553
699
|
return this.#indices.getPendingTxCount();
|
|
554
700
|
}
|
|
@@ -593,6 +739,9 @@ export class TxPoolV2Impl {
|
|
|
593
739
|
this.#config.archivedTxLimit = config.archivedTxLimit;
|
|
594
740
|
this.#archive.updateLimit(config.archivedTxLimit);
|
|
595
741
|
}
|
|
742
|
+
if (config.minTxPoolAgeMs !== undefined) {
|
|
743
|
+
this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
|
|
744
|
+
}
|
|
596
745
|
// Update eviction rules with new config
|
|
597
746
|
this.#evictionManager.updateConfig(config);
|
|
598
747
|
}
|
|
@@ -610,8 +759,17 @@ export class TxPoolV2Impl {
|
|
|
610
759
|
|
|
611
760
|
// === Metrics ===
|
|
612
761
|
|
|
613
|
-
countTxs(): {
|
|
614
|
-
|
|
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
|
+
};
|
|
615
773
|
}
|
|
616
774
|
|
|
617
775
|
// ============================================================================
|
|
@@ -626,11 +784,14 @@ export class TxPoolV2Impl {
|
|
|
626
784
|
tx: Tx,
|
|
627
785
|
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
628
786
|
opts: { source?: string } = {},
|
|
787
|
+
precomputedMeta?: TxMetaData,
|
|
629
788
|
): Promise<TxMetaData> {
|
|
630
789
|
const txHashStr = tx.getTxHash().toString();
|
|
631
|
-
const meta = await buildTxMetaData(tx);
|
|
790
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
791
|
+
meta.receivedAt = this.#dateProvider.now();
|
|
632
792
|
|
|
633
793
|
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
794
|
+
await this.#deletedPool.clearSoftDeleted(txHashStr);
|
|
634
795
|
this.#callbacks.onTxsAdded([tx], opts);
|
|
635
796
|
|
|
636
797
|
if (state === 'pending') {
|
|
@@ -643,9 +804,11 @@ export class TxPoolV2Impl {
|
|
|
643
804
|
}
|
|
644
805
|
|
|
645
806
|
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
646
|
-
this.#log.
|
|
807
|
+
this.#log.debug(`Added tx ${txHashStr} as ${stateStr}`, {
|
|
647
808
|
eventName: 'tx-added-to-pool',
|
|
809
|
+
txHash: txHashStr,
|
|
648
810
|
state: stateStr,
|
|
811
|
+
source: opts.source,
|
|
649
812
|
});
|
|
650
813
|
|
|
651
814
|
return meta;
|
|
@@ -673,6 +836,29 @@ export class TxPoolV2Impl {
|
|
|
673
836
|
}
|
|
674
837
|
}
|
|
675
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
|
+
|
|
676
862
|
// ============================================================================
|
|
677
863
|
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
678
864
|
// ============================================================================
|
|
@@ -828,7 +1014,9 @@ export class TxPoolV2Impl {
|
|
|
828
1014
|
if (preAddResult.shouldIgnore) {
|
|
829
1015
|
// Transaction rejected - mark for deletion from DB
|
|
830
1016
|
rejected.push(meta.txHash);
|
|
831
|
-
this.#log.debug(
|
|
1017
|
+
this.#log.debug(
|
|
1018
|
+
`Rejected tx ${meta.txHash} during rebuild: ${preAddResult.reason?.message ?? 'unknown reason'}`,
|
|
1019
|
+
);
|
|
832
1020
|
continue;
|
|
833
1021
|
}
|
|
834
1022
|
|
|
@@ -864,7 +1052,7 @@ export class TxPoolV2Impl {
|
|
|
864
1052
|
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
865
1053
|
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
866
1054
|
getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
|
|
867
|
-
deleteTxs: (txHashes: string[]) => this.#
|
|
1055
|
+
deleteTxs: (txHashes: string[], reason?: string) => this.#evictTxs(txHashes, reason ?? 'unknown'),
|
|
868
1056
|
};
|
|
869
1057
|
}
|
|
870
1058
|
|