@aztec/p2p 0.0.1-commit.43c09e3f → 0.0.1-commit.4ad48494d
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 +5 -5
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +44 -10
- package/dest/client/interface.d.ts +37 -15
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +35 -36
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +114 -138
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
- package/dest/config.d.ts +23 -5
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +16 -1
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +104 -88
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +441 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +353 -87
- package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
- package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/index.js +1 -2
- package/dest/mem_pools/index.d.ts +3 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/index.js +1 -1
- package/dest/mem_pools/interface.d.ts +5 -5
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +102 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +242 -0
- 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 +3 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +3 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +3 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +1 -1
- 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 +3 -1
- package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -1
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +11 -3
- 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 +27 -3
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +39 -5
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +105 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +345 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +4 -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 +12 -2
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +4 -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 +259 -520
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +3 -3
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.js +2 -2
- package/dest/services/dummy_service.d.ts +10 -2
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +6 -0
- package/dest/services/encoding.d.ts +2 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +2 -2
- package/dest/services/gossipsub/index.d.ts +3 -0
- package/dest/services/gossipsub/index.d.ts.map +1 -0
- package/dest/services/gossipsub/index.js +2 -0
- package/dest/services/gossipsub/scoring.d.ts +21 -3
- package/dest/services/gossipsub/scoring.d.ts.map +1 -1
- package/dest/services/gossipsub/scoring.js +24 -7
- package/dest/services/gossipsub/topic_score_params.d.ts +161 -0
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
- package/dest/services/gossipsub/topic_score_params.js +324 -0
- package/dest/services/libp2p/libp2p_service.d.ts +84 -35
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +368 -273
- package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +25 -2
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -4
- 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 +8 -8
- package/dest/services/reqresp/interface.d.ts +10 -1
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +15 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +21 -10
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +27 -11
- package/dest/services/reqresp/protocols/tx.d.ts +7 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +20 -0
- 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 +11 -4
- package/dest/services/service.d.ts +35 -1
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +22 -4
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +49 -3
- package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -5
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +27 -17
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_collection.js +165 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts +28 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_source.js +59 -0
- package/dest/services/tx_collection/index.d.ts +3 -2
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +1 -0
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +12 -12
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +4 -5
- package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -2
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +55 -23
- package/dest/services/tx_collection/tx_collection.d.ts +19 -7
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +75 -3
- package/dest/services/tx_collection/tx_collection_sink.d.ts +15 -6
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +13 -7
- package/dest/services/tx_file_store/config.d.ts +1 -3
- package/dest/services/tx_file_store/config.d.ts.map +1 -1
- package/dest/services/tx_file_store/config.js +0 -4
- package/dest/services/tx_file_store/tx_file_store.d.ts +4 -3
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_file_store/tx_file_store.js +8 -5
- 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 +5 -4
- package/dest/test-helpers/make-test-p2p-clients.d.ts +3 -3
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.d.ts +27 -1
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +97 -2
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +2 -1
- package/dest/test-helpers/testbench-utils.d.ts +40 -38
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +128 -59
- package/dest/testbench/p2p_client_testbench_worker.js +2 -2
- package/package.json +14 -14
- package/src/client/factory.ts +81 -13
- package/src/client/interface.ts +45 -14
- package/src/client/p2p_client.ts +151 -161
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
- package/src/config.ts +34 -2
- package/src/index.ts +1 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +496 -91
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
- package/src/mem_pools/attestation_pool/index.ts +9 -2
- package/src/mem_pools/index.ts +4 -1
- package/src/mem_pools/interface.ts +4 -4
- package/src/mem_pools/tx_pool_v2/README.md +103 -16
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +310 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +3 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +3 -1
- package/src/mem_pools/tx_pool_v2/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +10 -2
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +57 -6
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +433 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +10 -1
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +266 -607
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +2 -2
- package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
- package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
- package/src/msg_validators/tx_validator/timestamp_validator.ts +19 -14
- package/src/services/dummy_service.ts +12 -0
- package/src/services/encoding.ts +2 -2
- package/src/services/gossipsub/README.md +626 -0
- package/src/services/gossipsub/index.ts +2 -0
- package/src/services/gossipsub/scoring.ts +29 -5
- package/src/services/gossipsub/topic_score_params.ts +451 -0
- package/src/services/libp2p/libp2p_service.ts +370 -275
- package/src/services/peer-manager/peer_scoring.ts +25 -0
- package/src/services/reqresp/batch-tx-requester/README.md +7 -7
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +11 -11
- package/src/services/reqresp/interface.ts +26 -1
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +23 -14
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +38 -15
- package/src/services/reqresp/protocols/tx.ts +22 -0
- package/src/services/reqresp/reqresp.ts +13 -3
- package/src/services/service.ts +40 -0
- package/src/services/tx_collection/config.ts +74 -6
- package/src/services/tx_collection/fast_tx_collection.ts +28 -26
- package/src/services/tx_collection/file_store_tx_collection.ts +198 -0
- package/src/services/tx_collection/file_store_tx_source.ts +73 -0
- package/src/services/tx_collection/index.ts +2 -1
- package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
- package/src/services/tx_collection/slow_tx_collection.ts +64 -30
- package/src/services/tx_collection/tx_collection.ts +109 -13
- package/src/services/tx_collection/tx_collection_sink.ts +17 -7
- package/src/services/tx_file_store/config.ts +0 -6
- package/src/services/tx_file_store/tx_file_store.ts +9 -7
- package/src/services/tx_provider.ts +8 -7
- package/src/test-helpers/make-test-p2p-clients.ts +3 -3
- package/src/test-helpers/mock-pubsub.ts +133 -3
- package/src/test-helpers/reqresp-nodes.ts +2 -1
- package/src/test-helpers/testbench-utils.ts +127 -71
- package/src/testbench/p2p_client_testbench_worker.ts +2 -2
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
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';
|
|
@@ -10,6 +11,7 @@ import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
|
10
11
|
import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
|
|
11
12
|
|
|
12
13
|
import { TxArchive } from './archive/index.js';
|
|
14
|
+
import { DeletedPool } from './deleted_pool.js';
|
|
13
15
|
import {
|
|
14
16
|
EvictionManager,
|
|
15
17
|
FeePayerBalanceEvictionRule,
|
|
@@ -29,14 +31,8 @@ import {
|
|
|
29
31
|
type TxPoolV2Config,
|
|
30
32
|
type TxPoolV2Dependencies,
|
|
31
33
|
} from './interfaces.js';
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
type TxState,
|
|
35
|
-
buildTxMetaData,
|
|
36
|
-
checkNullifierConflict,
|
|
37
|
-
compareFee,
|
|
38
|
-
compareTxHash,
|
|
39
|
-
} from './tx_metadata.js';
|
|
34
|
+
import { type TxMetaData, type TxState, buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
|
|
35
|
+
import { TxPoolIndices } from './tx_pool_indices.js';
|
|
40
36
|
|
|
41
37
|
/**
|
|
42
38
|
* Callbacks for the implementation to notify the outer class about events and metrics.
|
|
@@ -59,27 +55,17 @@ export class TxPoolV2Impl {
|
|
|
59
55
|
// === Dependencies ===
|
|
60
56
|
#l2BlockSource: L2BlockSource;
|
|
61
57
|
#worldStateSynchronizer: WorldStateSynchronizer;
|
|
62
|
-
#
|
|
58
|
+
#createTxValidator: TxPoolV2Dependencies['createTxValidator'];
|
|
63
59
|
|
|
64
60
|
// === In-Memory Indices ===
|
|
65
|
-
|
|
66
|
-
#metadata: Map<string, TxMetaData> = new Map();
|
|
67
|
-
/** Nullifier to txHash index (pending txs only) */
|
|
68
|
-
#nullifierToTxHash: Map<string, string> = new Map();
|
|
69
|
-
/** Fee payer to txHashes index (pending txs only) */
|
|
70
|
-
#feePayerToTxHashes: Map<string, Set<string>> = new Map();
|
|
71
|
-
/**
|
|
72
|
-
* Pending txHashes grouped by priority fee.
|
|
73
|
-
* Outer map: priorityFee -> Set of txHashes at that fee level.
|
|
74
|
-
*/
|
|
75
|
-
#pendingByPriority: Map<bigint, Set<string>> = new Map();
|
|
76
|
-
/** Protected transactions: txHash -> slotNumber. Includes txs we have and txs we expect to receive. */
|
|
77
|
-
#protectedTransactions: Map<string, SlotNumber> = new Map();
|
|
61
|
+
#indices: TxPoolIndices = new TxPoolIndices();
|
|
78
62
|
|
|
79
63
|
// === Config & Services ===
|
|
80
64
|
#config: TxPoolV2Config;
|
|
81
65
|
#archive: TxArchive;
|
|
66
|
+
#deletedPool: DeletedPool;
|
|
82
67
|
#evictionManager: EvictionManager;
|
|
68
|
+
#dateProvider: DateProvider;
|
|
83
69
|
#log: Logger;
|
|
84
70
|
#callbacks: TxPoolV2Callbacks;
|
|
85
71
|
|
|
@@ -89,6 +75,7 @@ export class TxPoolV2Impl {
|
|
|
89
75
|
deps: TxPoolV2Dependencies,
|
|
90
76
|
callbacks: TxPoolV2Callbacks,
|
|
91
77
|
config: Partial<TxPoolV2Config> = {},
|
|
78
|
+
dateProvider: DateProvider,
|
|
92
79
|
log: Logger,
|
|
93
80
|
) {
|
|
94
81
|
this.#store = store;
|
|
@@ -96,10 +83,12 @@ export class TxPoolV2Impl {
|
|
|
96
83
|
|
|
97
84
|
this.#l2BlockSource = deps.l2BlockSource;
|
|
98
85
|
this.#worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
99
|
-
this.#
|
|
86
|
+
this.#createTxValidator = deps.createTxValidator;
|
|
100
87
|
|
|
101
88
|
this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
|
|
102
89
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
90
|
+
this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
|
|
91
|
+
this.#dateProvider = dateProvider;
|
|
103
92
|
this.#log = log;
|
|
104
93
|
this.#callbacks = callbacks;
|
|
105
94
|
|
|
@@ -134,26 +123,42 @@ export class TxPoolV2Impl {
|
|
|
134
123
|
* by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
|
|
135
124
|
*/
|
|
136
125
|
async hydrateFromDatabase(): Promise<void> {
|
|
137
|
-
// Step
|
|
126
|
+
// Step 0: Hydrate deleted pool state
|
|
127
|
+
await this.#deletedPool.hydrateFromDatabase();
|
|
128
|
+
|
|
129
|
+
// Step 1: Load all transactions from DB (excluding soft-deleted)
|
|
138
130
|
const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
|
|
139
131
|
|
|
140
132
|
// Step 2: Check mined status for each tx
|
|
141
133
|
await this.#markMinedStatusBatch(loaded.map(l => l.meta));
|
|
142
134
|
|
|
143
135
|
// Step 3: Partition by mined status
|
|
144
|
-
const
|
|
136
|
+
const mined: TxMetaData[] = [];
|
|
137
|
+
const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
|
|
138
|
+
for (const entry of loaded) {
|
|
139
|
+
if (entry.meta.minedL2BlockId !== undefined) {
|
|
140
|
+
mined.push(entry.meta);
|
|
141
|
+
} else {
|
|
142
|
+
nonMined.push(entry);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
145
|
|
|
146
146
|
// Step 4: Validate non-mined transactions
|
|
147
|
-
const { valid, invalid } = await this.#
|
|
147
|
+
const { valid, invalid } = await this.#revalidateMetadata(
|
|
148
|
+
nonMined.map(e => e.meta),
|
|
149
|
+
'on startup',
|
|
150
|
+
);
|
|
148
151
|
|
|
149
152
|
// Step 5: Populate mined indices (these don't need conflict resolution)
|
|
150
|
-
|
|
153
|
+
for (const meta of mined) {
|
|
154
|
+
this.#indices.addMined(meta);
|
|
155
|
+
}
|
|
151
156
|
|
|
152
157
|
// Step 6: Rebuild pending pool by running pre-add rules for each tx
|
|
153
158
|
// This resolves nullifier conflicts, fee payer balance issues, and pool size limits
|
|
154
159
|
const { rejected } = await this.#rebuildPendingPool(valid);
|
|
155
160
|
|
|
156
|
-
// Step 7: Delete invalid and rejected txs from DB
|
|
161
|
+
// Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
|
|
157
162
|
const toDelete = [...deserializationErrors, ...invalid, ...rejected];
|
|
158
163
|
if (toDelete.length === 0) {
|
|
159
164
|
return;
|
|
@@ -163,14 +168,13 @@ export class TxPoolV2Impl {
|
|
|
163
168
|
await this.#txsDB.delete(txHashStr);
|
|
164
169
|
}
|
|
165
170
|
});
|
|
166
|
-
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup
|
|
171
|
+
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, { txHashes: toDelete });
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
async addPendingTxs(txs: Tx[], opts: { source?: string }): Promise<AddTxsResult> {
|
|
170
175
|
const accepted: TxHash[] = [];
|
|
171
176
|
const ignored: TxHash[] = [];
|
|
172
177
|
const rejected: TxHash[] = [];
|
|
173
|
-
const newlyAdded: Tx[] = [];
|
|
174
178
|
const acceptedPending = new Set<string>();
|
|
175
179
|
|
|
176
180
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
@@ -181,31 +185,28 @@ export class TxPoolV2Impl {
|
|
|
181
185
|
const txHashStr = txHash.toString();
|
|
182
186
|
|
|
183
187
|
// Skip duplicates
|
|
184
|
-
if (this.#
|
|
188
|
+
if (this.#indices.has(txHashStr)) {
|
|
185
189
|
ignored.push(txHash);
|
|
186
190
|
continue;
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
// Check mined status first (applies to all paths)
|
|
190
194
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
191
|
-
const preProtectedSlot = this.#
|
|
195
|
+
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
192
196
|
|
|
193
197
|
if (minedBlockId) {
|
|
194
198
|
// Already mined - add directly (protection already set if pre-protected)
|
|
195
|
-
await this.#
|
|
199
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
196
200
|
accepted.push(txHash);
|
|
197
|
-
newlyAdded.push(tx);
|
|
198
201
|
} else if (preProtectedSlot !== undefined) {
|
|
199
202
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
200
|
-
await this.#
|
|
203
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
201
204
|
accepted.push(txHash);
|
|
202
|
-
newlyAdded.push(tx);
|
|
203
205
|
} else {
|
|
204
206
|
// Regular pending tx - validate and run pre-add rules
|
|
205
|
-
const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
|
|
207
|
+
const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
|
|
206
208
|
if (result.status === 'accepted') {
|
|
207
209
|
acceptedPending.add(txHashStr);
|
|
208
|
-
newlyAdded.push(tx);
|
|
209
210
|
} else if (result.status === 'rejected') {
|
|
210
211
|
rejected.push(txHash);
|
|
211
212
|
} else {
|
|
@@ -222,22 +223,18 @@ export class TxPoolV2Impl {
|
|
|
222
223
|
|
|
223
224
|
// Run post-add eviction rules for pending txs
|
|
224
225
|
if (acceptedPending.size > 0) {
|
|
225
|
-
const feePayers = Array.from(acceptedPending).map(txHash => this.#
|
|
226
|
+
const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
|
|
226
227
|
const uniqueFeePayers = new Set<string>(feePayers);
|
|
227
228
|
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
// Emit events
|
|
231
|
-
if (newlyAdded.length > 0) {
|
|
232
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
231
|
return { accepted, ignored, rejected };
|
|
236
232
|
}
|
|
237
233
|
|
|
238
234
|
/** Validates and adds a regular pending tx. Returns status. */
|
|
239
235
|
async #tryAddRegularPendingTx(
|
|
240
236
|
tx: Tx,
|
|
237
|
+
opts: { source?: string },
|
|
241
238
|
poolAccess: PreAddPoolAccess,
|
|
242
239
|
acceptedPending: Set<string>,
|
|
243
240
|
ignored: TxHash[],
|
|
@@ -245,15 +242,13 @@ export class TxPoolV2Impl {
|
|
|
245
242
|
const txHash = tx.getTxHash();
|
|
246
243
|
const txHashStr = txHash.toString();
|
|
247
244
|
|
|
248
|
-
//
|
|
249
|
-
const
|
|
250
|
-
if (
|
|
251
|
-
this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
|
|
245
|
+
// Build metadata and validate using metadata
|
|
246
|
+
const meta = await buildTxMetaData(tx);
|
|
247
|
+
if (!(await this.#validateMeta(meta))) {
|
|
252
248
|
return { status: 'rejected' };
|
|
253
249
|
}
|
|
254
250
|
|
|
255
|
-
//
|
|
256
|
-
const meta = await buildTxMetaData(tx);
|
|
251
|
+
// Run pre-add rules
|
|
257
252
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
258
253
|
|
|
259
254
|
if (preAddResult.shouldIgnore) {
|
|
@@ -261,18 +256,22 @@ export class TxPoolV2Impl {
|
|
|
261
256
|
return { status: 'ignored' };
|
|
262
257
|
}
|
|
263
258
|
|
|
264
|
-
// Evict conflicts
|
|
259
|
+
// Evict conflicts
|
|
265
260
|
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
266
261
|
await this.#deleteTx(evictHashStr);
|
|
267
|
-
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}
|
|
262
|
+
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
|
|
263
|
+
evictedTxHash: evictHashStr,
|
|
264
|
+
replacementTxHash: txHashStr,
|
|
265
|
+
});
|
|
268
266
|
if (acceptedPending.has(evictHashStr)) {
|
|
267
|
+
// Evicted tx was from this batch - mark as ignored in result
|
|
269
268
|
acceptedPending.delete(evictHashStr);
|
|
270
269
|
ignored.push(TxHash.fromString(evictHashStr));
|
|
271
270
|
}
|
|
272
271
|
}
|
|
273
272
|
|
|
274
273
|
// Add the transaction
|
|
275
|
-
await this.#
|
|
274
|
+
await this.#addTx(tx, 'pending', opts);
|
|
276
275
|
return { status: 'accepted' };
|
|
277
276
|
}
|
|
278
277
|
|
|
@@ -280,18 +279,18 @@ export class TxPoolV2Impl {
|
|
|
280
279
|
const txHashStr = tx.getTxHash().toString();
|
|
281
280
|
|
|
282
281
|
// Check if already in pool
|
|
283
|
-
if (this.#
|
|
282
|
+
if (this.#indices.has(txHashStr)) {
|
|
284
283
|
return 'ignored';
|
|
285
284
|
}
|
|
286
285
|
|
|
287
|
-
//
|
|
288
|
-
const
|
|
289
|
-
|
|
286
|
+
// Build metadata and validate using metadata
|
|
287
|
+
const meta = await buildTxMetaData(tx);
|
|
288
|
+
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
289
|
+
if (validationResult !== true) {
|
|
290
290
|
return 'rejected';
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
//
|
|
294
|
-
const meta = await buildTxMetaData(tx);
|
|
293
|
+
// Use pre-add rules
|
|
295
294
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
296
295
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
297
296
|
|
|
@@ -300,37 +299,32 @@ export class TxPoolV2Impl {
|
|
|
300
299
|
|
|
301
300
|
async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
302
301
|
const slotNumber = block.globalVariables.slotNumber;
|
|
303
|
-
const newlyAdded: Tx[] = [];
|
|
304
302
|
|
|
305
303
|
await this.#store.transactionAsync(async () => {
|
|
306
304
|
for (const tx of txs) {
|
|
307
305
|
const txHash = tx.getTxHash();
|
|
308
306
|
const txHashStr = txHash.toString();
|
|
309
|
-
const isNew = !this.#
|
|
307
|
+
const isNew = !this.#indices.has(txHashStr);
|
|
310
308
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
311
309
|
|
|
312
310
|
if (isNew) {
|
|
313
|
-
// New tx - add as mined or protected
|
|
311
|
+
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
314
312
|
if (minedBlockId) {
|
|
315
|
-
await this.#
|
|
316
|
-
this.#
|
|
313
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
314
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
317
315
|
} else {
|
|
318
|
-
await this.#
|
|
316
|
+
await this.#addTx(tx, { protected: slotNumber }, opts);
|
|
319
317
|
}
|
|
320
|
-
newlyAdded.push(tx);
|
|
321
318
|
} else {
|
|
322
319
|
// Existing tx - update protection and mined status
|
|
323
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
320
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
324
321
|
if (minedBlockId) {
|
|
325
|
-
this.#
|
|
322
|
+
const meta = this.#indices.getMetadata(txHashStr)!;
|
|
323
|
+
this.#indices.markAsMined(meta, minedBlockId);
|
|
326
324
|
}
|
|
327
325
|
}
|
|
328
326
|
}
|
|
329
327
|
});
|
|
330
|
-
|
|
331
|
-
if (newlyAdded.length > 0) {
|
|
332
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
333
|
-
}
|
|
334
328
|
}
|
|
335
329
|
|
|
336
330
|
protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
|
|
@@ -340,12 +334,12 @@ export class TxPoolV2Impl {
|
|
|
340
334
|
for (const txHash of txHashes) {
|
|
341
335
|
const txHashStr = txHash.toString();
|
|
342
336
|
|
|
343
|
-
if (this.#
|
|
344
|
-
//
|
|
345
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
337
|
+
if (this.#indices.has(txHashStr)) {
|
|
338
|
+
// Update protection for existing tx
|
|
339
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
346
340
|
} else {
|
|
347
|
-
//
|
|
348
|
-
this.#
|
|
341
|
+
// Pre-record protection for tx we don't have yet
|
|
342
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
349
343
|
missing.push(txHash);
|
|
350
344
|
}
|
|
351
345
|
}
|
|
@@ -356,28 +350,22 @@ export class TxPoolV2Impl {
|
|
|
356
350
|
async addMinedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
357
351
|
// Step 1: Build block ID
|
|
358
352
|
const blockId = await this.#buildBlockId(block);
|
|
359
|
-
const newlyAdded: Tx[] = [];
|
|
360
353
|
|
|
361
354
|
await this.#store.transactionAsync(async () => {
|
|
362
355
|
for (const tx of txs) {
|
|
363
356
|
const txHashStr = tx.getTxHash().toString();
|
|
364
|
-
const existingMeta = this.#
|
|
357
|
+
const existingMeta = this.#indices.getMetadata(txHashStr);
|
|
365
358
|
|
|
366
359
|
if (existingMeta) {
|
|
367
|
-
//
|
|
368
|
-
this.#markAsMined(existingMeta, blockId);
|
|
360
|
+
// Mark existing tx as mined
|
|
361
|
+
this.#indices.markAsMined(existingMeta, blockId);
|
|
369
362
|
} else {
|
|
370
|
-
//
|
|
371
|
-
await this.#
|
|
372
|
-
newlyAdded.push(tx);
|
|
363
|
+
// Add new mined tx (callback emitted by #addTx)
|
|
364
|
+
await this.#addTx(tx, { mined: blockId }, opts);
|
|
373
365
|
}
|
|
366
|
+
await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
|
|
374
367
|
}
|
|
375
368
|
});
|
|
376
|
-
|
|
377
|
-
// Step 3: Emit events for newly added txs
|
|
378
|
-
if (newlyAdded.length > 0) {
|
|
379
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
380
|
-
}
|
|
381
369
|
}
|
|
382
370
|
|
|
383
371
|
async handleMinedBlock(block: L2Block): Promise<void> {
|
|
@@ -392,7 +380,7 @@ export class TxPoolV2Impl {
|
|
|
392
380
|
const feePayers: string[] = [];
|
|
393
381
|
const found: TxMetaData[] = [];
|
|
394
382
|
for (const txHash of txHashes) {
|
|
395
|
-
const meta = this.#
|
|
383
|
+
const meta = this.#indices.getMetadata(txHash.toString());
|
|
396
384
|
if (meta) {
|
|
397
385
|
feePayers.push(meta.feePayer);
|
|
398
386
|
found.push(meta);
|
|
@@ -400,24 +388,29 @@ export class TxPoolV2Impl {
|
|
|
400
388
|
}
|
|
401
389
|
|
|
402
390
|
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
403
|
-
|
|
391
|
+
for (const meta of found) {
|
|
392
|
+
this.#indices.markAsMined(meta, blockId);
|
|
393
|
+
await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
|
|
394
|
+
}
|
|
404
395
|
|
|
405
396
|
// Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
|
|
406
397
|
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
407
398
|
|
|
408
|
-
this.#callbacks.onTxsRemoved(txHashes.map(h => h.toBigInt()));
|
|
409
399
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
410
400
|
}
|
|
411
401
|
|
|
412
402
|
async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
|
|
403
|
+
// Step 0: Clean up slot-deleted txs from previous slots
|
|
404
|
+
await this.#deletedPool.cleanupSlotDeleted(slotNumber);
|
|
405
|
+
|
|
413
406
|
// Step 1: Find expired protected txs
|
|
414
|
-
const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
|
|
407
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
415
408
|
|
|
416
409
|
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
417
|
-
this.#clearProtection(expiredProtected);
|
|
410
|
+
this.#indices.clearProtection(expiredProtected);
|
|
418
411
|
|
|
419
412
|
// Step 3: Filter to only txs that have metadata and are not mined
|
|
420
|
-
const txsToRestore = this.#filterRestorable(expiredProtected);
|
|
413
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
421
414
|
if (txsToRestore.length === 0) {
|
|
422
415
|
return;
|
|
423
416
|
}
|
|
@@ -425,7 +418,7 @@ export class TxPoolV2Impl {
|
|
|
425
418
|
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
426
419
|
|
|
427
420
|
// Step 4: Validate for pending pool
|
|
428
|
-
const { valid, invalid } = await this.#
|
|
421
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
429
422
|
|
|
430
423
|
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
431
424
|
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
@@ -446,7 +439,7 @@ export class TxPoolV2Impl {
|
|
|
446
439
|
|
|
447
440
|
async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
|
|
448
441
|
// Step 1: Find transactions mined after the prune point
|
|
449
|
-
const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
|
|
442
|
+
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
450
443
|
if (txsToUnmine.length === 0) {
|
|
451
444
|
this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
|
|
452
445
|
return;
|
|
@@ -454,46 +447,60 @@ export class TxPoolV2Impl {
|
|
|
454
447
|
|
|
455
448
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
456
449
|
|
|
457
|
-
// Step 2:
|
|
458
|
-
|
|
450
|
+
// Step 2: Mark ALL un-mined txs with their original mined block number
|
|
451
|
+
// This ensures they get soft-deleted if removed later, and only hard-deleted
|
|
452
|
+
// when their original mined block is finalized
|
|
453
|
+
await this.#deletedPool.markFromPrunedBlock(
|
|
454
|
+
txsToUnmine.map(m => ({
|
|
455
|
+
txHash: m.txHash,
|
|
456
|
+
minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
|
|
457
|
+
})),
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
461
|
+
for (const meta of txsToUnmine) {
|
|
462
|
+
this.#indices.markAsUnmined(meta);
|
|
463
|
+
}
|
|
459
464
|
|
|
460
|
-
// Step
|
|
461
|
-
const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
|
|
465
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
466
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
462
467
|
|
|
463
468
|
// Step 4: Validate for pending pool
|
|
464
|
-
const { valid, invalid } = await this.#
|
|
469
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
465
470
|
|
|
466
|
-
// Step
|
|
471
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
467
472
|
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
468
473
|
|
|
469
|
-
// Step
|
|
474
|
+
// Step 7: Delete invalid and evicted txs
|
|
470
475
|
await this.#deleteTxsBatch([...invalid, ...toEvict]);
|
|
471
476
|
|
|
472
|
-
|
|
477
|
+
this.#log.info(
|
|
478
|
+
`Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`,
|
|
479
|
+
{ txHashesRestored: valid.map(m => m.txHash), txHashesInvalid: invalid, txHashesEvicted: toEvict },
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Step 8: Run eviction rules for ALL pending txs (not just restored ones)
|
|
473
483
|
// This handles cases like existing pending txs with invalid fee payer balances
|
|
474
484
|
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
475
485
|
}
|
|
476
486
|
|
|
477
487
|
async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
|
|
478
|
-
//
|
|
488
|
+
// Delete failed txs
|
|
479
489
|
await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
|
|
480
490
|
|
|
481
|
-
this.#log.info(`Deleted ${txHashes.length} failed txs
|
|
491
|
+
this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
|
|
482
492
|
}
|
|
483
493
|
|
|
484
494
|
async handleFinalizedBlock(block: BlockHeader): Promise<void> {
|
|
485
495
|
const blockNumber = block.globalVariables.blockNumber;
|
|
486
496
|
|
|
487
|
-
// Step 1: Find txs
|
|
488
|
-
const
|
|
489
|
-
if (txsToFinalize.length === 0) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
497
|
+
// Step 1: Find mined txs at or before finalized block
|
|
498
|
+
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
492
499
|
|
|
493
|
-
// Step 2: Collect txs for archiving (before deletion)
|
|
500
|
+
// Step 2: Collect mined txs for archiving (before deletion)
|
|
494
501
|
const txsToArchive: Tx[] = [];
|
|
495
502
|
if (this.#archive.isEnabled()) {
|
|
496
|
-
for (const txHashStr of
|
|
503
|
+
for (const txHashStr of minedTxsToFinalize) {
|
|
497
504
|
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
498
505
|
if (buffer) {
|
|
499
506
|
txsToArchive.push(Tx.fromBuffer(buffer));
|
|
@@ -501,15 +508,22 @@ export class TxPoolV2Impl {
|
|
|
501
508
|
}
|
|
502
509
|
}
|
|
503
510
|
|
|
504
|
-
// Step 3: Delete from active pool
|
|
505
|
-
await this.#deleteTxsBatch(
|
|
511
|
+
// Step 3: Delete mined txs from active pool
|
|
512
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
506
513
|
|
|
507
|
-
// Step 4:
|
|
514
|
+
// Step 4: Finalize soft-deleted txs
|
|
515
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
516
|
+
|
|
517
|
+
// Step 5: Archive mined txs
|
|
508
518
|
if (txsToArchive.length > 0) {
|
|
509
519
|
await this.#archive.archiveTxs(txsToArchive);
|
|
510
520
|
}
|
|
511
521
|
|
|
512
|
-
|
|
522
|
+
if (minedTxsToFinalize.length > 0) {
|
|
523
|
+
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
|
|
524
|
+
txHashes: minedTxsToFinalize,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
513
527
|
}
|
|
514
528
|
|
|
515
529
|
// === Query Methods ===
|
|
@@ -529,42 +543,47 @@ export class TxPoolV2Impl {
|
|
|
529
543
|
}
|
|
530
544
|
|
|
531
545
|
hasTxs(txHashes: TxHash[]): boolean[] {
|
|
532
|
-
return txHashes.map(h =>
|
|
546
|
+
return txHashes.map(h => {
|
|
547
|
+
const hashStr = h.toString();
|
|
548
|
+
return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
|
|
549
|
+
});
|
|
533
550
|
}
|
|
534
551
|
|
|
535
552
|
getTxStatus(txHash: TxHash): TxState | undefined {
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
553
|
+
const txHashStr = txHash.toString();
|
|
554
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
555
|
+
if (meta) {
|
|
556
|
+
return this.#indices.getTxState(meta);
|
|
557
|
+
}
|
|
558
|
+
// Check if soft-deleted
|
|
559
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
560
|
+
return 'deleted';
|
|
539
561
|
}
|
|
540
|
-
return
|
|
562
|
+
return undefined;
|
|
541
563
|
}
|
|
542
564
|
|
|
543
565
|
getPendingTxHashes(): TxHash[] {
|
|
544
|
-
return [...this.#iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
566
|
+
return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
getEligiblePendingTxHashes(): TxHash[] {
|
|
570
|
+
const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
|
|
571
|
+
return [...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)].map(hash =>
|
|
572
|
+
TxHash.fromString(hash),
|
|
573
|
+
);
|
|
545
574
|
}
|
|
546
575
|
|
|
547
576
|
getPendingTxCount(): number {
|
|
548
|
-
|
|
549
|
-
for (const hashes of this.#pendingByPriority.values()) {
|
|
550
|
-
count += hashes.size;
|
|
551
|
-
}
|
|
552
|
-
return count;
|
|
577
|
+
return this.#indices.getPendingTxCount();
|
|
553
578
|
}
|
|
554
579
|
|
|
555
580
|
getMinedTxHashes(): [TxHash, L2BlockId][] {
|
|
556
|
-
|
|
557
|
-
for (const [txHash, meta] of this.#metadata) {
|
|
558
|
-
if (meta.minedL2BlockId !== undefined) {
|
|
559
|
-
result.push([TxHash.fromString(txHash), meta.minedL2BlockId]);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
return result;
|
|
581
|
+
return this.#indices.getMinedTxs().map(([hash, blockId]) => [TxHash.fromString(hash), blockId]);
|
|
563
582
|
}
|
|
564
583
|
|
|
565
584
|
getMinedTxCount(): number {
|
|
566
585
|
let count = 0;
|
|
567
|
-
for (const meta of this.#
|
|
586
|
+
for (const [, meta] of this.#indices.iterateMetadata()) {
|
|
568
587
|
if (meta.minedL2BlockId !== undefined) {
|
|
569
588
|
count++;
|
|
570
589
|
}
|
|
@@ -573,11 +592,11 @@ export class TxPoolV2Impl {
|
|
|
573
592
|
}
|
|
574
593
|
|
|
575
594
|
isEmpty(): boolean {
|
|
576
|
-
return this.#
|
|
595
|
+
return this.#indices.isEmpty();
|
|
577
596
|
}
|
|
578
597
|
|
|
579
598
|
getTxCount(): number {
|
|
580
|
-
return this.#
|
|
599
|
+
return this.#indices.getTxCount();
|
|
581
600
|
}
|
|
582
601
|
|
|
583
602
|
getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
@@ -585,18 +604,7 @@ export class TxPoolV2Impl {
|
|
|
585
604
|
}
|
|
586
605
|
|
|
587
606
|
getLowestPriorityPending(limit: number): TxHash[] {
|
|
588
|
-
|
|
589
|
-
return [];
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const result: TxHash[] = [];
|
|
593
|
-
for (const hash of this.#iteratePendingByPriority('asc')) {
|
|
594
|
-
result.push(TxHash.fromString(hash));
|
|
595
|
-
if (result.length >= limit) {
|
|
596
|
-
break;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return result;
|
|
607
|
+
return this.#indices.getLowestPriorityPending(limit).map(h => TxHash.fromString(h));
|
|
600
608
|
}
|
|
601
609
|
|
|
602
610
|
// === Configuration ===
|
|
@@ -609,6 +617,9 @@ export class TxPoolV2Impl {
|
|
|
609
617
|
this.#config.archivedTxLimit = config.archivedTxLimit;
|
|
610
618
|
this.#archive.updateLimit(config.archivedTxLimit);
|
|
611
619
|
}
|
|
620
|
+
if (config.minTxPoolAgeMs !== undefined) {
|
|
621
|
+
this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
|
|
622
|
+
}
|
|
612
623
|
// Update eviction rules with new config
|
|
613
624
|
this.#evictionManager.updateConfig(config);
|
|
614
625
|
}
|
|
@@ -617,159 +628,111 @@ export class TxPoolV2Impl {
|
|
|
617
628
|
|
|
618
629
|
getPoolReadAccess(): PoolReadAccess {
|
|
619
630
|
return {
|
|
620
|
-
getMetadata: (txHash: string) => this.#
|
|
621
|
-
getTxHashByNullifier: (nullifier: string) => this.#
|
|
622
|
-
getTxHashesByFeePayer: (feePayer: string) => this.#
|
|
623
|
-
getPendingTxCount: () => this.getPendingTxCount(),
|
|
631
|
+
getMetadata: (txHash: string) => this.#indices.getMetadata(txHash),
|
|
632
|
+
getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
|
|
633
|
+
getTxHashesByFeePayer: (feePayer: string) => this.#indices.getTxHashesByFeePayer(feePayer),
|
|
634
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
624
635
|
};
|
|
625
636
|
}
|
|
626
637
|
|
|
627
638
|
// === Metrics ===
|
|
628
639
|
|
|
629
640
|
countTxs(): { pending: number; protected: number; mined: number } {
|
|
630
|
-
|
|
631
|
-
let protected_ = 0;
|
|
632
|
-
let mined = 0;
|
|
633
|
-
|
|
634
|
-
for (const meta of this.#metadata.values()) {
|
|
635
|
-
const state = this.#getTxState(meta);
|
|
636
|
-
if (state === 'pending') {
|
|
637
|
-
pending++;
|
|
638
|
-
} else if (state === 'protected') {
|
|
639
|
-
protected_++;
|
|
640
|
-
} else if (state === 'mined') {
|
|
641
|
-
mined++;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
return { pending, protected: protected_, mined };
|
|
641
|
+
return this.#indices.countTxs();
|
|
646
642
|
}
|
|
647
643
|
|
|
648
644
|
// ============================================================================
|
|
649
|
-
// PRIVATE
|
|
645
|
+
// PRIVATE HELPERS - Transaction Management
|
|
650
646
|
// ============================================================================
|
|
651
647
|
|
|
652
648
|
/**
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
* - 'mined' if it has a minedL2BlockId
|
|
656
|
-
* - 'protected' if it's in the protectedTransactions map (but not mined)
|
|
657
|
-
* - 'pending' otherwise
|
|
649
|
+
* Adds a new transaction to the pool with the specified state.
|
|
650
|
+
* Emits onTxsAdded callback immediately after DB write.
|
|
658
651
|
*/
|
|
659
|
-
#
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
652
|
+
async #addTx(
|
|
653
|
+
tx: Tx,
|
|
654
|
+
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
655
|
+
opts: { source?: string } = {},
|
|
656
|
+
): Promise<TxMetaData> {
|
|
657
|
+
const txHashStr = tx.getTxHash().toString();
|
|
658
|
+
const meta = await buildTxMetaData(tx);
|
|
659
|
+
meta.receivedAt = this.#dateProvider.now();
|
|
668
660
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
*/
|
|
673
|
-
*#iteratePendingByPriority(order: 'asc' | 'desc'): Generator<string> {
|
|
674
|
-
// Use shared comparators, negating for descending order
|
|
675
|
-
const feeCompareFn =
|
|
676
|
-
order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : (a: bigint, b: bigint) => compareFee(a, b);
|
|
677
|
-
const hashCompareFn =
|
|
678
|
-
order === 'desc' ? (a: string, b: string) => compareTxHash(b, a) : (a: string, b: string) => compareTxHash(a, b);
|
|
679
|
-
|
|
680
|
-
const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
|
|
681
|
-
|
|
682
|
-
for (const fee of sortedFees) {
|
|
683
|
-
const hashesAtFee = this.#pendingByPriority.get(fee)!;
|
|
684
|
-
const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
|
|
685
|
-
for (const hash of sortedHashes) {
|
|
686
|
-
yield hash;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
661
|
+
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
662
|
+
await this.#deletedPool.clearSoftDeleted(txHashStr);
|
|
663
|
+
this.#callbacks.onTxsAdded([tx], opts);
|
|
690
664
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
665
|
+
if (state === 'pending') {
|
|
666
|
+
this.#indices.addPending(meta);
|
|
667
|
+
} else if ('protected' in state) {
|
|
668
|
+
this.#indices.addProtected(meta, state.protected);
|
|
669
|
+
} else {
|
|
670
|
+
meta.minedL2BlockId = state.mined;
|
|
671
|
+
this.#indices.addMined(meta);
|
|
672
|
+
}
|
|
694
673
|
|
|
695
|
-
|
|
674
|
+
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
675
|
+
this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
|
|
676
|
+
eventName: 'tx-added-to-pool',
|
|
677
|
+
state: stateStr,
|
|
678
|
+
});
|
|
696
679
|
|
|
697
|
-
|
|
698
|
-
#findTxsMinedAfter(blockNumber: number): TxMetaData[] {
|
|
699
|
-
const result: TxMetaData[] = [];
|
|
700
|
-
for (const meta of this.#metadata.values()) {
|
|
701
|
-
if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number > blockNumber) {
|
|
702
|
-
result.push(meta);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return result;
|
|
680
|
+
return meta;
|
|
706
681
|
}
|
|
707
682
|
|
|
708
|
-
/**
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
683
|
+
/**
|
|
684
|
+
* Deletes a transaction from both indices and DB.
|
|
685
|
+
* Emits onTxsRemoved callback immediately after DB delete.
|
|
686
|
+
*/
|
|
687
|
+
/**
|
|
688
|
+
* Deletes a transaction from the pool.
|
|
689
|
+
* Delegates to DeletedPool which decides soft vs hard delete based on whether
|
|
690
|
+
* the tx is from a pruned block.
|
|
691
|
+
*/
|
|
692
|
+
async #deleteTx(txHashStr: string): Promise<void> {
|
|
693
|
+
this.#indices.remove(txHashStr);
|
|
694
|
+
this.#callbacks.onTxsRemoved([txHashStr]);
|
|
695
|
+
await this.#deletedPool.deleteTx(txHashStr);
|
|
717
696
|
}
|
|
718
697
|
|
|
719
|
-
/**
|
|
720
|
-
#
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
if (protectedSlot < slotNumber) {
|
|
724
|
-
result.push(txHashStr);
|
|
725
|
-
}
|
|
698
|
+
/** Deletes a batch of transactions, emitting callbacks individually for each. */
|
|
699
|
+
async #deleteTxsBatch(txHashes: string[]): Promise<void> {
|
|
700
|
+
for (const txHashStr of txHashes) {
|
|
701
|
+
await this.#deleteTx(txHashStr);
|
|
726
702
|
}
|
|
727
|
-
return result;
|
|
728
703
|
}
|
|
729
704
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
705
|
+
// ============================================================================
|
|
706
|
+
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
707
|
+
// ============================================================================
|
|
734
708
|
|
|
735
|
-
/**
|
|
736
|
-
#
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
709
|
+
/** Validates transaction metadata, returning true if valid */
|
|
710
|
+
async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
|
|
711
|
+
const txValidator = validator ?? (await this.#createTxValidator());
|
|
712
|
+
const result = await txValidator.validateTx(meta);
|
|
713
|
+
if (result.result !== 'valid') {
|
|
714
|
+
const contextStr = context ? ` ${context}` : '';
|
|
715
|
+
this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
|
|
716
|
+
return false;
|
|
743
717
|
}
|
|
744
|
-
return
|
|
718
|
+
return true;
|
|
745
719
|
}
|
|
746
720
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
721
|
+
/** Validates metadata directly */
|
|
722
|
+
async #revalidateMetadata(
|
|
723
|
+
metas: TxMetaData[],
|
|
724
|
+
context?: string,
|
|
725
|
+
): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
|
|
751
726
|
const valid: TxMetaData[] = [];
|
|
752
727
|
const invalid: string[] = [];
|
|
753
|
-
|
|
754
|
-
for (const meta of
|
|
755
|
-
|
|
756
|
-
if (!buffer) {
|
|
757
|
-
this.#log.warn(`Tx ${meta.txHash} not found in DB during validation`);
|
|
758
|
-
invalid.push(meta.txHash);
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
const tx = Tx.fromBuffer(buffer);
|
|
763
|
-
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
764
|
-
|
|
765
|
-
if (result.result === 'valid') {
|
|
728
|
+
const validator = await this.#createTxValidator();
|
|
729
|
+
for (const meta of metas) {
|
|
730
|
+
if (await this.#validateMeta(meta, validator, context)) {
|
|
766
731
|
valid.push(meta);
|
|
767
732
|
} else {
|
|
768
|
-
this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
|
|
769
733
|
invalid.push(meta.txHash);
|
|
770
734
|
}
|
|
771
735
|
}
|
|
772
|
-
|
|
773
736
|
return { valid, invalid };
|
|
774
737
|
}
|
|
775
738
|
|
|
@@ -785,8 +748,8 @@ export class TxPoolV2Impl {
|
|
|
785
748
|
for (const meta of txs) {
|
|
786
749
|
const conflict = checkNullifierConflict(
|
|
787
750
|
meta,
|
|
788
|
-
nullifier => this.#
|
|
789
|
-
txHash => this.#
|
|
751
|
+
nullifier => this.#indices.getTxHashByNullifier(nullifier),
|
|
752
|
+
txHash => this.#indices.getMetadata(txHash),
|
|
790
753
|
);
|
|
791
754
|
if (conflict.shouldIgnore) {
|
|
792
755
|
// Lower priority than existing - don't add, mark for deletion
|
|
@@ -796,13 +759,13 @@ export class TxPoolV2Impl {
|
|
|
796
759
|
toEvict.push(...conflict.txHashesToEvict);
|
|
797
760
|
// Remove evicted from indices immediately for subsequent checks
|
|
798
761
|
for (const evictHash of conflict.txHashesToEvict) {
|
|
799
|
-
const evictMeta = this.#
|
|
762
|
+
const evictMeta = this.#indices.getMetadata(evictHash);
|
|
800
763
|
if (evictMeta) {
|
|
801
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
764
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
802
765
|
}
|
|
803
766
|
}
|
|
804
767
|
// Add to pending indices immediately so subsequent txs in the batch see this tx
|
|
805
|
-
this.#addToPendingIndices(meta);
|
|
768
|
+
this.#indices.addToPendingIndices(meta);
|
|
806
769
|
added.push(meta);
|
|
807
770
|
}
|
|
808
771
|
}
|
|
@@ -810,43 +773,10 @@ export class TxPoolV2Impl {
|
|
|
810
773
|
return { added, toEvict };
|
|
811
774
|
}
|
|
812
775
|
|
|
813
|
-
//
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
#unmineTxs(txs: TxMetaData[]): TxMetaData[] {
|
|
817
|
-
for (const meta of txs) {
|
|
818
|
-
meta.minedL2BlockId = undefined;
|
|
819
|
-
}
|
|
820
|
-
return txs;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/** Removes protection from tx hashes and clears them from the protected map */
|
|
824
|
-
#clearProtection(txHashes: string[]): void {
|
|
825
|
-
for (const txHashStr of txHashes) {
|
|
826
|
-
this.#protectedTransactions.delete(txHashStr);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// --- Batch Operation Steps ---
|
|
831
|
-
|
|
832
|
-
/** Deletes a batch of transactions permanently */
|
|
833
|
-
async #deleteTxsBatch(txHashes: string[]): Promise<void> {
|
|
834
|
-
if (txHashes.length === 0) {
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
await this.#store.transactionAsync(async () => {
|
|
839
|
-
for (const txHashStr of txHashes) {
|
|
840
|
-
await this.#deleteTx(txHashStr);
|
|
841
|
-
}
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
this.#callbacks.onTxsRemoved(txHashes);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
// --- Block & Tx Info Steps ---
|
|
776
|
+
// ============================================================================
|
|
777
|
+
// PRIVATE HELPERS - Block & Hydration
|
|
778
|
+
// ============================================================================
|
|
848
779
|
|
|
849
|
-
/** Builds a block ID from a block header */
|
|
850
780
|
async #buildBlockId(block: BlockHeader): Promise<L2BlockId> {
|
|
851
781
|
return {
|
|
852
782
|
number: block.globalVariables.blockNumber,
|
|
@@ -866,50 +796,6 @@ export class TxPoolV2Impl {
|
|
|
866
796
|
};
|
|
867
797
|
}
|
|
868
798
|
|
|
869
|
-
/** Marks a batch of transactions as mined */
|
|
870
|
-
#markTxsAsMined(metas: TxMetaData[], blockId: L2BlockId): void {
|
|
871
|
-
for (const meta of metas) {
|
|
872
|
-
this.#markAsMined(meta, blockId);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// --- Add Transaction Steps ---
|
|
877
|
-
|
|
878
|
-
/** Persists a transaction to the database */
|
|
879
|
-
async #persistTx(txHashStr: string, tx: Tx): Promise<void> {
|
|
880
|
-
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/** Adds a new transaction as protected, returning its metadata */
|
|
884
|
-
async #addNewProtectedTx(tx: Tx, slotNumber: SlotNumber): Promise<TxMetaData> {
|
|
885
|
-
const txHashStr = tx.getTxHash().toString();
|
|
886
|
-
const meta = await buildTxMetaData(tx);
|
|
887
|
-
|
|
888
|
-
this.#protectedTransactions.set(txHashStr, slotNumber);
|
|
889
|
-
await this.#persistTx(txHashStr, tx);
|
|
890
|
-
this.#metadata.set(txHashStr, meta);
|
|
891
|
-
// Don't add to pending indices since it's protected
|
|
892
|
-
|
|
893
|
-
this.#log.verbose(`Added protected tx ${txHashStr} for slot ${slotNumber}`);
|
|
894
|
-
return meta;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
/** Adds a new transaction as mined, returning its metadata */
|
|
898
|
-
async #addNewMinedTx(tx: Tx, blockId: L2BlockId): Promise<TxMetaData> {
|
|
899
|
-
const txHashStr = tx.getTxHash().toString();
|
|
900
|
-
const meta = await buildTxMetaData(tx);
|
|
901
|
-
meta.minedL2BlockId = blockId;
|
|
902
|
-
|
|
903
|
-
await this.#persistTx(txHashStr, tx);
|
|
904
|
-
this.#metadata.set(txHashStr, meta);
|
|
905
|
-
// Don't add to pending indices since it's mined
|
|
906
|
-
|
|
907
|
-
this.#log.verbose(`Added mined tx ${txHashStr} from block ${blockId.number}`);
|
|
908
|
-
return meta;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// --- Hydration Steps ---
|
|
912
|
-
|
|
913
799
|
/** Loads all transactions from the database, returning loaded txs and deserialization errors */
|
|
914
800
|
async #loadAllTxsFromDb(): Promise<{
|
|
915
801
|
loaded: { tx: Tx; meta: TxMetaData }[];
|
|
@@ -919,6 +805,11 @@ export class TxPoolV2Impl {
|
|
|
919
805
|
const errors: string[] = [];
|
|
920
806
|
|
|
921
807
|
for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
|
|
808
|
+
// Skip soft-deleted transactions - they stay in DB but not in indices
|
|
809
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
|
|
922
813
|
try {
|
|
923
814
|
const tx = Tx.fromBuffer(buffer);
|
|
924
815
|
const meta = await buildTxMetaData(tx);
|
|
@@ -949,50 +840,6 @@ export class TxPoolV2Impl {
|
|
|
949
840
|
}
|
|
950
841
|
}
|
|
951
842
|
|
|
952
|
-
/** Partitions transactions by mined status */
|
|
953
|
-
#partitionByMinedStatus(txs: { tx: Tx; meta: TxMetaData }[]): {
|
|
954
|
-
mined: TxMetaData[];
|
|
955
|
-
nonMined: { tx: Tx; meta: TxMetaData }[];
|
|
956
|
-
} {
|
|
957
|
-
const mined: TxMetaData[] = [];
|
|
958
|
-
const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
|
|
959
|
-
|
|
960
|
-
for (const entry of txs) {
|
|
961
|
-
if (entry.meta.minedL2BlockId !== undefined) {
|
|
962
|
-
mined.push(entry.meta);
|
|
963
|
-
} else {
|
|
964
|
-
nonMined.push(entry);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
return { mined, nonMined };
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
/** Validates non-mined transactions, returning valid metadata and invalid hashes */
|
|
972
|
-
async #validateNonMinedTxs(txs: { tx: Tx; meta: TxMetaData }[]): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
|
|
973
|
-
const valid: TxMetaData[] = [];
|
|
974
|
-
const invalid: string[] = [];
|
|
975
|
-
|
|
976
|
-
for (const { tx, meta } of txs) {
|
|
977
|
-
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
978
|
-
if (result.result === 'valid') {
|
|
979
|
-
valid.push(meta);
|
|
980
|
-
} else {
|
|
981
|
-
this.#log.info(`Removing invalid tx ${meta.txHash} on startup: ${result.reason?.join(', ')}`);
|
|
982
|
-
invalid.push(meta.txHash);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
return { valid, invalid };
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
/** Populates metadata index for mined transactions */
|
|
990
|
-
#populateMinedIndices(metas: TxMetaData[]): void {
|
|
991
|
-
for (const meta of metas) {
|
|
992
|
-
this.#metadata.set(meta.txHash, meta);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
843
|
/**
|
|
997
844
|
* Rebuilds the pending pool by processing each tx through pre-add rules.
|
|
998
845
|
* Starts with an empty pending pool and adds txs one by one, resolving conflicts.
|
|
@@ -1016,18 +863,18 @@ export class TxPoolV2Impl {
|
|
|
1016
863
|
|
|
1017
864
|
// Evict any conflicting txs identified by pre-add rules
|
|
1018
865
|
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
1019
|
-
const evictMeta = this.#
|
|
866
|
+
const evictMeta = this.#indices.getMetadata(evictHashStr);
|
|
1020
867
|
if (evictMeta) {
|
|
1021
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
1022
|
-
this.#
|
|
868
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
869
|
+
this.#indices.remove(evictHashStr);
|
|
1023
870
|
rejected.push(evictHashStr);
|
|
1024
871
|
accepted.delete(evictHashStr);
|
|
1025
872
|
this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
|
|
1026
873
|
}
|
|
1027
874
|
}
|
|
1028
875
|
|
|
1029
|
-
// Add to
|
|
1030
|
-
this.#
|
|
876
|
+
// Add to indices
|
|
877
|
+
this.#indices.addPending(meta);
|
|
1031
878
|
accepted.add(meta.txHash);
|
|
1032
879
|
}
|
|
1033
880
|
|
|
@@ -1035,207 +882,32 @@ export class TxPoolV2Impl {
|
|
|
1035
882
|
return { accepted: [...accepted], rejected };
|
|
1036
883
|
}
|
|
1037
884
|
|
|
1038
|
-
// --- Add Pending Tx Steps ---
|
|
1039
|
-
|
|
1040
|
-
/** Checks if a tx is a duplicate (already in pool) */
|
|
1041
|
-
#isDuplicateTx(txHashStr: string): boolean {
|
|
1042
|
-
return this.#metadata.has(txHashStr);
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
/** Adds a new pending tx to the pool, returning its metadata */
|
|
1046
|
-
async #addNewPendingTx(tx: Tx): Promise<TxMetaData> {
|
|
1047
|
-
const txHashStr = tx.getTxHash().toString();
|
|
1048
|
-
const meta = await buildTxMetaData(tx);
|
|
1049
|
-
|
|
1050
|
-
await this.#persistTx(txHashStr, tx);
|
|
1051
|
-
this.#addToIndices(meta);
|
|
1052
|
-
|
|
1053
|
-
this.#log.verbose(`Added tx ${txHashStr} to pool`, {
|
|
1054
|
-
eventName: 'tx-added-to-pool',
|
|
1055
|
-
state: this.#getTxState(meta),
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
|
-
return meta;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// ============================================================================
|
|
1062
|
-
// HELPER FUNCTIONS - Index Management
|
|
1063
|
-
// ============================================================================
|
|
1064
|
-
|
|
1065
|
-
#addToIndices(meta: TxMetaData): void {
|
|
1066
|
-
this.#metadata.set(meta.txHash, meta);
|
|
1067
|
-
|
|
1068
|
-
if (this.#getTxState(meta) === 'pending') {
|
|
1069
|
-
this.#addToPendingIndices(meta);
|
|
1070
|
-
}
|
|
1071
|
-
// Protected and mined txs don't go into pending indices
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
#addToPendingIndices(meta: TxMetaData): void {
|
|
1075
|
-
// Add to nullifier index
|
|
1076
|
-
for (const nullifier of meta.nullifiers) {
|
|
1077
|
-
this.#nullifierToTxHash.set(nullifier, meta.txHash);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// Add to fee payer index
|
|
1081
|
-
let feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
|
|
1082
|
-
if (!feePayerSet) {
|
|
1083
|
-
feePayerSet = new Set();
|
|
1084
|
-
this.#feePayerToTxHashes.set(meta.feePayer, feePayerSet);
|
|
1085
|
-
}
|
|
1086
|
-
feePayerSet.add(meta.txHash);
|
|
1087
|
-
|
|
1088
|
-
// Add to priority bucket
|
|
1089
|
-
let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
|
|
1090
|
-
if (!prioritySet) {
|
|
1091
|
-
prioritySet = new Set();
|
|
1092
|
-
this.#pendingByPriority.set(meta.priorityFee, prioritySet);
|
|
1093
|
-
}
|
|
1094
|
-
prioritySet.add(meta.txHash);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
#removeFromPendingIndices(meta: TxMetaData): void {
|
|
1098
|
-
// Remove from nullifier index
|
|
1099
|
-
for (const nullifier of meta.nullifiers) {
|
|
1100
|
-
this.#nullifierToTxHash.delete(nullifier);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// Remove from fee payer index
|
|
1104
|
-
const feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
|
|
1105
|
-
if (feePayerSet) {
|
|
1106
|
-
feePayerSet.delete(meta.txHash);
|
|
1107
|
-
if (feePayerSet.size === 0) {
|
|
1108
|
-
this.#feePayerToTxHashes.delete(meta.feePayer);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
// Remove from priority map
|
|
1113
|
-
const hashSet = this.#pendingByPriority.get(meta.priorityFee);
|
|
1114
|
-
if (hashSet) {
|
|
1115
|
-
hashSet.delete(meta.txHash);
|
|
1116
|
-
if (hashSet.size === 0) {
|
|
1117
|
-
this.#pendingByPriority.delete(meta.priorityFee);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
#updateProtection(txHashStr: string, slotNumber: SlotNumber): void {
|
|
1123
|
-
const currentSlot = this.#protectedTransactions.get(txHashStr);
|
|
1124
|
-
|
|
1125
|
-
// Only update if not already protected at an equal or later slot
|
|
1126
|
-
if (currentSlot !== undefined && currentSlot >= slotNumber) {
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// Remove from pending indices if transitioning from pending to protected
|
|
1131
|
-
if (currentSlot === undefined) {
|
|
1132
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1133
|
-
if (meta) {
|
|
1134
|
-
this.#removeFromPendingIndices(meta);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
this.#protectedTransactions.set(txHashStr, slotNumber);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
#markAsMined(meta: TxMetaData, blockId: L2BlockId): void {
|
|
1142
|
-
meta.minedL2BlockId = blockId;
|
|
1143
|
-
// Safe to call unconditionally - removeFromPendingIndices is idempotent
|
|
1144
|
-
this.#removeFromPendingIndices(meta);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
async #deleteTx(txHashStr: string): Promise<void> {
|
|
1148
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1149
|
-
if (!meta) {
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
// Remove from all indices
|
|
1154
|
-
this.#metadata.delete(txHashStr);
|
|
1155
|
-
this.#protectedTransactions.delete(txHashStr);
|
|
1156
|
-
this.#removeFromPendingIndices(meta);
|
|
1157
|
-
|
|
1158
|
-
// Remove from persistence
|
|
1159
|
-
await this.#txsDB.delete(txHashStr);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
885
|
// ============================================================================
|
|
1163
|
-
//
|
|
886
|
+
// PRIVATE HELPERS - Pool Access Adapters
|
|
1164
887
|
// ============================================================================
|
|
1165
888
|
|
|
1166
|
-
/** Gets all pending transactions for a given fee payer. */
|
|
1167
|
-
#getFeePayerPendingTxs(feePayer: string): TxMetaData[] {
|
|
1168
|
-
const txHashes = this.#feePayerToTxHashes.get(feePayer);
|
|
1169
|
-
if (!txHashes) {
|
|
1170
|
-
return [];
|
|
1171
|
-
}
|
|
1172
|
-
const result: TxMetaData[] = [];
|
|
1173
|
-
for (const txHashStr of txHashes) {
|
|
1174
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1175
|
-
if (meta && this.#getTxState(meta) === 'pending') {
|
|
1176
|
-
result.push(meta);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
return result;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
/**
|
|
1183
|
-
* Creates a PoolOperations adapter for use with the eviction manager.
|
|
1184
|
-
*/
|
|
1185
889
|
#createPoolOperations(): PoolOperations {
|
|
1186
890
|
return {
|
|
1187
|
-
getPendingTxs: ()
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
result.push(meta);
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
return result;
|
|
1198
|
-
},
|
|
1199
|
-
getPendingFeePayers: (): string[] => {
|
|
1200
|
-
return Array.from(this.#feePayerToTxHashes.keys());
|
|
1201
|
-
},
|
|
1202
|
-
getFeePayerPendingTxs: (feePayer: string): TxMetaData[] => {
|
|
1203
|
-
return this.#getFeePayerPendingTxs(feePayer);
|
|
1204
|
-
},
|
|
1205
|
-
getPendingTxCount: (): number => {
|
|
1206
|
-
return this.getPendingTxCount();
|
|
1207
|
-
},
|
|
1208
|
-
getLowestPriorityPending: (limit: number): string[] => {
|
|
1209
|
-
return this.getLowestPriorityPending(limit).map(h => h.toString());
|
|
1210
|
-
},
|
|
1211
|
-
deleteTxs: async (txHashes: string[]): Promise<void> => {
|
|
1212
|
-
await this.#store.transactionAsync(async () => {
|
|
1213
|
-
for (const txHashStr of txHashes) {
|
|
1214
|
-
await this.#deleteTx(txHashStr);
|
|
1215
|
-
}
|
|
1216
|
-
});
|
|
1217
|
-
this.#callbacks.onTxsRemoved(txHashes);
|
|
1218
|
-
},
|
|
891
|
+
getPendingTxs: () => this.#indices.getPendingTxs(),
|
|
892
|
+
getPendingFeePayers: () => this.#indices.getPendingFeePayers(),
|
|
893
|
+
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
894
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
895
|
+
getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
|
|
896
|
+
deleteTxs: (txHashes: string[]) => this.#deleteTxsBatch(txHashes),
|
|
1219
897
|
};
|
|
1220
898
|
}
|
|
1221
899
|
|
|
1222
|
-
/**
|
|
1223
|
-
* Creates a PreAddPoolAccess adapter for use with pre-add eviction rules.
|
|
1224
|
-
* All methods work with strings and TxMetaData for efficiency.
|
|
1225
|
-
*/
|
|
1226
900
|
#createPreAddPoolAccess(): PreAddPoolAccess {
|
|
1227
901
|
return {
|
|
1228
|
-
getMetadata: (txHashStr: string)
|
|
1229
|
-
const meta = this.#
|
|
1230
|
-
if (!meta || this.#getTxState(meta) !== 'pending') {
|
|
902
|
+
getMetadata: (txHashStr: string) => {
|
|
903
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
904
|
+
if (!meta || this.#indices.getTxState(meta) !== 'pending') {
|
|
1231
905
|
return undefined;
|
|
1232
906
|
}
|
|
1233
907
|
return meta;
|
|
1234
908
|
},
|
|
1235
|
-
getTxHashByNullifier: (nullifier: string)
|
|
1236
|
-
|
|
1237
|
-
},
|
|
1238
|
-
getFeePayerBalance: async (feePayer: string): Promise<bigint> => {
|
|
909
|
+
getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
|
|
910
|
+
getFeePayerBalance: async (feePayer: string) => {
|
|
1239
911
|
const db = this.#worldStateSynchronizer.getCommitted();
|
|
1240
912
|
const publicStateSource = new DatabasePublicStateSource(db);
|
|
1241
913
|
const balance = await publicStateSource.storageRead(
|
|
@@ -1244,22 +916,9 @@ export class TxPoolV2Impl {
|
|
|
1244
916
|
);
|
|
1245
917
|
return balance.toBigInt();
|
|
1246
918
|
},
|
|
1247
|
-
getFeePayerPendingTxs: (feePayer: string)
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
getPendingTxCount: (): number => {
|
|
1251
|
-
return this.getPendingTxCount();
|
|
1252
|
-
},
|
|
1253
|
-
getLowestPriorityPendingTx: (): TxMetaData | undefined => {
|
|
1254
|
-
// Iterate in ascending order to find the lowest priority
|
|
1255
|
-
for (const txHashStr of this.#iteratePendingByPriority('asc')) {
|
|
1256
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1257
|
-
if (meta) {
|
|
1258
|
-
return meta;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
return undefined;
|
|
1262
|
-
},
|
|
919
|
+
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
920
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
921
|
+
getLowestPriorityPendingTx: () => this.#indices.getLowestPriorityPendingTx(),
|
|
1263
922
|
};
|
|
1264
923
|
}
|
|
1265
924
|
}
|