@aztec/p2p 0.0.1-commit.43c09e3f → 0.0.1-commit.4d79d1f2d
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 +40 -9
- package/dest/client/interface.d.ts +33 -15
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +33 -36
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +104 -139
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
- package/dest/config.d.ts +14 -5
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -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 +102 -88
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +436 -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 +87 -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 +180 -0
- 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 +7 -3
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +25 -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 +38 -5
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +99 -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 +332 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +6 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
- 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 +226 -516
- 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 +10 -4
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +19 -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 +44 -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 +118 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts +27 -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 +57 -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 +3 -1
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +48 -19
- package/dest/services/tx_collection/tx_collection.d.ts +17 -7
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +58 -2
- 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 +3 -3
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_provider.d.ts +3 -3
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +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 +38 -38
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +124 -61
- package/dest/testbench/p2p_client_testbench_worker.js +2 -2
- package/package.json +14 -14
- package/src/client/factory.ts +68 -12
- package/src/client/interface.ts +39 -14
- package/src/client/p2p_client.ts +139 -162
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
- package/src/config.ts +26 -2
- package/src/index.ts +1 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +488 -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 +87 -16
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +234 -0
- package/src/mem_pools/tx_pool_v2/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +4 -2
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +53 -6
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +417 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +3 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +234 -604
- 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 +32 -6
- package/src/services/tx_collection/fast_tx_collection.ts +28 -26
- package/src/services/tx_collection/file_store_tx_collection.ts +152 -0
- package/src/services/tx_collection/file_store_tx_source.ts +70 -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 +55 -26
- package/src/services/tx_collection/tx_collection.ts +78 -12
- 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 +4 -4
- 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 +122 -74
- 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,4 +1,4 @@
|
|
|
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
3
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
4
4
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
@@ -10,6 +10,7 @@ import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
|
10
10
|
import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
|
|
11
11
|
|
|
12
12
|
import { TxArchive } from './archive/index.js';
|
|
13
|
+
import { DeletedPool } from './deleted_pool.js';
|
|
13
14
|
import {
|
|
14
15
|
EvictionManager,
|
|
15
16
|
FeePayerBalanceEvictionRule,
|
|
@@ -29,14 +30,8 @@ import {
|
|
|
29
30
|
type TxPoolV2Config,
|
|
30
31
|
type TxPoolV2Dependencies,
|
|
31
32
|
} from './interfaces.js';
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
type TxState,
|
|
35
|
-
buildTxMetaData,
|
|
36
|
-
checkNullifierConflict,
|
|
37
|
-
compareFee,
|
|
38
|
-
compareTxHash,
|
|
39
|
-
} from './tx_metadata.js';
|
|
33
|
+
import { type TxMetaData, type TxState, buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
|
|
34
|
+
import { TxPoolIndices } from './tx_pool_indices.js';
|
|
40
35
|
|
|
41
36
|
/**
|
|
42
37
|
* Callbacks for the implementation to notify the outer class about events and metrics.
|
|
@@ -59,26 +54,15 @@ export class TxPoolV2Impl {
|
|
|
59
54
|
// === Dependencies ===
|
|
60
55
|
#l2BlockSource: L2BlockSource;
|
|
61
56
|
#worldStateSynchronizer: WorldStateSynchronizer;
|
|
62
|
-
#
|
|
57
|
+
#createTxValidator: TxPoolV2Dependencies['createTxValidator'];
|
|
63
58
|
|
|
64
59
|
// === 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();
|
|
60
|
+
#indices: TxPoolIndices = new TxPoolIndices();
|
|
78
61
|
|
|
79
62
|
// === Config & Services ===
|
|
80
63
|
#config: TxPoolV2Config;
|
|
81
64
|
#archive: TxArchive;
|
|
65
|
+
#deletedPool: DeletedPool;
|
|
82
66
|
#evictionManager: EvictionManager;
|
|
83
67
|
#log: Logger;
|
|
84
68
|
#callbacks: TxPoolV2Callbacks;
|
|
@@ -96,10 +80,11 @@ export class TxPoolV2Impl {
|
|
|
96
80
|
|
|
97
81
|
this.#l2BlockSource = deps.l2BlockSource;
|
|
98
82
|
this.#worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
99
|
-
this.#
|
|
83
|
+
this.#createTxValidator = deps.createTxValidator;
|
|
100
84
|
|
|
101
85
|
this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
|
|
102
86
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
87
|
+
this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
|
|
103
88
|
this.#log = log;
|
|
104
89
|
this.#callbacks = callbacks;
|
|
105
90
|
|
|
@@ -134,26 +119,42 @@ export class TxPoolV2Impl {
|
|
|
134
119
|
* by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
|
|
135
120
|
*/
|
|
136
121
|
async hydrateFromDatabase(): Promise<void> {
|
|
137
|
-
// Step
|
|
122
|
+
// Step 0: Hydrate deleted pool state
|
|
123
|
+
await this.#deletedPool.hydrateFromDatabase();
|
|
124
|
+
|
|
125
|
+
// Step 1: Load all transactions from DB (excluding soft-deleted)
|
|
138
126
|
const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
|
|
139
127
|
|
|
140
128
|
// Step 2: Check mined status for each tx
|
|
141
129
|
await this.#markMinedStatusBatch(loaded.map(l => l.meta));
|
|
142
130
|
|
|
143
131
|
// Step 3: Partition by mined status
|
|
144
|
-
const
|
|
132
|
+
const mined: TxMetaData[] = [];
|
|
133
|
+
const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
|
|
134
|
+
for (const entry of loaded) {
|
|
135
|
+
if (entry.meta.minedL2BlockId !== undefined) {
|
|
136
|
+
mined.push(entry.meta);
|
|
137
|
+
} else {
|
|
138
|
+
nonMined.push(entry);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
145
141
|
|
|
146
142
|
// Step 4: Validate non-mined transactions
|
|
147
|
-
const { valid, invalid } = await this.#
|
|
143
|
+
const { valid, invalid } = await this.#revalidateMetadata(
|
|
144
|
+
nonMined.map(e => e.meta),
|
|
145
|
+
'on startup',
|
|
146
|
+
);
|
|
148
147
|
|
|
149
148
|
// Step 5: Populate mined indices (these don't need conflict resolution)
|
|
150
|
-
|
|
149
|
+
for (const meta of mined) {
|
|
150
|
+
this.#indices.addMined(meta);
|
|
151
|
+
}
|
|
151
152
|
|
|
152
153
|
// Step 6: Rebuild pending pool by running pre-add rules for each tx
|
|
153
154
|
// This resolves nullifier conflicts, fee payer balance issues, and pool size limits
|
|
154
155
|
const { rejected } = await this.#rebuildPendingPool(valid);
|
|
155
156
|
|
|
156
|
-
// Step 7: Delete invalid and rejected txs from DB
|
|
157
|
+
// Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
|
|
157
158
|
const toDelete = [...deserializationErrors, ...invalid, ...rejected];
|
|
158
159
|
if (toDelete.length === 0) {
|
|
159
160
|
return;
|
|
@@ -170,7 +171,6 @@ export class TxPoolV2Impl {
|
|
|
170
171
|
const accepted: TxHash[] = [];
|
|
171
172
|
const ignored: TxHash[] = [];
|
|
172
173
|
const rejected: TxHash[] = [];
|
|
173
|
-
const newlyAdded: Tx[] = [];
|
|
174
174
|
const acceptedPending = new Set<string>();
|
|
175
175
|
|
|
176
176
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
@@ -181,31 +181,28 @@ export class TxPoolV2Impl {
|
|
|
181
181
|
const txHashStr = txHash.toString();
|
|
182
182
|
|
|
183
183
|
// Skip duplicates
|
|
184
|
-
if (this.#
|
|
184
|
+
if (this.#indices.has(txHashStr)) {
|
|
185
185
|
ignored.push(txHash);
|
|
186
186
|
continue;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// Check mined status first (applies to all paths)
|
|
190
190
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
191
|
-
const preProtectedSlot = this.#
|
|
191
|
+
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
192
192
|
|
|
193
193
|
if (minedBlockId) {
|
|
194
194
|
// Already mined - add directly (protection already set if pre-protected)
|
|
195
|
-
await this.#
|
|
195
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
196
196
|
accepted.push(txHash);
|
|
197
|
-
newlyAdded.push(tx);
|
|
198
197
|
} else if (preProtectedSlot !== undefined) {
|
|
199
198
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
200
|
-
await this.#
|
|
199
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
201
200
|
accepted.push(txHash);
|
|
202
|
-
newlyAdded.push(tx);
|
|
203
201
|
} else {
|
|
204
202
|
// Regular pending tx - validate and run pre-add rules
|
|
205
|
-
const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
|
|
203
|
+
const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
|
|
206
204
|
if (result.status === 'accepted') {
|
|
207
205
|
acceptedPending.add(txHashStr);
|
|
208
|
-
newlyAdded.push(tx);
|
|
209
206
|
} else if (result.status === 'rejected') {
|
|
210
207
|
rejected.push(txHash);
|
|
211
208
|
} else {
|
|
@@ -222,22 +219,18 @@ export class TxPoolV2Impl {
|
|
|
222
219
|
|
|
223
220
|
// Run post-add eviction rules for pending txs
|
|
224
221
|
if (acceptedPending.size > 0) {
|
|
225
|
-
const feePayers = Array.from(acceptedPending).map(txHash => this.#
|
|
222
|
+
const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
|
|
226
223
|
const uniqueFeePayers = new Set<string>(feePayers);
|
|
227
224
|
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
228
225
|
}
|
|
229
226
|
|
|
230
|
-
// Emit events
|
|
231
|
-
if (newlyAdded.length > 0) {
|
|
232
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
227
|
return { accepted, ignored, rejected };
|
|
236
228
|
}
|
|
237
229
|
|
|
238
230
|
/** Validates and adds a regular pending tx. Returns status. */
|
|
239
231
|
async #tryAddRegularPendingTx(
|
|
240
232
|
tx: Tx,
|
|
233
|
+
opts: { source?: string },
|
|
241
234
|
poolAccess: PreAddPoolAccess,
|
|
242
235
|
acceptedPending: Set<string>,
|
|
243
236
|
ignored: TxHash[],
|
|
@@ -245,15 +238,13 @@ export class TxPoolV2Impl {
|
|
|
245
238
|
const txHash = tx.getTxHash();
|
|
246
239
|
const txHashStr = txHash.toString();
|
|
247
240
|
|
|
248
|
-
//
|
|
249
|
-
const
|
|
250
|
-
if (
|
|
251
|
-
this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
|
|
241
|
+
// Build metadata and validate using metadata
|
|
242
|
+
const meta = await buildTxMetaData(tx);
|
|
243
|
+
if (!(await this.#validateMeta(meta))) {
|
|
252
244
|
return { status: 'rejected' };
|
|
253
245
|
}
|
|
254
246
|
|
|
255
|
-
//
|
|
256
|
-
const meta = await buildTxMetaData(tx);
|
|
247
|
+
// Run pre-add rules
|
|
257
248
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
258
249
|
|
|
259
250
|
if (preAddResult.shouldIgnore) {
|
|
@@ -261,18 +252,19 @@ export class TxPoolV2Impl {
|
|
|
261
252
|
return { status: 'ignored' };
|
|
262
253
|
}
|
|
263
254
|
|
|
264
|
-
// Evict conflicts
|
|
255
|
+
// Evict conflicts
|
|
265
256
|
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
266
257
|
await this.#deleteTx(evictHashStr);
|
|
267
258
|
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
|
|
268
259
|
if (acceptedPending.has(evictHashStr)) {
|
|
260
|
+
// Evicted tx was from this batch - mark as ignored in result
|
|
269
261
|
acceptedPending.delete(evictHashStr);
|
|
270
262
|
ignored.push(TxHash.fromString(evictHashStr));
|
|
271
263
|
}
|
|
272
264
|
}
|
|
273
265
|
|
|
274
266
|
// Add the transaction
|
|
275
|
-
await this.#
|
|
267
|
+
await this.#addTx(tx, 'pending', opts);
|
|
276
268
|
return { status: 'accepted' };
|
|
277
269
|
}
|
|
278
270
|
|
|
@@ -280,18 +272,18 @@ export class TxPoolV2Impl {
|
|
|
280
272
|
const txHashStr = tx.getTxHash().toString();
|
|
281
273
|
|
|
282
274
|
// Check if already in pool
|
|
283
|
-
if (this.#
|
|
275
|
+
if (this.#indices.has(txHashStr)) {
|
|
284
276
|
return 'ignored';
|
|
285
277
|
}
|
|
286
278
|
|
|
287
|
-
//
|
|
288
|
-
const
|
|
289
|
-
|
|
279
|
+
// Build metadata and validate using metadata
|
|
280
|
+
const meta = await buildTxMetaData(tx);
|
|
281
|
+
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
282
|
+
if (validationResult !== true) {
|
|
290
283
|
return 'rejected';
|
|
291
284
|
}
|
|
292
285
|
|
|
293
|
-
//
|
|
294
|
-
const meta = await buildTxMetaData(tx);
|
|
286
|
+
// Use pre-add rules
|
|
295
287
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
296
288
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
297
289
|
|
|
@@ -300,37 +292,32 @@ export class TxPoolV2Impl {
|
|
|
300
292
|
|
|
301
293
|
async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
302
294
|
const slotNumber = block.globalVariables.slotNumber;
|
|
303
|
-
const newlyAdded: Tx[] = [];
|
|
304
295
|
|
|
305
296
|
await this.#store.transactionAsync(async () => {
|
|
306
297
|
for (const tx of txs) {
|
|
307
298
|
const txHash = tx.getTxHash();
|
|
308
299
|
const txHashStr = txHash.toString();
|
|
309
|
-
const isNew = !this.#
|
|
300
|
+
const isNew = !this.#indices.has(txHashStr);
|
|
310
301
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
311
302
|
|
|
312
303
|
if (isNew) {
|
|
313
|
-
// New tx - add as mined or protected
|
|
304
|
+
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
314
305
|
if (minedBlockId) {
|
|
315
|
-
await this.#
|
|
316
|
-
this.#
|
|
306
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
307
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
317
308
|
} else {
|
|
318
|
-
await this.#
|
|
309
|
+
await this.#addTx(tx, { protected: slotNumber }, opts);
|
|
319
310
|
}
|
|
320
|
-
newlyAdded.push(tx);
|
|
321
311
|
} else {
|
|
322
312
|
// Existing tx - update protection and mined status
|
|
323
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
313
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
324
314
|
if (minedBlockId) {
|
|
325
|
-
this.#
|
|
315
|
+
const meta = this.#indices.getMetadata(txHashStr)!;
|
|
316
|
+
this.#indices.markAsMined(meta, minedBlockId);
|
|
326
317
|
}
|
|
327
318
|
}
|
|
328
319
|
}
|
|
329
320
|
});
|
|
330
|
-
|
|
331
|
-
if (newlyAdded.length > 0) {
|
|
332
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
333
|
-
}
|
|
334
321
|
}
|
|
335
322
|
|
|
336
323
|
protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
|
|
@@ -340,12 +327,12 @@ export class TxPoolV2Impl {
|
|
|
340
327
|
for (const txHash of txHashes) {
|
|
341
328
|
const txHashStr = txHash.toString();
|
|
342
329
|
|
|
343
|
-
if (this.#
|
|
344
|
-
//
|
|
345
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
330
|
+
if (this.#indices.has(txHashStr)) {
|
|
331
|
+
// Update protection for existing tx
|
|
332
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
346
333
|
} else {
|
|
347
|
-
//
|
|
348
|
-
this.#
|
|
334
|
+
// Pre-record protection for tx we don't have yet
|
|
335
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
349
336
|
missing.push(txHash);
|
|
350
337
|
}
|
|
351
338
|
}
|
|
@@ -356,28 +343,22 @@ export class TxPoolV2Impl {
|
|
|
356
343
|
async addMinedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
357
344
|
// Step 1: Build block ID
|
|
358
345
|
const blockId = await this.#buildBlockId(block);
|
|
359
|
-
const newlyAdded: Tx[] = [];
|
|
360
346
|
|
|
361
347
|
await this.#store.transactionAsync(async () => {
|
|
362
348
|
for (const tx of txs) {
|
|
363
349
|
const txHashStr = tx.getTxHash().toString();
|
|
364
|
-
const existingMeta = this.#
|
|
350
|
+
const existingMeta = this.#indices.getMetadata(txHashStr);
|
|
365
351
|
|
|
366
352
|
if (existingMeta) {
|
|
367
|
-
//
|
|
368
|
-
this.#markAsMined(existingMeta, blockId);
|
|
353
|
+
// Mark existing tx as mined
|
|
354
|
+
this.#indices.markAsMined(existingMeta, blockId);
|
|
369
355
|
} else {
|
|
370
|
-
//
|
|
371
|
-
await this.#
|
|
372
|
-
newlyAdded.push(tx);
|
|
356
|
+
// Add new mined tx (callback emitted by #addTx)
|
|
357
|
+
await this.#addTx(tx, { mined: blockId }, opts);
|
|
373
358
|
}
|
|
359
|
+
await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
|
|
374
360
|
}
|
|
375
361
|
});
|
|
376
|
-
|
|
377
|
-
// Step 3: Emit events for newly added txs
|
|
378
|
-
if (newlyAdded.length > 0) {
|
|
379
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
380
|
-
}
|
|
381
362
|
}
|
|
382
363
|
|
|
383
364
|
async handleMinedBlock(block: L2Block): Promise<void> {
|
|
@@ -392,7 +373,7 @@ export class TxPoolV2Impl {
|
|
|
392
373
|
const feePayers: string[] = [];
|
|
393
374
|
const found: TxMetaData[] = [];
|
|
394
375
|
for (const txHash of txHashes) {
|
|
395
|
-
const meta = this.#
|
|
376
|
+
const meta = this.#indices.getMetadata(txHash.toString());
|
|
396
377
|
if (meta) {
|
|
397
378
|
feePayers.push(meta.feePayer);
|
|
398
379
|
found.push(meta);
|
|
@@ -400,24 +381,26 @@ export class TxPoolV2Impl {
|
|
|
400
381
|
}
|
|
401
382
|
|
|
402
383
|
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
403
|
-
|
|
384
|
+
for (const meta of found) {
|
|
385
|
+
this.#indices.markAsMined(meta, blockId);
|
|
386
|
+
await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
|
|
387
|
+
}
|
|
404
388
|
|
|
405
389
|
// Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
|
|
406
390
|
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
407
391
|
|
|
408
|
-
this.#callbacks.onTxsRemoved(txHashes.map(h => h.toBigInt()));
|
|
409
392
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
410
393
|
}
|
|
411
394
|
|
|
412
395
|
async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
|
|
413
396
|
// Step 1: Find expired protected txs
|
|
414
|
-
const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
|
|
397
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
415
398
|
|
|
416
399
|
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
417
|
-
this.#clearProtection(expiredProtected);
|
|
400
|
+
this.#indices.clearProtection(expiredProtected);
|
|
418
401
|
|
|
419
402
|
// Step 3: Filter to only txs that have metadata and are not mined
|
|
420
|
-
const txsToRestore = this.#filterRestorable(expiredProtected);
|
|
403
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
421
404
|
if (txsToRestore.length === 0) {
|
|
422
405
|
return;
|
|
423
406
|
}
|
|
@@ -425,7 +408,7 @@ export class TxPoolV2Impl {
|
|
|
425
408
|
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
426
409
|
|
|
427
410
|
// Step 4: Validate for pending pool
|
|
428
|
-
const { valid, invalid } = await this.#
|
|
411
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
429
412
|
|
|
430
413
|
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
431
414
|
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
@@ -446,7 +429,7 @@ export class TxPoolV2Impl {
|
|
|
446
429
|
|
|
447
430
|
async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
|
|
448
431
|
// Step 1: Find transactions mined after the prune point
|
|
449
|
-
const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
|
|
432
|
+
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
450
433
|
if (txsToUnmine.length === 0) {
|
|
451
434
|
this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
|
|
452
435
|
return;
|
|
@@ -454,28 +437,40 @@ export class TxPoolV2Impl {
|
|
|
454
437
|
|
|
455
438
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
456
439
|
|
|
457
|
-
// Step 2:
|
|
458
|
-
|
|
440
|
+
// Step 2: Mark ALL un-mined txs with their original mined block number
|
|
441
|
+
// This ensures they get soft-deleted if removed later, and only hard-deleted
|
|
442
|
+
// when their original mined block is finalized
|
|
443
|
+
await this.#deletedPool.markFromPrunedBlock(
|
|
444
|
+
txsToUnmine.map(m => ({
|
|
445
|
+
txHash: m.txHash,
|
|
446
|
+
minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
|
|
447
|
+
})),
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
451
|
+
for (const meta of txsToUnmine) {
|
|
452
|
+
this.#indices.markAsUnmined(meta);
|
|
453
|
+
}
|
|
459
454
|
|
|
460
|
-
// Step
|
|
461
|
-
const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
|
|
455
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
456
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
462
457
|
|
|
463
458
|
// Step 4: Validate for pending pool
|
|
464
|
-
const { valid, invalid } = await this.#
|
|
459
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
465
460
|
|
|
466
|
-
// Step
|
|
461
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
467
462
|
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
468
463
|
|
|
469
|
-
// Step
|
|
464
|
+
// Step 7: Delete invalid and evicted txs
|
|
470
465
|
await this.#deleteTxsBatch([...invalid, ...toEvict]);
|
|
471
466
|
|
|
472
|
-
// Step
|
|
467
|
+
// Step 8: Run eviction rules for ALL pending txs (not just restored ones)
|
|
473
468
|
// This handles cases like existing pending txs with invalid fee payer balances
|
|
474
469
|
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
475
470
|
}
|
|
476
471
|
|
|
477
472
|
async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
|
|
478
|
-
//
|
|
473
|
+
// Delete failed txs
|
|
479
474
|
await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
|
|
480
475
|
|
|
481
476
|
this.#log.info(`Deleted ${txHashes.length} failed txs`);
|
|
@@ -484,16 +479,13 @@ export class TxPoolV2Impl {
|
|
|
484
479
|
async handleFinalizedBlock(block: BlockHeader): Promise<void> {
|
|
485
480
|
const blockNumber = block.globalVariables.blockNumber;
|
|
486
481
|
|
|
487
|
-
// Step 1: Find txs
|
|
488
|
-
const
|
|
489
|
-
if (txsToFinalize.length === 0) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
482
|
+
// Step 1: Find mined txs at or before finalized block
|
|
483
|
+
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
492
484
|
|
|
493
|
-
// Step 2: Collect txs for archiving (before deletion)
|
|
485
|
+
// Step 2: Collect mined txs for archiving (before deletion)
|
|
494
486
|
const txsToArchive: Tx[] = [];
|
|
495
487
|
if (this.#archive.isEnabled()) {
|
|
496
|
-
for (const txHashStr of
|
|
488
|
+
for (const txHashStr of minedTxsToFinalize) {
|
|
497
489
|
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
498
490
|
if (buffer) {
|
|
499
491
|
txsToArchive.push(Tx.fromBuffer(buffer));
|
|
@@ -501,15 +493,20 @@ export class TxPoolV2Impl {
|
|
|
501
493
|
}
|
|
502
494
|
}
|
|
503
495
|
|
|
504
|
-
// Step 3: Delete from active pool
|
|
505
|
-
await this.#deleteTxsBatch(
|
|
496
|
+
// Step 3: Delete mined txs from active pool
|
|
497
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
506
498
|
|
|
507
|
-
// Step 4:
|
|
499
|
+
// Step 4: Finalize soft-deleted txs
|
|
500
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
501
|
+
|
|
502
|
+
// Step 5: Archive mined txs
|
|
508
503
|
if (txsToArchive.length > 0) {
|
|
509
504
|
await this.#archive.archiveTxs(txsToArchive);
|
|
510
505
|
}
|
|
511
506
|
|
|
512
|
-
|
|
507
|
+
if (minedTxsToFinalize.length > 0) {
|
|
508
|
+
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`);
|
|
509
|
+
}
|
|
513
510
|
}
|
|
514
511
|
|
|
515
512
|
// === Query Methods ===
|
|
@@ -529,42 +526,40 @@ export class TxPoolV2Impl {
|
|
|
529
526
|
}
|
|
530
527
|
|
|
531
528
|
hasTxs(txHashes: TxHash[]): boolean[] {
|
|
532
|
-
return txHashes.map(h =>
|
|
529
|
+
return txHashes.map(h => {
|
|
530
|
+
const hashStr = h.toString();
|
|
531
|
+
return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
|
|
532
|
+
});
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
getTxStatus(txHash: TxHash): TxState | undefined {
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
536
|
+
const txHashStr = txHash.toString();
|
|
537
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
538
|
+
if (meta) {
|
|
539
|
+
return this.#indices.getTxState(meta);
|
|
540
|
+
}
|
|
541
|
+
// Check if soft-deleted
|
|
542
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
543
|
+
return 'deleted';
|
|
539
544
|
}
|
|
540
|
-
return
|
|
545
|
+
return undefined;
|
|
541
546
|
}
|
|
542
547
|
|
|
543
548
|
getPendingTxHashes(): TxHash[] {
|
|
544
|
-
return [...this.#iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
549
|
+
return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
545
550
|
}
|
|
546
551
|
|
|
547
552
|
getPendingTxCount(): number {
|
|
548
|
-
|
|
549
|
-
for (const hashes of this.#pendingByPriority.values()) {
|
|
550
|
-
count += hashes.size;
|
|
551
|
-
}
|
|
552
|
-
return count;
|
|
553
|
+
return this.#indices.getPendingTxCount();
|
|
553
554
|
}
|
|
554
555
|
|
|
555
556
|
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;
|
|
557
|
+
return this.#indices.getMinedTxs().map(([hash, blockId]) => [TxHash.fromString(hash), blockId]);
|
|
563
558
|
}
|
|
564
559
|
|
|
565
560
|
getMinedTxCount(): number {
|
|
566
561
|
let count = 0;
|
|
567
|
-
for (const meta of this.#
|
|
562
|
+
for (const [, meta] of this.#indices.iterateMetadata()) {
|
|
568
563
|
if (meta.minedL2BlockId !== undefined) {
|
|
569
564
|
count++;
|
|
570
565
|
}
|
|
@@ -573,11 +568,11 @@ export class TxPoolV2Impl {
|
|
|
573
568
|
}
|
|
574
569
|
|
|
575
570
|
isEmpty(): boolean {
|
|
576
|
-
return this.#
|
|
571
|
+
return this.#indices.isEmpty();
|
|
577
572
|
}
|
|
578
573
|
|
|
579
574
|
getTxCount(): number {
|
|
580
|
-
return this.#
|
|
575
|
+
return this.#indices.getTxCount();
|
|
581
576
|
}
|
|
582
577
|
|
|
583
578
|
getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
@@ -585,18 +580,7 @@ export class TxPoolV2Impl {
|
|
|
585
580
|
}
|
|
586
581
|
|
|
587
582
|
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;
|
|
583
|
+
return this.#indices.getLowestPriorityPending(limit).map(h => TxHash.fromString(h));
|
|
600
584
|
}
|
|
601
585
|
|
|
602
586
|
// === Configuration ===
|
|
@@ -617,159 +601,109 @@ export class TxPoolV2Impl {
|
|
|
617
601
|
|
|
618
602
|
getPoolReadAccess(): PoolReadAccess {
|
|
619
603
|
return {
|
|
620
|
-
getMetadata: (txHash: string) => this.#
|
|
621
|
-
getTxHashByNullifier: (nullifier: string) => this.#
|
|
622
|
-
getTxHashesByFeePayer: (feePayer: string) => this.#
|
|
623
|
-
getPendingTxCount: () => this.getPendingTxCount(),
|
|
604
|
+
getMetadata: (txHash: string) => this.#indices.getMetadata(txHash),
|
|
605
|
+
getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
|
|
606
|
+
getTxHashesByFeePayer: (feePayer: string) => this.#indices.getTxHashesByFeePayer(feePayer),
|
|
607
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
624
608
|
};
|
|
625
609
|
}
|
|
626
610
|
|
|
627
611
|
// === Metrics ===
|
|
628
612
|
|
|
629
613
|
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 };
|
|
614
|
+
return this.#indices.countTxs();
|
|
646
615
|
}
|
|
647
616
|
|
|
648
617
|
// ============================================================================
|
|
649
|
-
// PRIVATE
|
|
618
|
+
// PRIVATE HELPERS - Transaction Management
|
|
650
619
|
// ============================================================================
|
|
651
620
|
|
|
652
621
|
/**
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
* - 'mined' if it has a minedL2BlockId
|
|
656
|
-
* - 'protected' if it's in the protectedTransactions map (but not mined)
|
|
657
|
-
* - 'pending' otherwise
|
|
622
|
+
* Adds a new transaction to the pool with the specified state.
|
|
623
|
+
* Emits onTxsAdded callback immediately after DB write.
|
|
658
624
|
*/
|
|
659
|
-
#
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
667
|
-
}
|
|
625
|
+
async #addTx(
|
|
626
|
+
tx: Tx,
|
|
627
|
+
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
628
|
+
opts: { source?: string } = {},
|
|
629
|
+
): Promise<TxMetaData> {
|
|
630
|
+
const txHashStr = tx.getTxHash().toString();
|
|
631
|
+
const meta = await buildTxMetaData(tx);
|
|
668
632
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
* @param order - 'desc' for highest priority first, 'asc' for lowest priority first
|
|
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
|
-
}
|
|
633
|
+
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
634
|
+
this.#callbacks.onTxsAdded([tx], opts);
|
|
690
635
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
636
|
+
if (state === 'pending') {
|
|
637
|
+
this.#indices.addPending(meta);
|
|
638
|
+
} else if ('protected' in state) {
|
|
639
|
+
this.#indices.addProtected(meta, state.protected);
|
|
640
|
+
} else {
|
|
641
|
+
meta.minedL2BlockId = state.mined;
|
|
642
|
+
this.#indices.addMined(meta);
|
|
643
|
+
}
|
|
694
644
|
|
|
695
|
-
|
|
645
|
+
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
646
|
+
this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
|
|
647
|
+
eventName: 'tx-added-to-pool',
|
|
648
|
+
state: stateStr,
|
|
649
|
+
});
|
|
696
650
|
|
|
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;
|
|
651
|
+
return meta;
|
|
706
652
|
}
|
|
707
653
|
|
|
708
|
-
/**
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
654
|
+
/**
|
|
655
|
+
* Deletes a transaction from both indices and DB.
|
|
656
|
+
* Emits onTxsRemoved callback immediately after DB delete.
|
|
657
|
+
*/
|
|
658
|
+
/**
|
|
659
|
+
* Deletes a transaction from the pool.
|
|
660
|
+
* Delegates to DeletedPool which decides soft vs hard delete based on whether
|
|
661
|
+
* the tx is from a pruned block.
|
|
662
|
+
*/
|
|
663
|
+
async #deleteTx(txHashStr: string): Promise<void> {
|
|
664
|
+
this.#indices.remove(txHashStr);
|
|
665
|
+
this.#callbacks.onTxsRemoved([txHashStr]);
|
|
666
|
+
await this.#deletedPool.deleteTx(txHashStr);
|
|
717
667
|
}
|
|
718
668
|
|
|
719
|
-
/**
|
|
720
|
-
#
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
if (protectedSlot < slotNumber) {
|
|
724
|
-
result.push(txHashStr);
|
|
725
|
-
}
|
|
669
|
+
/** Deletes a batch of transactions, emitting callbacks individually for each. */
|
|
670
|
+
async #deleteTxsBatch(txHashes: string[]): Promise<void> {
|
|
671
|
+
for (const txHashStr of txHashes) {
|
|
672
|
+
await this.#deleteTx(txHashStr);
|
|
726
673
|
}
|
|
727
|
-
return result;
|
|
728
674
|
}
|
|
729
675
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
676
|
+
// ============================================================================
|
|
677
|
+
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
678
|
+
// ============================================================================
|
|
734
679
|
|
|
735
|
-
/**
|
|
736
|
-
#
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
680
|
+
/** Validates transaction metadata, returning true if valid */
|
|
681
|
+
async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
|
|
682
|
+
const txValidator = validator ?? (await this.#createTxValidator());
|
|
683
|
+
const result = await txValidator.validateTx(meta);
|
|
684
|
+
if (result.result !== 'valid') {
|
|
685
|
+
const contextStr = context ? ` ${context}` : '';
|
|
686
|
+
this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
|
|
687
|
+
return false;
|
|
743
688
|
}
|
|
744
|
-
return
|
|
689
|
+
return true;
|
|
745
690
|
}
|
|
746
691
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
692
|
+
/** Validates metadata directly */
|
|
693
|
+
async #revalidateMetadata(
|
|
694
|
+
metas: TxMetaData[],
|
|
695
|
+
context?: string,
|
|
696
|
+
): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
|
|
751
697
|
const valid: TxMetaData[] = [];
|
|
752
698
|
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') {
|
|
699
|
+
const validator = await this.#createTxValidator();
|
|
700
|
+
for (const meta of metas) {
|
|
701
|
+
if (await this.#validateMeta(meta, validator, context)) {
|
|
766
702
|
valid.push(meta);
|
|
767
703
|
} else {
|
|
768
|
-
this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
|
|
769
704
|
invalid.push(meta.txHash);
|
|
770
705
|
}
|
|
771
706
|
}
|
|
772
|
-
|
|
773
707
|
return { valid, invalid };
|
|
774
708
|
}
|
|
775
709
|
|
|
@@ -785,8 +719,8 @@ export class TxPoolV2Impl {
|
|
|
785
719
|
for (const meta of txs) {
|
|
786
720
|
const conflict = checkNullifierConflict(
|
|
787
721
|
meta,
|
|
788
|
-
nullifier => this.#
|
|
789
|
-
txHash => this.#
|
|
722
|
+
nullifier => this.#indices.getTxHashByNullifier(nullifier),
|
|
723
|
+
txHash => this.#indices.getMetadata(txHash),
|
|
790
724
|
);
|
|
791
725
|
if (conflict.shouldIgnore) {
|
|
792
726
|
// Lower priority than existing - don't add, mark for deletion
|
|
@@ -796,13 +730,13 @@ export class TxPoolV2Impl {
|
|
|
796
730
|
toEvict.push(...conflict.txHashesToEvict);
|
|
797
731
|
// Remove evicted from indices immediately for subsequent checks
|
|
798
732
|
for (const evictHash of conflict.txHashesToEvict) {
|
|
799
|
-
const evictMeta = this.#
|
|
733
|
+
const evictMeta = this.#indices.getMetadata(evictHash);
|
|
800
734
|
if (evictMeta) {
|
|
801
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
735
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
802
736
|
}
|
|
803
737
|
}
|
|
804
738
|
// Add to pending indices immediately so subsequent txs in the batch see this tx
|
|
805
|
-
this.#addToPendingIndices(meta);
|
|
739
|
+
this.#indices.addToPendingIndices(meta);
|
|
806
740
|
added.push(meta);
|
|
807
741
|
}
|
|
808
742
|
}
|
|
@@ -810,43 +744,10 @@ export class TxPoolV2Impl {
|
|
|
810
744
|
return { added, toEvict };
|
|
811
745
|
}
|
|
812
746
|
|
|
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 ---
|
|
747
|
+
// ============================================================================
|
|
748
|
+
// PRIVATE HELPERS - Block & Hydration
|
|
749
|
+
// ============================================================================
|
|
848
750
|
|
|
849
|
-
/** Builds a block ID from a block header */
|
|
850
751
|
async #buildBlockId(block: BlockHeader): Promise<L2BlockId> {
|
|
851
752
|
return {
|
|
852
753
|
number: block.globalVariables.blockNumber,
|
|
@@ -866,50 +767,6 @@ export class TxPoolV2Impl {
|
|
|
866
767
|
};
|
|
867
768
|
}
|
|
868
769
|
|
|
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
770
|
/** Loads all transactions from the database, returning loaded txs and deserialization errors */
|
|
914
771
|
async #loadAllTxsFromDb(): Promise<{
|
|
915
772
|
loaded: { tx: Tx; meta: TxMetaData }[];
|
|
@@ -919,6 +776,11 @@ export class TxPoolV2Impl {
|
|
|
919
776
|
const errors: string[] = [];
|
|
920
777
|
|
|
921
778
|
for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
|
|
779
|
+
// Skip soft-deleted transactions - they stay in DB but not in indices
|
|
780
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
|
|
922
784
|
try {
|
|
923
785
|
const tx = Tx.fromBuffer(buffer);
|
|
924
786
|
const meta = await buildTxMetaData(tx);
|
|
@@ -949,50 +811,6 @@ export class TxPoolV2Impl {
|
|
|
949
811
|
}
|
|
950
812
|
}
|
|
951
813
|
|
|
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
814
|
/**
|
|
997
815
|
* Rebuilds the pending pool by processing each tx through pre-add rules.
|
|
998
816
|
* Starts with an empty pending pool and adds txs one by one, resolving conflicts.
|
|
@@ -1016,18 +834,18 @@ export class TxPoolV2Impl {
|
|
|
1016
834
|
|
|
1017
835
|
// Evict any conflicting txs identified by pre-add rules
|
|
1018
836
|
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
1019
|
-
const evictMeta = this.#
|
|
837
|
+
const evictMeta = this.#indices.getMetadata(evictHashStr);
|
|
1020
838
|
if (evictMeta) {
|
|
1021
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
1022
|
-
this.#
|
|
839
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
840
|
+
this.#indices.remove(evictHashStr);
|
|
1023
841
|
rejected.push(evictHashStr);
|
|
1024
842
|
accepted.delete(evictHashStr);
|
|
1025
843
|
this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
|
|
1026
844
|
}
|
|
1027
845
|
}
|
|
1028
846
|
|
|
1029
|
-
// Add to
|
|
1030
|
-
this.#
|
|
847
|
+
// Add to indices
|
|
848
|
+
this.#indices.addPending(meta);
|
|
1031
849
|
accepted.add(meta.txHash);
|
|
1032
850
|
}
|
|
1033
851
|
|
|
@@ -1035,207 +853,32 @@ export class TxPoolV2Impl {
|
|
|
1035
853
|
return { accepted: [...accepted], rejected };
|
|
1036
854
|
}
|
|
1037
855
|
|
|
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
856
|
// ============================================================================
|
|
1163
|
-
//
|
|
857
|
+
// PRIVATE HELPERS - Pool Access Adapters
|
|
1164
858
|
// ============================================================================
|
|
1165
859
|
|
|
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
860
|
#createPoolOperations(): PoolOperations {
|
|
1186
861
|
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
|
-
},
|
|
862
|
+
getPendingTxs: () => this.#indices.getPendingTxs(),
|
|
863
|
+
getPendingFeePayers: () => this.#indices.getPendingFeePayers(),
|
|
864
|
+
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
865
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
866
|
+
getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
|
|
867
|
+
deleteTxs: (txHashes: string[]) => this.#deleteTxsBatch(txHashes),
|
|
1219
868
|
};
|
|
1220
869
|
}
|
|
1221
870
|
|
|
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
871
|
#createPreAddPoolAccess(): PreAddPoolAccess {
|
|
1227
872
|
return {
|
|
1228
|
-
getMetadata: (txHashStr: string)
|
|
1229
|
-
const meta = this.#
|
|
1230
|
-
if (!meta || this.#getTxState(meta) !== 'pending') {
|
|
873
|
+
getMetadata: (txHashStr: string) => {
|
|
874
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
875
|
+
if (!meta || this.#indices.getTxState(meta) !== 'pending') {
|
|
1231
876
|
return undefined;
|
|
1232
877
|
}
|
|
1233
878
|
return meta;
|
|
1234
879
|
},
|
|
1235
|
-
getTxHashByNullifier: (nullifier: string)
|
|
1236
|
-
|
|
1237
|
-
},
|
|
1238
|
-
getFeePayerBalance: async (feePayer: string): Promise<bigint> => {
|
|
880
|
+
getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
|
|
881
|
+
getFeePayerBalance: async (feePayer: string) => {
|
|
1239
882
|
const db = this.#worldStateSynchronizer.getCommitted();
|
|
1240
883
|
const publicStateSource = new DatabasePublicStateSource(db);
|
|
1241
884
|
const balance = await publicStateSource.storageRead(
|
|
@@ -1244,22 +887,9 @@ export class TxPoolV2Impl {
|
|
|
1244
887
|
);
|
|
1245
888
|
return balance.toBigInt();
|
|
1246
889
|
},
|
|
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
|
-
},
|
|
890
|
+
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
891
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
892
|
+
getLowestPriorityPendingTx: () => this.#indices.getLowestPriorityPendingTx(),
|
|
1263
893
|
};
|
|
1264
894
|
}
|
|
1265
895
|
}
|