@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,12 +1,15 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
2
3
|
import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
3
4
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
5
|
import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
5
6
|
import { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
6
7
|
import { TxArchive } from './archive/index.js';
|
|
8
|
+
import { DeletedPool } from './deleted_pool.js';
|
|
7
9
|
import { EvictionManager, FeePayerBalanceEvictionRule, FeePayerBalancePreAddRule, InvalidTxsAfterMiningRule, InvalidTxsAfterReorgRule, LowPriorityEvictionRule, LowPriorityPreAddRule, NullifierConflictRule } from './eviction/index.js';
|
|
8
10
|
import { DEFAULT_TX_POOL_V2_CONFIG } from './interfaces.js';
|
|
9
|
-
import { buildTxMetaData, checkNullifierConflict
|
|
11
|
+
import { buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
|
|
12
|
+
import { TxPoolIndices } from './tx_pool_indices.js';
|
|
10
13
|
/**
|
|
11
14
|
* Implementation of TxPoolV2 logic.
|
|
12
15
|
*
|
|
@@ -18,33 +21,30 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
18
21
|
// === Dependencies ===
|
|
19
22
|
#l2BlockSource;
|
|
20
23
|
#worldStateSynchronizer;
|
|
21
|
-
#
|
|
24
|
+
#createTxValidator;
|
|
22
25
|
// === In-Memory Indices ===
|
|
23
|
-
|
|
24
|
-
/** Nullifier to txHash index (pending txs only) */ #nullifierToTxHash = new Map();
|
|
25
|
-
/** Fee payer to txHashes index (pending txs only) */ #feePayerToTxHashes = new Map();
|
|
26
|
-
/**
|
|
27
|
-
* Pending txHashes grouped by priority fee.
|
|
28
|
-
* Outer map: priorityFee -> Set of txHashes at that fee level.
|
|
29
|
-
*/ #pendingByPriority = new Map();
|
|
30
|
-
/** Protected transactions: txHash -> slotNumber. Includes txs we have and txs we expect to receive. */ #protectedTransactions = new Map();
|
|
26
|
+
#indices = new TxPoolIndices();
|
|
31
27
|
// === Config & Services ===
|
|
32
28
|
#config;
|
|
33
29
|
#archive;
|
|
30
|
+
#deletedPool;
|
|
34
31
|
#evictionManager;
|
|
32
|
+
#dateProvider;
|
|
35
33
|
#log;
|
|
36
34
|
#callbacks;
|
|
37
|
-
constructor(store, archiveStore, deps, callbacks, config = {}, log){
|
|
35
|
+
constructor(store, archiveStore, deps, callbacks, config = {}, dateProvider, log){
|
|
38
36
|
this.#store = store;
|
|
39
37
|
this.#txsDB = store.openMap('txs');
|
|
40
38
|
this.#l2BlockSource = deps.l2BlockSource;
|
|
41
39
|
this.#worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
42
|
-
this.#
|
|
40
|
+
this.#createTxValidator = deps.createTxValidator;
|
|
43
41
|
this.#config = {
|
|
44
42
|
...DEFAULT_TX_POOL_V2_CONFIG,
|
|
45
43
|
...config
|
|
46
44
|
};
|
|
47
45
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
46
|
+
this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
|
|
47
|
+
this.#dateProvider = dateProvider;
|
|
48
48
|
this.#log = log;
|
|
49
49
|
this.#callbacks = callbacks;
|
|
50
50
|
// Setup eviction manager with rules
|
|
@@ -75,20 +75,32 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
75
75
|
* Note: Protected status is lost on restart. All non-mined txs are rebuilt as pending
|
|
76
76
|
* by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
|
|
77
77
|
*/ async hydrateFromDatabase() {
|
|
78
|
-
// Step
|
|
78
|
+
// Step 0: Hydrate deleted pool state
|
|
79
|
+
await this.#deletedPool.hydrateFromDatabase();
|
|
80
|
+
// Step 1: Load all transactions from DB (excluding soft-deleted)
|
|
79
81
|
const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
|
|
80
82
|
// Step 2: Check mined status for each tx
|
|
81
83
|
await this.#markMinedStatusBatch(loaded.map((l)=>l.meta));
|
|
82
84
|
// Step 3: Partition by mined status
|
|
83
|
-
const
|
|
85
|
+
const mined = [];
|
|
86
|
+
const nonMined = [];
|
|
87
|
+
for (const entry of loaded){
|
|
88
|
+
if (entry.meta.minedL2BlockId !== undefined) {
|
|
89
|
+
mined.push(entry.meta);
|
|
90
|
+
} else {
|
|
91
|
+
nonMined.push(entry);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
84
94
|
// Step 4: Validate non-mined transactions
|
|
85
|
-
const { valid, invalid } = await this.#
|
|
95
|
+
const { valid, invalid } = await this.#revalidateMetadata(nonMined.map((e)=>e.meta), 'on startup');
|
|
86
96
|
// Step 5: Populate mined indices (these don't need conflict resolution)
|
|
87
|
-
|
|
97
|
+
for (const meta of mined){
|
|
98
|
+
this.#indices.addMined(meta);
|
|
99
|
+
}
|
|
88
100
|
// Step 6: Rebuild pending pool by running pre-add rules for each tx
|
|
89
101
|
// This resolves nullifier conflicts, fee payer balance issues, and pool size limits
|
|
90
102
|
const { rejected } = await this.#rebuildPendingPool(valid);
|
|
91
|
-
// Step 7: Delete invalid and rejected txs from DB
|
|
103
|
+
// Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
|
|
92
104
|
const toDelete = [
|
|
93
105
|
...deserializationErrors,
|
|
94
106
|
...invalid,
|
|
@@ -102,13 +114,14 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
102
114
|
await this.#txsDB.delete(txHashStr);
|
|
103
115
|
}
|
|
104
116
|
});
|
|
105
|
-
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup
|
|
117
|
+
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, {
|
|
118
|
+
txHashes: toDelete
|
|
119
|
+
});
|
|
106
120
|
}
|
|
107
121
|
async addPendingTxs(txs, opts) {
|
|
108
122
|
const accepted = [];
|
|
109
123
|
const ignored = [];
|
|
110
124
|
const rejected = [];
|
|
111
|
-
const newlyAdded = [];
|
|
112
125
|
const acceptedPending = new Set();
|
|
113
126
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
114
127
|
await this.#store.transactionAsync(async ()=>{
|
|
@@ -116,29 +129,30 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
116
129
|
const txHash = tx.getTxHash();
|
|
117
130
|
const txHashStr = txHash.toString();
|
|
118
131
|
// Skip duplicates
|
|
119
|
-
if (this.#
|
|
132
|
+
if (this.#indices.has(txHashStr)) {
|
|
120
133
|
ignored.push(txHash);
|
|
121
134
|
continue;
|
|
122
135
|
}
|
|
123
136
|
// Check mined status first (applies to all paths)
|
|
124
137
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
125
|
-
const preProtectedSlot = this.#
|
|
138
|
+
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
126
139
|
if (minedBlockId) {
|
|
127
140
|
// Already mined - add directly (protection already set if pre-protected)
|
|
128
|
-
await this.#
|
|
141
|
+
await this.#addTx(tx, {
|
|
142
|
+
mined: minedBlockId
|
|
143
|
+
}, opts);
|
|
129
144
|
accepted.push(txHash);
|
|
130
|
-
newlyAdded.push(tx);
|
|
131
145
|
} else if (preProtectedSlot !== undefined) {
|
|
132
146
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
133
|
-
await this.#
|
|
147
|
+
await this.#addTx(tx, {
|
|
148
|
+
protected: preProtectedSlot
|
|
149
|
+
}, opts);
|
|
134
150
|
accepted.push(txHash);
|
|
135
|
-
newlyAdded.push(tx);
|
|
136
151
|
} else {
|
|
137
152
|
// Regular pending tx - validate and run pre-add rules
|
|
138
|
-
const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
|
|
153
|
+
const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
|
|
139
154
|
if (result.status === 'accepted') {
|
|
140
155
|
acceptedPending.add(txHashStr);
|
|
141
|
-
newlyAdded.push(tx);
|
|
142
156
|
} else if (result.status === 'rejected') {
|
|
143
157
|
rejected.push(txHash);
|
|
144
158
|
} else {
|
|
@@ -153,35 +167,29 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
153
167
|
}
|
|
154
168
|
// Run post-add eviction rules for pending txs
|
|
155
169
|
if (acceptedPending.size > 0) {
|
|
156
|
-
const feePayers = Array.from(acceptedPending).map((txHash)=>this.#
|
|
170
|
+
const feePayers = Array.from(acceptedPending).map((txHash)=>this.#indices.getMetadata(txHash).feePayer);
|
|
157
171
|
const uniqueFeePayers = new Set(feePayers);
|
|
158
172
|
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [
|
|
159
173
|
...uniqueFeePayers
|
|
160
174
|
]);
|
|
161
175
|
}
|
|
162
|
-
// Emit events
|
|
163
|
-
if (newlyAdded.length > 0) {
|
|
164
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
165
|
-
}
|
|
166
176
|
return {
|
|
167
177
|
accepted,
|
|
168
178
|
ignored,
|
|
169
179
|
rejected
|
|
170
180
|
};
|
|
171
181
|
}
|
|
172
|
-
/** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored) {
|
|
182
|
+
/** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored) {
|
|
173
183
|
const txHash = tx.getTxHash();
|
|
174
184
|
const txHashStr = txHash.toString();
|
|
175
|
-
//
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
|
|
185
|
+
// Build metadata and validate using metadata
|
|
186
|
+
const meta = await buildTxMetaData(tx);
|
|
187
|
+
if (!await this.#validateMeta(meta)) {
|
|
179
188
|
return {
|
|
180
189
|
status: 'rejected'
|
|
181
190
|
};
|
|
182
191
|
}
|
|
183
|
-
//
|
|
184
|
-
const meta = await buildTxMetaData(tx);
|
|
192
|
+
// Run pre-add rules
|
|
185
193
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
186
194
|
if (preAddResult.shouldIgnore) {
|
|
187
195
|
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason}`);
|
|
@@ -189,17 +197,21 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
189
197
|
status: 'ignored'
|
|
190
198
|
};
|
|
191
199
|
}
|
|
192
|
-
// Evict conflicts
|
|
200
|
+
// Evict conflicts
|
|
193
201
|
for (const evictHashStr of preAddResult.txHashesToEvict){
|
|
194
202
|
await this.#deleteTx(evictHashStr);
|
|
195
|
-
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}
|
|
203
|
+
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
|
|
204
|
+
evictedTxHash: evictHashStr,
|
|
205
|
+
replacementTxHash: txHashStr
|
|
206
|
+
});
|
|
196
207
|
if (acceptedPending.has(evictHashStr)) {
|
|
208
|
+
// Evicted tx was from this batch - mark as ignored in result
|
|
197
209
|
acceptedPending.delete(evictHashStr);
|
|
198
210
|
ignored.push(TxHash.fromString(evictHashStr));
|
|
199
211
|
}
|
|
200
212
|
}
|
|
201
213
|
// Add the transaction
|
|
202
|
-
await this.#
|
|
214
|
+
await this.#addTx(tx, 'pending', opts);
|
|
203
215
|
return {
|
|
204
216
|
status: 'accepted'
|
|
205
217
|
};
|
|
@@ -207,62 +219,62 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
207
219
|
async canAddPendingTx(tx) {
|
|
208
220
|
const txHashStr = tx.getTxHash().toString();
|
|
209
221
|
// Check if already in pool
|
|
210
|
-
if (this.#
|
|
222
|
+
if (this.#indices.has(txHashStr)) {
|
|
211
223
|
return 'ignored';
|
|
212
224
|
}
|
|
213
|
-
//
|
|
214
|
-
const
|
|
215
|
-
|
|
225
|
+
// Build metadata and validate using metadata
|
|
226
|
+
const meta = await buildTxMetaData(tx);
|
|
227
|
+
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
228
|
+
if (validationResult !== true) {
|
|
216
229
|
return 'rejected';
|
|
217
230
|
}
|
|
218
|
-
//
|
|
219
|
-
const meta = await buildTxMetaData(tx);
|
|
231
|
+
// Use pre-add rules
|
|
220
232
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
221
233
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
222
234
|
return preAddResult.shouldIgnore ? 'ignored' : 'accepted';
|
|
223
235
|
}
|
|
224
236
|
async addProtectedTxs(txs, block, opts) {
|
|
225
237
|
const slotNumber = block.globalVariables.slotNumber;
|
|
226
|
-
const newlyAdded = [];
|
|
227
238
|
await this.#store.transactionAsync(async ()=>{
|
|
228
239
|
for (const tx of txs){
|
|
229
240
|
const txHash = tx.getTxHash();
|
|
230
241
|
const txHashStr = txHash.toString();
|
|
231
|
-
const isNew = !this.#
|
|
242
|
+
const isNew = !this.#indices.has(txHashStr);
|
|
232
243
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
233
244
|
if (isNew) {
|
|
234
|
-
// New tx - add as mined or protected
|
|
245
|
+
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
235
246
|
if (minedBlockId) {
|
|
236
|
-
await this.#
|
|
237
|
-
|
|
247
|
+
await this.#addTx(tx, {
|
|
248
|
+
mined: minedBlockId
|
|
249
|
+
}, opts);
|
|
250
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
238
251
|
} else {
|
|
239
|
-
await this.#
|
|
252
|
+
await this.#addTx(tx, {
|
|
253
|
+
protected: slotNumber
|
|
254
|
+
}, opts);
|
|
240
255
|
}
|
|
241
|
-
newlyAdded.push(tx);
|
|
242
256
|
} else {
|
|
243
257
|
// Existing tx - update protection and mined status
|
|
244
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
258
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
245
259
|
if (minedBlockId) {
|
|
246
|
-
this.#
|
|
260
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
261
|
+
this.#indices.markAsMined(meta, minedBlockId);
|
|
247
262
|
}
|
|
248
263
|
}
|
|
249
264
|
}
|
|
250
265
|
});
|
|
251
|
-
if (newlyAdded.length > 0) {
|
|
252
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
253
|
-
}
|
|
254
266
|
}
|
|
255
267
|
protectTxs(txHashes, block) {
|
|
256
268
|
const slotNumber = block.globalVariables.slotNumber;
|
|
257
269
|
const missing = [];
|
|
258
270
|
for (const txHash of txHashes){
|
|
259
271
|
const txHashStr = txHash.toString();
|
|
260
|
-
if (this.#
|
|
261
|
-
//
|
|
262
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
272
|
+
if (this.#indices.has(txHashStr)) {
|
|
273
|
+
// Update protection for existing tx
|
|
274
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
263
275
|
} else {
|
|
264
|
-
//
|
|
265
|
-
this.#
|
|
276
|
+
// Pre-record protection for tx we don't have yet
|
|
277
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
266
278
|
missing.push(txHash);
|
|
267
279
|
}
|
|
268
280
|
}
|
|
@@ -271,25 +283,22 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
271
283
|
async addMinedTxs(txs, block, opts) {
|
|
272
284
|
// Step 1: Build block ID
|
|
273
285
|
const blockId = await this.#buildBlockId(block);
|
|
274
|
-
const newlyAdded = [];
|
|
275
286
|
await this.#store.transactionAsync(async ()=>{
|
|
276
287
|
for (const tx of txs){
|
|
277
288
|
const txHashStr = tx.getTxHash().toString();
|
|
278
|
-
const existingMeta = this.#
|
|
289
|
+
const existingMeta = this.#indices.getMetadata(txHashStr);
|
|
279
290
|
if (existingMeta) {
|
|
280
|
-
//
|
|
281
|
-
this.#markAsMined(existingMeta, blockId);
|
|
291
|
+
// Mark existing tx as mined
|
|
292
|
+
this.#indices.markAsMined(existingMeta, blockId);
|
|
282
293
|
} else {
|
|
283
|
-
//
|
|
284
|
-
await this.#
|
|
285
|
-
|
|
294
|
+
// Add new mined tx (callback emitted by #addTx)
|
|
295
|
+
await this.#addTx(tx, {
|
|
296
|
+
mined: blockId
|
|
297
|
+
}, opts);
|
|
286
298
|
}
|
|
299
|
+
await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
|
|
287
300
|
}
|
|
288
301
|
});
|
|
289
|
-
// Step 3: Emit events for newly added txs
|
|
290
|
-
if (newlyAdded.length > 0) {
|
|
291
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
292
|
-
}
|
|
293
302
|
}
|
|
294
303
|
async handleMinedBlock(block) {
|
|
295
304
|
// Step 1: Build block ID
|
|
@@ -301,32 +310,36 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
301
310
|
const feePayers = [];
|
|
302
311
|
const found = [];
|
|
303
312
|
for (const txHash of txHashes){
|
|
304
|
-
const meta = this.#
|
|
313
|
+
const meta = this.#indices.getMetadata(txHash.toString());
|
|
305
314
|
if (meta) {
|
|
306
315
|
feePayers.push(meta.feePayer);
|
|
307
316
|
found.push(meta);
|
|
308
317
|
}
|
|
309
318
|
}
|
|
310
319
|
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
311
|
-
|
|
320
|
+
for (const meta of found){
|
|
321
|
+
this.#indices.markAsMined(meta, blockId);
|
|
322
|
+
await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
|
|
323
|
+
}
|
|
312
324
|
// Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
|
|
313
325
|
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
314
|
-
this.#callbacks.onTxsRemoved(txHashes.map((h)=>h.toBigInt()));
|
|
315
326
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
316
327
|
}
|
|
317
328
|
async prepareForSlot(slotNumber) {
|
|
329
|
+
// Step 0: Clean up slot-deleted txs from previous slots
|
|
330
|
+
await this.#deletedPool.cleanupSlotDeleted(slotNumber);
|
|
318
331
|
// Step 1: Find expired protected txs
|
|
319
|
-
const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
|
|
332
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
320
333
|
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
321
|
-
this.#clearProtection(expiredProtected);
|
|
334
|
+
this.#indices.clearProtection(expiredProtected);
|
|
322
335
|
// Step 3: Filter to only txs that have metadata and are not mined
|
|
323
|
-
const txsToRestore = this.#filterRestorable(expiredProtected);
|
|
336
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
324
337
|
if (txsToRestore.length === 0) {
|
|
325
338
|
return;
|
|
326
339
|
}
|
|
327
340
|
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
328
341
|
// Step 4: Validate for pending pool
|
|
329
|
-
const { valid, invalid } = await this.#
|
|
342
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
330
343
|
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
331
344
|
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
332
345
|
// Step 6: Delete invalid and evicted txs
|
|
@@ -345,58 +358,77 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
345
358
|
}
|
|
346
359
|
async handlePrunedBlocks(latestBlock) {
|
|
347
360
|
// Step 1: Find transactions mined after the prune point
|
|
348
|
-
const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
|
|
361
|
+
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
349
362
|
if (txsToUnmine.length === 0) {
|
|
350
363
|
this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
|
|
351
364
|
return;
|
|
352
365
|
}
|
|
353
366
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
354
|
-
// Step 2:
|
|
355
|
-
|
|
356
|
-
//
|
|
357
|
-
|
|
367
|
+
// Step 2: Mark ALL un-mined txs with their original mined block number
|
|
368
|
+
// This ensures they get soft-deleted if removed later, and only hard-deleted
|
|
369
|
+
// when their original mined block is finalized
|
|
370
|
+
await this.#deletedPool.markFromPrunedBlock(txsToUnmine.map((m)=>({
|
|
371
|
+
txHash: m.txHash,
|
|
372
|
+
minedAtBlock: BlockNumber(m.minedL2BlockId.number)
|
|
373
|
+
})));
|
|
374
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
375
|
+
for (const meta of txsToUnmine){
|
|
376
|
+
this.#indices.markAsUnmined(meta);
|
|
377
|
+
}
|
|
378
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
379
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
358
380
|
// Step 4: Validate for pending pool
|
|
359
|
-
const { valid, invalid } = await this.#
|
|
360
|
-
// Step
|
|
381
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
382
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
361
383
|
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
362
|
-
// Step
|
|
384
|
+
// Step 7: Delete invalid and evicted txs
|
|
363
385
|
await this.#deleteTxsBatch([
|
|
364
386
|
...invalid,
|
|
365
387
|
...toEvict
|
|
366
388
|
]);
|
|
367
|
-
|
|
389
|
+
this.#log.info(`Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`, {
|
|
390
|
+
txHashesRestored: valid.map((m)=>m.txHash),
|
|
391
|
+
txHashesInvalid: invalid,
|
|
392
|
+
txHashesEvicted: toEvict
|
|
393
|
+
});
|
|
394
|
+
// Step 8: Run eviction rules for ALL pending txs (not just restored ones)
|
|
368
395
|
// This handles cases like existing pending txs with invalid fee payer balances
|
|
369
396
|
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
370
397
|
}
|
|
371
398
|
async handleFailedExecution(txHashes) {
|
|
372
|
-
//
|
|
399
|
+
// Delete failed txs
|
|
373
400
|
await this.#deleteTxsBatch(txHashes.map((h)=>h.toString()));
|
|
374
|
-
this.#log.info(`Deleted ${txHashes.length} failed txs
|
|
401
|
+
this.#log.info(`Deleted ${txHashes.length} failed txs`, {
|
|
402
|
+
txHashes: txHashes.map((h)=>h.toString())
|
|
403
|
+
});
|
|
375
404
|
}
|
|
376
405
|
async handleFinalizedBlock(block) {
|
|
377
406
|
const blockNumber = block.globalVariables.blockNumber;
|
|
378
|
-
// Step 1: Find txs
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
// Step 2: Collect txs for archiving (before deletion)
|
|
407
|
+
// Step 1: Find mined txs at or before finalized block
|
|
408
|
+
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
409
|
+
// Step 2: Collect mined txs for archiving (before deletion)
|
|
384
410
|
const txsToArchive = [];
|
|
385
411
|
if (this.#archive.isEnabled()) {
|
|
386
|
-
for (const txHashStr of
|
|
412
|
+
for (const txHashStr of minedTxsToFinalize){
|
|
387
413
|
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
388
414
|
if (buffer) {
|
|
389
415
|
txsToArchive.push(Tx.fromBuffer(buffer));
|
|
390
416
|
}
|
|
391
417
|
}
|
|
392
418
|
}
|
|
393
|
-
// Step 3: Delete from active pool
|
|
394
|
-
await this.#deleteTxsBatch(
|
|
395
|
-
// Step 4:
|
|
419
|
+
// Step 3: Delete mined txs from active pool
|
|
420
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
421
|
+
// Step 4: Finalize soft-deleted txs
|
|
422
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
423
|
+
// Step 5: Archive mined txs
|
|
396
424
|
if (txsToArchive.length > 0) {
|
|
397
425
|
await this.#archive.archiveTxs(txsToArchive);
|
|
398
426
|
}
|
|
399
|
-
|
|
427
|
+
if (minedTxsToFinalize.length > 0) {
|
|
428
|
+
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
|
|
429
|
+
txHashes: minedTxsToFinalize
|
|
430
|
+
});
|
|
431
|
+
}
|
|
400
432
|
}
|
|
401
433
|
// === Query Methods ===
|
|
402
434
|
async getTxByHash(txHash) {
|
|
@@ -412,42 +444,46 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
412
444
|
return results;
|
|
413
445
|
}
|
|
414
446
|
hasTxs(txHashes) {
|
|
415
|
-
return txHashes.map((h)=>
|
|
447
|
+
return txHashes.map((h)=>{
|
|
448
|
+
const hashStr = h.toString();
|
|
449
|
+
return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
|
|
450
|
+
});
|
|
416
451
|
}
|
|
417
452
|
getTxStatus(txHash) {
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
453
|
+
const txHashStr = txHash.toString();
|
|
454
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
455
|
+
if (meta) {
|
|
456
|
+
return this.#indices.getTxState(meta);
|
|
421
457
|
}
|
|
422
|
-
|
|
458
|
+
// Check if soft-deleted
|
|
459
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
460
|
+
return 'deleted';
|
|
461
|
+
}
|
|
462
|
+
return undefined;
|
|
423
463
|
}
|
|
424
464
|
getPendingTxHashes() {
|
|
425
465
|
return [
|
|
426
|
-
...this.#iteratePendingByPriority('desc')
|
|
466
|
+
...this.#indices.iteratePendingByPriority('desc')
|
|
467
|
+
].map((hash)=>TxHash.fromString(hash));
|
|
468
|
+
}
|
|
469
|
+
getEligiblePendingTxHashes() {
|
|
470
|
+
const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
|
|
471
|
+
return [
|
|
472
|
+
...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)
|
|
427
473
|
].map((hash)=>TxHash.fromString(hash));
|
|
428
474
|
}
|
|
429
475
|
getPendingTxCount() {
|
|
430
|
-
|
|
431
|
-
for (const hashes of this.#pendingByPriority.values()){
|
|
432
|
-
count += hashes.size;
|
|
433
|
-
}
|
|
434
|
-
return count;
|
|
476
|
+
return this.#indices.getPendingTxCount();
|
|
435
477
|
}
|
|
436
478
|
getMinedTxHashes() {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
TxHash.fromString(txHash),
|
|
442
|
-
meta.minedL2BlockId
|
|
443
|
-
]);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
return result;
|
|
479
|
+
return this.#indices.getMinedTxs().map(([hash, blockId])=>[
|
|
480
|
+
TxHash.fromString(hash),
|
|
481
|
+
blockId
|
|
482
|
+
]);
|
|
447
483
|
}
|
|
448
484
|
getMinedTxCount() {
|
|
449
485
|
let count = 0;
|
|
450
|
-
for (const meta of this.#
|
|
486
|
+
for (const [, meta] of this.#indices.iterateMetadata()){
|
|
451
487
|
if (meta.minedL2BlockId !== undefined) {
|
|
452
488
|
count++;
|
|
453
489
|
}
|
|
@@ -455,26 +491,16 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
455
491
|
return count;
|
|
456
492
|
}
|
|
457
493
|
isEmpty() {
|
|
458
|
-
return this.#
|
|
494
|
+
return this.#indices.isEmpty();
|
|
459
495
|
}
|
|
460
496
|
getTxCount() {
|
|
461
|
-
return this.#
|
|
497
|
+
return this.#indices.getTxCount();
|
|
462
498
|
}
|
|
463
499
|
getArchivedTxByHash(txHash) {
|
|
464
500
|
return this.#archive.getTxByHash(txHash);
|
|
465
501
|
}
|
|
466
502
|
getLowestPriorityPending(limit) {
|
|
467
|
-
|
|
468
|
-
return [];
|
|
469
|
-
}
|
|
470
|
-
const result = [];
|
|
471
|
-
for (const hash of this.#iteratePendingByPriority('asc')){
|
|
472
|
-
result.push(TxHash.fromString(hash));
|
|
473
|
-
if (result.length >= limit) {
|
|
474
|
-
break;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return result;
|
|
503
|
+
return this.#indices.getLowestPriorityPending(limit).map((h)=>TxHash.fromString(h));
|
|
478
504
|
}
|
|
479
505
|
// === Configuration ===
|
|
480
506
|
updateConfig(config) {
|
|
@@ -485,138 +511,95 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
485
511
|
this.#config.archivedTxLimit = config.archivedTxLimit;
|
|
486
512
|
this.#archive.updateLimit(config.archivedTxLimit);
|
|
487
513
|
}
|
|
514
|
+
if (config.minTxPoolAgeMs !== undefined) {
|
|
515
|
+
this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
|
|
516
|
+
}
|
|
488
517
|
// Update eviction rules with new config
|
|
489
518
|
this.#evictionManager.updateConfig(config);
|
|
490
519
|
}
|
|
491
520
|
// === Pool Read Access ===
|
|
492
521
|
getPoolReadAccess() {
|
|
493
522
|
return {
|
|
494
|
-
getMetadata: (txHash)=>this.#
|
|
495
|
-
getTxHashByNullifier: (nullifier)=>this.#
|
|
496
|
-
getTxHashesByFeePayer: (feePayer)=>this.#
|
|
497
|
-
getPendingTxCount: ()=>this.getPendingTxCount()
|
|
523
|
+
getMetadata: (txHash)=>this.#indices.getMetadata(txHash),
|
|
524
|
+
getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
|
|
525
|
+
getTxHashesByFeePayer: (feePayer)=>this.#indices.getTxHashesByFeePayer(feePayer),
|
|
526
|
+
getPendingTxCount: ()=>this.#indices.getPendingTxCount()
|
|
498
527
|
};
|
|
499
528
|
}
|
|
500
529
|
// === Metrics ===
|
|
501
530
|
countTxs() {
|
|
502
|
-
|
|
503
|
-
let protected_ = 0;
|
|
504
|
-
let mined = 0;
|
|
505
|
-
for (const meta of this.#metadata.values()){
|
|
506
|
-
const state = this.#getTxState(meta);
|
|
507
|
-
if (state === 'pending') {
|
|
508
|
-
pending++;
|
|
509
|
-
} else if (state === 'protected') {
|
|
510
|
-
protected_++;
|
|
511
|
-
} else if (state === 'mined') {
|
|
512
|
-
mined++;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return {
|
|
516
|
-
pending,
|
|
517
|
-
protected: protected_,
|
|
518
|
-
mined
|
|
519
|
-
};
|
|
531
|
+
return this.#indices.countTxs();
|
|
520
532
|
}
|
|
521
533
|
// ============================================================================
|
|
522
|
-
// PRIVATE
|
|
534
|
+
// PRIVATE HELPERS - Transaction Management
|
|
523
535
|
// ============================================================================
|
|
524
536
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
537
|
+
* Adds a new transaction to the pool with the specified state.
|
|
538
|
+
* Emits onTxsAdded callback immediately after DB write.
|
|
539
|
+
*/ async #addTx(tx, state, opts = {}) {
|
|
540
|
+
const txHashStr = tx.getTxHash().toString();
|
|
541
|
+
const meta = await buildTxMetaData(tx);
|
|
542
|
+
meta.receivedAt = this.#dateProvider.now();
|
|
543
|
+
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
544
|
+
await this.#deletedPool.clearSoftDeleted(txHashStr);
|
|
545
|
+
this.#callbacks.onTxsAdded([
|
|
546
|
+
tx
|
|
547
|
+
], opts);
|
|
548
|
+
if (state === 'pending') {
|
|
549
|
+
this.#indices.addPending(meta);
|
|
550
|
+
} else if ('protected' in state) {
|
|
551
|
+
this.#indices.addProtected(meta, state.protected);
|
|
535
552
|
} else {
|
|
536
|
-
|
|
553
|
+
meta.minedL2BlockId = state.mined;
|
|
554
|
+
this.#indices.addMined(meta);
|
|
537
555
|
}
|
|
556
|
+
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
557
|
+
this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
|
|
558
|
+
eventName: 'tx-added-to-pool',
|
|
559
|
+
state: stateStr
|
|
560
|
+
});
|
|
561
|
+
return meta;
|
|
538
562
|
}
|
|
539
563
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*/
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
564
|
+
* Deletes a transaction from both indices and DB.
|
|
565
|
+
* Emits onTxsRemoved callback immediately after DB delete.
|
|
566
|
+
*/ /**
|
|
567
|
+
* Deletes a transaction from the pool.
|
|
568
|
+
* Delegates to DeletedPool which decides soft vs hard delete based on whether
|
|
569
|
+
* the tx is from a pruned block.
|
|
570
|
+
*/ async #deleteTx(txHashStr) {
|
|
571
|
+
this.#indices.remove(txHashStr);
|
|
572
|
+
this.#callbacks.onTxsRemoved([
|
|
573
|
+
txHashStr
|
|
574
|
+
]);
|
|
575
|
+
await this.#deletedPool.deleteTx(txHashStr);
|
|
576
|
+
}
|
|
577
|
+
/** Deletes a batch of transactions, emitting callbacks individually for each. */ async #deleteTxsBatch(txHashes) {
|
|
578
|
+
for (const txHashStr of txHashes){
|
|
579
|
+
await this.#deleteTx(txHashStr);
|
|
557
580
|
}
|
|
558
581
|
}
|
|
559
582
|
// ============================================================================
|
|
560
|
-
//
|
|
583
|
+
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
561
584
|
// ============================================================================
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const result =
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
return result;
|
|
571
|
-
}
|
|
572
|
-
/** Finds tx hashes mined at or before the given block number */ #findTxsMinedAtOrBefore(blockNumber) {
|
|
573
|
-
const result = [];
|
|
574
|
-
for (const [txHashStr, meta] of this.#metadata){
|
|
575
|
-
if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number <= blockNumber) {
|
|
576
|
-
result.push(txHashStr);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return result;
|
|
580
|
-
}
|
|
581
|
-
/** Finds protected tx hashes from slots earlier than the given slot number */ #findExpiredProtectedTxs(slotNumber) {
|
|
582
|
-
const result = [];
|
|
583
|
-
for (const [txHashStr, protectedSlot] of this.#protectedTransactions){
|
|
584
|
-
if (protectedSlot < slotNumber) {
|
|
585
|
-
result.push(txHashStr);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return result;
|
|
589
|
-
}
|
|
590
|
-
/** Filters out transactions that are currently protected */ #filterUnprotected(txs) {
|
|
591
|
-
return txs.filter((meta)=>!this.#protectedTransactions.has(meta.txHash));
|
|
592
|
-
}
|
|
593
|
-
/** Filters to transactions that have metadata and are not mined */ #filterRestorable(txHashes) {
|
|
594
|
-
const result = [];
|
|
595
|
-
for (const txHashStr of txHashes){
|
|
596
|
-
const meta = this.#metadata.get(txHashStr);
|
|
597
|
-
if (meta && meta.minedL2BlockId === undefined) {
|
|
598
|
-
result.push(meta);
|
|
599
|
-
}
|
|
585
|
+
/** Validates transaction metadata, returning true if valid */ async #validateMeta(meta, validator, context) {
|
|
586
|
+
const txValidator = validator ?? await this.#createTxValidator();
|
|
587
|
+
const result = await txValidator.validateTx(meta);
|
|
588
|
+
if (result.result !== 'valid') {
|
|
589
|
+
const contextStr = context ? ` ${context}` : '';
|
|
590
|
+
this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
|
|
591
|
+
return false;
|
|
600
592
|
}
|
|
601
|
-
return
|
|
593
|
+
return true;
|
|
602
594
|
}
|
|
603
|
-
|
|
604
|
-
/** Validates transactions for pending pool, returning valid and invalid groups */ async #validateForPending(txs) {
|
|
595
|
+
/** Validates metadata directly */ async #revalidateMetadata(metas, context) {
|
|
605
596
|
const valid = [];
|
|
606
597
|
const invalid = [];
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (
|
|
610
|
-
this.#log.warn(`Tx ${meta.txHash} not found in DB during validation`);
|
|
611
|
-
invalid.push(meta.txHash);
|
|
612
|
-
continue;
|
|
613
|
-
}
|
|
614
|
-
const tx = Tx.fromBuffer(buffer);
|
|
615
|
-
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
616
|
-
if (result.result === 'valid') {
|
|
598
|
+
const validator = await this.#createTxValidator();
|
|
599
|
+
for (const meta of metas){
|
|
600
|
+
if (await this.#validateMeta(meta, validator, context)) {
|
|
617
601
|
valid.push(meta);
|
|
618
602
|
} else {
|
|
619
|
-
this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
|
|
620
603
|
invalid.push(meta.txHash);
|
|
621
604
|
}
|
|
622
605
|
}
|
|
@@ -633,7 +616,7 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
633
616
|
const added = [];
|
|
634
617
|
const toEvict = [];
|
|
635
618
|
for (const meta of txs){
|
|
636
|
-
const conflict = checkNullifierConflict(meta, (nullifier)=>this.#
|
|
619
|
+
const conflict = checkNullifierConflict(meta, (nullifier)=>this.#indices.getTxHashByNullifier(nullifier), (txHash)=>this.#indices.getMetadata(txHash));
|
|
637
620
|
if (conflict.shouldIgnore) {
|
|
638
621
|
// Lower priority than existing - don't add, mark for deletion
|
|
639
622
|
toEvict.push(meta.txHash);
|
|
@@ -642,13 +625,13 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
642
625
|
toEvict.push(...conflict.txHashesToEvict);
|
|
643
626
|
// Remove evicted from indices immediately for subsequent checks
|
|
644
627
|
for (const evictHash of conflict.txHashesToEvict){
|
|
645
|
-
const evictMeta = this.#
|
|
628
|
+
const evictMeta = this.#indices.getMetadata(evictHash);
|
|
646
629
|
if (evictMeta) {
|
|
647
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
630
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
648
631
|
}
|
|
649
632
|
}
|
|
650
633
|
// Add to pending indices immediately so subsequent txs in the batch see this tx
|
|
651
|
-
this.#addToPendingIndices(meta);
|
|
634
|
+
this.#indices.addToPendingIndices(meta);
|
|
652
635
|
added.push(meta);
|
|
653
636
|
}
|
|
654
637
|
}
|
|
@@ -657,32 +640,10 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
657
640
|
toEvict
|
|
658
641
|
};
|
|
659
642
|
}
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
return txs;
|
|
666
|
-
}
|
|
667
|
-
/** Removes protection from tx hashes and clears them from the protected map */ #clearProtection(txHashes) {
|
|
668
|
-
for (const txHashStr of txHashes){
|
|
669
|
-
this.#protectedTransactions.delete(txHashStr);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
// --- Batch Operation Steps ---
|
|
673
|
-
/** Deletes a batch of transactions permanently */ async #deleteTxsBatch(txHashes) {
|
|
674
|
-
if (txHashes.length === 0) {
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
await this.#store.transactionAsync(async ()=>{
|
|
678
|
-
for (const txHashStr of txHashes){
|
|
679
|
-
await this.#deleteTx(txHashStr);
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
this.#callbacks.onTxsRemoved(txHashes);
|
|
683
|
-
}
|
|
684
|
-
// --- Block & Tx Info Steps ---
|
|
685
|
-
/** Builds a block ID from a block header */ async #buildBlockId(block) {
|
|
643
|
+
// ============================================================================
|
|
644
|
+
// PRIVATE HELPERS - Block & Hydration
|
|
645
|
+
// ============================================================================
|
|
646
|
+
async #buildBlockId(block) {
|
|
686
647
|
return {
|
|
687
648
|
number: block.globalVariables.blockNumber,
|
|
688
649
|
hash: (await block.hash()).toString()
|
|
@@ -698,40 +659,14 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
698
659
|
hash: txEffect.l2BlockHash.toString()
|
|
699
660
|
};
|
|
700
661
|
}
|
|
701
|
-
/** Marks a batch of transactions as mined */ #markTxsAsMined(metas, blockId) {
|
|
702
|
-
for (const meta of metas){
|
|
703
|
-
this.#markAsMined(meta, blockId);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
// --- Add Transaction Steps ---
|
|
707
|
-
/** Persists a transaction to the database */ async #persistTx(txHashStr, tx) {
|
|
708
|
-
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
709
|
-
}
|
|
710
|
-
/** Adds a new transaction as protected, returning its metadata */ async #addNewProtectedTx(tx, slotNumber) {
|
|
711
|
-
const txHashStr = tx.getTxHash().toString();
|
|
712
|
-
const meta = await buildTxMetaData(tx);
|
|
713
|
-
this.#protectedTransactions.set(txHashStr, slotNumber);
|
|
714
|
-
await this.#persistTx(txHashStr, tx);
|
|
715
|
-
this.#metadata.set(txHashStr, meta);
|
|
716
|
-
// Don't add to pending indices since it's protected
|
|
717
|
-
this.#log.verbose(`Added protected tx ${txHashStr} for slot ${slotNumber}`);
|
|
718
|
-
return meta;
|
|
719
|
-
}
|
|
720
|
-
/** Adds a new transaction as mined, returning its metadata */ async #addNewMinedTx(tx, blockId) {
|
|
721
|
-
const txHashStr = tx.getTxHash().toString();
|
|
722
|
-
const meta = await buildTxMetaData(tx);
|
|
723
|
-
meta.minedL2BlockId = blockId;
|
|
724
|
-
await this.#persistTx(txHashStr, tx);
|
|
725
|
-
this.#metadata.set(txHashStr, meta);
|
|
726
|
-
// Don't add to pending indices since it's mined
|
|
727
|
-
this.#log.verbose(`Added mined tx ${txHashStr} from block ${blockId.number}`);
|
|
728
|
-
return meta;
|
|
729
|
-
}
|
|
730
|
-
// --- Hydration Steps ---
|
|
731
662
|
/** Loads all transactions from the database, returning loaded txs and deserialization errors */ async #loadAllTxsFromDb() {
|
|
732
663
|
const loaded = [];
|
|
733
664
|
const errors = [];
|
|
734
665
|
for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()){
|
|
666
|
+
// Skip soft-deleted transactions - they stay in DB but not in indices
|
|
667
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
735
670
|
try {
|
|
736
671
|
const tx = Tx.fromBuffer(buffer);
|
|
737
672
|
const meta = await buildTxMetaData(tx);
|
|
@@ -768,43 +703,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
768
703
|
}
|
|
769
704
|
}
|
|
770
705
|
}
|
|
771
|
-
/** Partitions transactions by mined status */ #partitionByMinedStatus(txs) {
|
|
772
|
-
const mined = [];
|
|
773
|
-
const nonMined = [];
|
|
774
|
-
for (const entry of txs){
|
|
775
|
-
if (entry.meta.minedL2BlockId !== undefined) {
|
|
776
|
-
mined.push(entry.meta);
|
|
777
|
-
} else {
|
|
778
|
-
nonMined.push(entry);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
return {
|
|
782
|
-
mined,
|
|
783
|
-
nonMined
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
/** Validates non-mined transactions, returning valid metadata and invalid hashes */ async #validateNonMinedTxs(txs) {
|
|
787
|
-
const valid = [];
|
|
788
|
-
const invalid = [];
|
|
789
|
-
for (const { tx, meta } of txs){
|
|
790
|
-
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
791
|
-
if (result.result === 'valid') {
|
|
792
|
-
valid.push(meta);
|
|
793
|
-
} else {
|
|
794
|
-
this.#log.info(`Removing invalid tx ${meta.txHash} on startup: ${result.reason?.join(', ')}`);
|
|
795
|
-
invalid.push(meta.txHash);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
return {
|
|
799
|
-
valid,
|
|
800
|
-
invalid
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
/** Populates metadata index for mined transactions */ #populateMinedIndices(metas) {
|
|
804
|
-
for (const meta of metas){
|
|
805
|
-
this.#metadata.set(meta.txHash, meta);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
706
|
/**
|
|
809
707
|
* Rebuilds the pending pool by processing each tx through pre-add rules.
|
|
810
708
|
* Starts with an empty pending pool and adds txs one by one, resolving conflicts.
|
|
@@ -824,17 +722,17 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
824
722
|
}
|
|
825
723
|
// Evict any conflicting txs identified by pre-add rules
|
|
826
724
|
for (const evictHashStr of preAddResult.txHashesToEvict){
|
|
827
|
-
const evictMeta = this.#
|
|
725
|
+
const evictMeta = this.#indices.getMetadata(evictHashStr);
|
|
828
726
|
if (evictMeta) {
|
|
829
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
830
|
-
this.#
|
|
727
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
728
|
+
this.#indices.remove(evictHashStr);
|
|
831
729
|
rejected.push(evictHashStr);
|
|
832
730
|
accepted.delete(evictHashStr);
|
|
833
731
|
this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
|
|
834
732
|
}
|
|
835
733
|
}
|
|
836
|
-
// Add to
|
|
837
|
-
this.#
|
|
734
|
+
// Add to indices
|
|
735
|
+
this.#indices.addPending(meta);
|
|
838
736
|
accepted.add(meta.txHash);
|
|
839
737
|
}
|
|
840
738
|
this.#log.info(`Rebuilt pending pool: ${accepted.size} accepted, ${rejected.length} rejected`);
|
|
@@ -845,197 +743,38 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
845
743
|
rejected
|
|
846
744
|
};
|
|
847
745
|
}
|
|
848
|
-
// --- Add Pending Tx Steps ---
|
|
849
|
-
/** Checks if a tx is a duplicate (already in pool) */ #isDuplicateTx(txHashStr) {
|
|
850
|
-
return this.#metadata.has(txHashStr);
|
|
851
|
-
}
|
|
852
|
-
/** Adds a new pending tx to the pool, returning its metadata */ async #addNewPendingTx(tx) {
|
|
853
|
-
const txHashStr = tx.getTxHash().toString();
|
|
854
|
-
const meta = await buildTxMetaData(tx);
|
|
855
|
-
await this.#persistTx(txHashStr, tx);
|
|
856
|
-
this.#addToIndices(meta);
|
|
857
|
-
this.#log.verbose(`Added tx ${txHashStr} to pool`, {
|
|
858
|
-
eventName: 'tx-added-to-pool',
|
|
859
|
-
state: this.#getTxState(meta)
|
|
860
|
-
});
|
|
861
|
-
return meta;
|
|
862
|
-
}
|
|
863
|
-
// ============================================================================
|
|
864
|
-
// HELPER FUNCTIONS - Index Management
|
|
865
|
-
// ============================================================================
|
|
866
|
-
#addToIndices(meta) {
|
|
867
|
-
this.#metadata.set(meta.txHash, meta);
|
|
868
|
-
if (this.#getTxState(meta) === 'pending') {
|
|
869
|
-
this.#addToPendingIndices(meta);
|
|
870
|
-
}
|
|
871
|
-
// Protected and mined txs don't go into pending indices
|
|
872
|
-
}
|
|
873
|
-
#addToPendingIndices(meta) {
|
|
874
|
-
// Add to nullifier index
|
|
875
|
-
for (const nullifier of meta.nullifiers){
|
|
876
|
-
this.#nullifierToTxHash.set(nullifier, meta.txHash);
|
|
877
|
-
}
|
|
878
|
-
// Add to fee payer index
|
|
879
|
-
let feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
|
|
880
|
-
if (!feePayerSet) {
|
|
881
|
-
feePayerSet = new Set();
|
|
882
|
-
this.#feePayerToTxHashes.set(meta.feePayer, feePayerSet);
|
|
883
|
-
}
|
|
884
|
-
feePayerSet.add(meta.txHash);
|
|
885
|
-
// Add to priority bucket
|
|
886
|
-
let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
|
|
887
|
-
if (!prioritySet) {
|
|
888
|
-
prioritySet = new Set();
|
|
889
|
-
this.#pendingByPriority.set(meta.priorityFee, prioritySet);
|
|
890
|
-
}
|
|
891
|
-
prioritySet.add(meta.txHash);
|
|
892
|
-
}
|
|
893
|
-
#removeFromPendingIndices(meta) {
|
|
894
|
-
// Remove from nullifier index
|
|
895
|
-
for (const nullifier of meta.nullifiers){
|
|
896
|
-
this.#nullifierToTxHash.delete(nullifier);
|
|
897
|
-
}
|
|
898
|
-
// Remove from fee payer index
|
|
899
|
-
const feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
|
|
900
|
-
if (feePayerSet) {
|
|
901
|
-
feePayerSet.delete(meta.txHash);
|
|
902
|
-
if (feePayerSet.size === 0) {
|
|
903
|
-
this.#feePayerToTxHashes.delete(meta.feePayer);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
// Remove from priority map
|
|
907
|
-
const hashSet = this.#pendingByPriority.get(meta.priorityFee);
|
|
908
|
-
if (hashSet) {
|
|
909
|
-
hashSet.delete(meta.txHash);
|
|
910
|
-
if (hashSet.size === 0) {
|
|
911
|
-
this.#pendingByPriority.delete(meta.priorityFee);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
#updateProtection(txHashStr, slotNumber) {
|
|
916
|
-
const currentSlot = this.#protectedTransactions.get(txHashStr);
|
|
917
|
-
// Only update if not already protected at an equal or later slot
|
|
918
|
-
if (currentSlot !== undefined && currentSlot >= slotNumber) {
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
// Remove from pending indices if transitioning from pending to protected
|
|
922
|
-
if (currentSlot === undefined) {
|
|
923
|
-
const meta = this.#metadata.get(txHashStr);
|
|
924
|
-
if (meta) {
|
|
925
|
-
this.#removeFromPendingIndices(meta);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
this.#protectedTransactions.set(txHashStr, slotNumber);
|
|
929
|
-
}
|
|
930
|
-
#markAsMined(meta, blockId) {
|
|
931
|
-
meta.minedL2BlockId = blockId;
|
|
932
|
-
// Safe to call unconditionally - removeFromPendingIndices is idempotent
|
|
933
|
-
this.#removeFromPendingIndices(meta);
|
|
934
|
-
}
|
|
935
|
-
async #deleteTx(txHashStr) {
|
|
936
|
-
const meta = this.#metadata.get(txHashStr);
|
|
937
|
-
if (!meta) {
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
// Remove from all indices
|
|
941
|
-
this.#metadata.delete(txHashStr);
|
|
942
|
-
this.#protectedTransactions.delete(txHashStr);
|
|
943
|
-
this.#removeFromPendingIndices(meta);
|
|
944
|
-
// Remove from persistence
|
|
945
|
-
await this.#txsDB.delete(txHashStr);
|
|
946
|
-
}
|
|
947
746
|
// ============================================================================
|
|
948
|
-
//
|
|
747
|
+
// PRIVATE HELPERS - Pool Access Adapters
|
|
949
748
|
// ============================================================================
|
|
950
|
-
|
|
951
|
-
const txHashes = this.#feePayerToTxHashes.get(feePayer);
|
|
952
|
-
if (!txHashes) {
|
|
953
|
-
return [];
|
|
954
|
-
}
|
|
955
|
-
const result = [];
|
|
956
|
-
for (const txHashStr of txHashes){
|
|
957
|
-
const meta = this.#metadata.get(txHashStr);
|
|
958
|
-
if (meta && this.#getTxState(meta) === 'pending') {
|
|
959
|
-
result.push(meta);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return result;
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Creates a PoolOperations adapter for use with the eviction manager.
|
|
966
|
-
*/ #createPoolOperations() {
|
|
749
|
+
#createPoolOperations() {
|
|
967
750
|
return {
|
|
968
|
-
getPendingTxs: ()=>
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
result.push(meta);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
return result;
|
|
979
|
-
},
|
|
980
|
-
getPendingFeePayers: ()=>{
|
|
981
|
-
return Array.from(this.#feePayerToTxHashes.keys());
|
|
982
|
-
},
|
|
983
|
-
getFeePayerPendingTxs: (feePayer)=>{
|
|
984
|
-
return this.#getFeePayerPendingTxs(feePayer);
|
|
985
|
-
},
|
|
986
|
-
getPendingTxCount: ()=>{
|
|
987
|
-
return this.getPendingTxCount();
|
|
988
|
-
},
|
|
989
|
-
getLowestPriorityPending: (limit)=>{
|
|
990
|
-
return this.getLowestPriorityPending(limit).map((h)=>h.toString());
|
|
991
|
-
},
|
|
992
|
-
deleteTxs: async (txHashes)=>{
|
|
993
|
-
await this.#store.transactionAsync(async ()=>{
|
|
994
|
-
for (const txHashStr of txHashes){
|
|
995
|
-
await this.#deleteTx(txHashStr);
|
|
996
|
-
}
|
|
997
|
-
});
|
|
998
|
-
this.#callbacks.onTxsRemoved(txHashes);
|
|
999
|
-
}
|
|
751
|
+
getPendingTxs: ()=>this.#indices.getPendingTxs(),
|
|
752
|
+
getPendingFeePayers: ()=>this.#indices.getPendingFeePayers(),
|
|
753
|
+
getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
|
|
754
|
+
getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
|
|
755
|
+
getLowestPriorityPending: (limit)=>this.#indices.getLowestPriorityPending(limit),
|
|
756
|
+
deleteTxs: (txHashes)=>this.#deleteTxsBatch(txHashes)
|
|
1000
757
|
};
|
|
1001
758
|
}
|
|
1002
|
-
|
|
1003
|
-
* Creates a PreAddPoolAccess adapter for use with pre-add eviction rules.
|
|
1004
|
-
* All methods work with strings and TxMetaData for efficiency.
|
|
1005
|
-
*/ #createPreAddPoolAccess() {
|
|
759
|
+
#createPreAddPoolAccess() {
|
|
1006
760
|
return {
|
|
1007
761
|
getMetadata: (txHashStr)=>{
|
|
1008
|
-
const meta = this.#
|
|
1009
|
-
if (!meta || this.#getTxState(meta) !== 'pending') {
|
|
762
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
763
|
+
if (!meta || this.#indices.getTxState(meta) !== 'pending') {
|
|
1010
764
|
return undefined;
|
|
1011
765
|
}
|
|
1012
766
|
return meta;
|
|
1013
767
|
},
|
|
1014
|
-
getTxHashByNullifier: (nullifier)=>
|
|
1015
|
-
return this.#nullifierToTxHash.get(nullifier);
|
|
1016
|
-
},
|
|
768
|
+
getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
|
|
1017
769
|
getFeePayerBalance: async (feePayer)=>{
|
|
1018
770
|
const db = this.#worldStateSynchronizer.getCommitted();
|
|
1019
771
|
const publicStateSource = new DatabasePublicStateSource(db);
|
|
1020
772
|
const balance = await publicStateSource.storageRead(ProtocolContractAddress.FeeJuice, await computeFeePayerBalanceStorageSlot(AztecAddress.fromString(feePayer)));
|
|
1021
773
|
return balance.toBigInt();
|
|
1022
774
|
},
|
|
1023
|
-
getFeePayerPendingTxs: (feePayer)=>
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
getPendingTxCount: ()=>{
|
|
1027
|
-
return this.getPendingTxCount();
|
|
1028
|
-
},
|
|
1029
|
-
getLowestPriorityPendingTx: ()=>{
|
|
1030
|
-
// Iterate in ascending order to find the lowest priority
|
|
1031
|
-
for (const txHashStr of this.#iteratePendingByPriority('asc')){
|
|
1032
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1033
|
-
if (meta) {
|
|
1034
|
-
return meta;
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
return undefined;
|
|
1038
|
-
}
|
|
775
|
+
getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
|
|
776
|
+
getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
|
|
777
|
+
getLowestPriorityPendingTx: ()=>this.#indices.getLowestPriorityPendingTx()
|
|
1039
778
|
};
|
|
1040
779
|
}
|
|
1041
780
|
}
|