@aztec/p2p 0.0.1-commit.2e2504e2 → 0.0.1-commit.2eb6648a
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 +3 -3
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +2 -2
- package/dest/client/interface.d.ts +9 -2
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +4 -3
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +11 -5
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
- package/dest/config.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +94 -87
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +411 -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 +351 -85
- 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 +2 -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 +3 -3
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +5 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +2 -1
- 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 +193 -486
- 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/services/dummy_service.d.ts +6 -2
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +3 -0
- package/dest/services/libp2p/libp2p_service.d.ts +74 -33
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +299 -228
- 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/protocols/block_txs/block_txs_handler.d.ts +6 -4
- 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 +15 -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 +12 -11
- package/dest/services/service.d.ts +18 -1
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +3 -3
- package/dest/services/tx_collection/config.js +3 -3
- package/dest/services/tx_collection/fast_tx_collection.d.ts +4 -5
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +10 -14
- package/dest/services/tx_collection/index.d.ts +1 -1
- 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/test-helpers/testbench-utils.d.ts +10 -16
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +32 -30
- package/dest/testbench/p2p_client_testbench_worker.js +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +3 -4
- package/src/client/interface.ts +13 -1
- package/src/client/p2p_client.ts +20 -8
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +444 -90
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +436 -100
- package/src/mem_pools/attestation_pool/index.ts +9 -2
- package/src/mem_pools/index.ts +1 -1
- package/src/mem_pools/interface.ts +2 -2
- package/src/mem_pools/tx_pool_v2/README.md +28 -7
- package/src/mem_pools/tx_pool_v2/interfaces.ts +2 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +2 -1
- 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 +185 -568
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
- package/src/services/dummy_service.ts +6 -0
- package/src/services/libp2p/libp2p_service.ts +304 -230
- 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/protocols/block_txs/block_txs_handler.ts +22 -13
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +21 -15
- package/src/services/service.ts +20 -0
- package/src/services/tx_collection/config.ts +6 -6
- package/src/services/tx_collection/fast_tx_collection.ts +14 -24
- package/src/services/tx_collection/index.ts +1 -1
- package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
- package/src/test-helpers/testbench-utils.ts +18 -39
- package/src/testbench/p2p_client_testbench_worker.ts +1 -1
- 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
|
@@ -6,7 +6,8 @@ import { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
|
6
6
|
import { TxArchive } from './archive/index.js';
|
|
7
7
|
import { EvictionManager, FeePayerBalanceEvictionRule, FeePayerBalancePreAddRule, InvalidTxsAfterMiningRule, InvalidTxsAfterReorgRule, LowPriorityEvictionRule, LowPriorityPreAddRule, NullifierConflictRule } from './eviction/index.js';
|
|
8
8
|
import { DEFAULT_TX_POOL_V2_CONFIG } from './interfaces.js';
|
|
9
|
-
import { buildTxMetaData, checkNullifierConflict
|
|
9
|
+
import { buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
|
|
10
|
+
import { TxPoolIndices } from './tx_pool_indices.js';
|
|
10
11
|
/**
|
|
11
12
|
* Implementation of TxPoolV2 logic.
|
|
12
13
|
*
|
|
@@ -20,14 +21,7 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
20
21
|
#worldStateSynchronizer;
|
|
21
22
|
#pendingTxValidator;
|
|
22
23
|
// === 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();
|
|
24
|
+
#indices = new TxPoolIndices();
|
|
31
25
|
// === Config & Services ===
|
|
32
26
|
#config;
|
|
33
27
|
#archive;
|
|
@@ -80,15 +74,25 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
80
74
|
// Step 2: Check mined status for each tx
|
|
81
75
|
await this.#markMinedStatusBatch(loaded.map((l)=>l.meta));
|
|
82
76
|
// Step 3: Partition by mined status
|
|
83
|
-
const
|
|
77
|
+
const mined = [];
|
|
78
|
+
const nonMined = [];
|
|
79
|
+
for (const entry of loaded){
|
|
80
|
+
if (entry.meta.minedL2BlockId !== undefined) {
|
|
81
|
+
mined.push(entry.meta);
|
|
82
|
+
} else {
|
|
83
|
+
nonMined.push(entry);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
84
86
|
// Step 4: Validate non-mined transactions
|
|
85
|
-
const { valid, invalid } = await this.#
|
|
87
|
+
const { valid, invalid } = await this.#validateTxBatch(nonMined, 'on startup');
|
|
86
88
|
// Step 5: Populate mined indices (these don't need conflict resolution)
|
|
87
|
-
|
|
89
|
+
for (const meta of mined){
|
|
90
|
+
this.#indices.addMined(meta);
|
|
91
|
+
}
|
|
88
92
|
// Step 6: Rebuild pending pool by running pre-add rules for each tx
|
|
89
93
|
// This resolves nullifier conflicts, fee payer balance issues, and pool size limits
|
|
90
94
|
const { rejected } = await this.#rebuildPendingPool(valid);
|
|
91
|
-
// Step 7: Delete invalid and rejected txs from DB
|
|
95
|
+
// Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
|
|
92
96
|
const toDelete = [
|
|
93
97
|
...deserializationErrors,
|
|
94
98
|
...invalid,
|
|
@@ -108,7 +112,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
108
112
|
const accepted = [];
|
|
109
113
|
const ignored = [];
|
|
110
114
|
const rejected = [];
|
|
111
|
-
const newlyAdded = [];
|
|
112
115
|
const acceptedPending = new Set();
|
|
113
116
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
114
117
|
await this.#store.transactionAsync(async ()=>{
|
|
@@ -116,29 +119,30 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
116
119
|
const txHash = tx.getTxHash();
|
|
117
120
|
const txHashStr = txHash.toString();
|
|
118
121
|
// Skip duplicates
|
|
119
|
-
if (this.#
|
|
122
|
+
if (this.#indices.has(txHashStr)) {
|
|
120
123
|
ignored.push(txHash);
|
|
121
124
|
continue;
|
|
122
125
|
}
|
|
123
126
|
// Check mined status first (applies to all paths)
|
|
124
127
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
125
|
-
const preProtectedSlot = this.#
|
|
128
|
+
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
126
129
|
if (minedBlockId) {
|
|
127
130
|
// Already mined - add directly (protection already set if pre-protected)
|
|
128
|
-
await this.#
|
|
131
|
+
await this.#addTx(tx, {
|
|
132
|
+
mined: minedBlockId
|
|
133
|
+
}, opts);
|
|
129
134
|
accepted.push(txHash);
|
|
130
|
-
newlyAdded.push(tx);
|
|
131
135
|
} else if (preProtectedSlot !== undefined) {
|
|
132
136
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
133
|
-
await this.#
|
|
137
|
+
await this.#addTx(tx, {
|
|
138
|
+
protected: preProtectedSlot
|
|
139
|
+
}, opts);
|
|
134
140
|
accepted.push(txHash);
|
|
135
|
-
newlyAdded.push(tx);
|
|
136
141
|
} else {
|
|
137
142
|
// Regular pending tx - validate and run pre-add rules
|
|
138
|
-
const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
|
|
143
|
+
const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
|
|
139
144
|
if (result.status === 'accepted') {
|
|
140
145
|
acceptedPending.add(txHashStr);
|
|
141
|
-
newlyAdded.push(tx);
|
|
142
146
|
} else if (result.status === 'rejected') {
|
|
143
147
|
rejected.push(txHash);
|
|
144
148
|
} else {
|
|
@@ -153,29 +157,23 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
153
157
|
}
|
|
154
158
|
// Run post-add eviction rules for pending txs
|
|
155
159
|
if (acceptedPending.size > 0) {
|
|
156
|
-
const feePayers = Array.from(acceptedPending).map((txHash)=>this.#
|
|
160
|
+
const feePayers = Array.from(acceptedPending).map((txHash)=>this.#indices.getMetadata(txHash).feePayer);
|
|
157
161
|
const uniqueFeePayers = new Set(feePayers);
|
|
158
162
|
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [
|
|
159
163
|
...uniqueFeePayers
|
|
160
164
|
]);
|
|
161
165
|
}
|
|
162
|
-
// Emit events
|
|
163
|
-
if (newlyAdded.length > 0) {
|
|
164
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
165
|
-
}
|
|
166
166
|
return {
|
|
167
167
|
accepted,
|
|
168
168
|
ignored,
|
|
169
169
|
rejected
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
|
-
/** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored) {
|
|
172
|
+
/** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored) {
|
|
173
173
|
const txHash = tx.getTxHash();
|
|
174
174
|
const txHashStr = txHash.toString();
|
|
175
175
|
// Validate transaction
|
|
176
|
-
|
|
177
|
-
if (validationResult.result !== 'valid') {
|
|
178
|
-
this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
|
|
176
|
+
if (!await this.#validateTx(tx)) {
|
|
179
177
|
return {
|
|
180
178
|
status: 'rejected'
|
|
181
179
|
};
|
|
@@ -189,17 +187,18 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
189
187
|
status: 'ignored'
|
|
190
188
|
};
|
|
191
189
|
}
|
|
192
|
-
// Evict conflicts
|
|
190
|
+
// Evict conflicts
|
|
193
191
|
for (const evictHashStr of preAddResult.txHashesToEvict){
|
|
194
192
|
await this.#deleteTx(evictHashStr);
|
|
195
193
|
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
|
|
196
194
|
if (acceptedPending.has(evictHashStr)) {
|
|
195
|
+
// Evicted tx was from this batch - mark as ignored in result
|
|
197
196
|
acceptedPending.delete(evictHashStr);
|
|
198
197
|
ignored.push(TxHash.fromString(evictHashStr));
|
|
199
198
|
}
|
|
200
199
|
}
|
|
201
200
|
// Add the transaction
|
|
202
|
-
await this.#
|
|
201
|
+
await this.#addTx(tx, 'pending', opts);
|
|
203
202
|
return {
|
|
204
203
|
status: 'accepted'
|
|
205
204
|
};
|
|
@@ -207,10 +206,10 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
207
206
|
async canAddPendingTx(tx) {
|
|
208
207
|
const txHashStr = tx.getTxHash().toString();
|
|
209
208
|
// Check if already in pool
|
|
210
|
-
if (this.#
|
|
209
|
+
if (this.#indices.has(txHashStr)) {
|
|
211
210
|
return 'ignored';
|
|
212
211
|
}
|
|
213
|
-
// Validate transaction
|
|
212
|
+
// Validate transaction (no logging for dry-run check)
|
|
214
213
|
const validationResult = await this.#pendingTxValidator.validateTx(tx);
|
|
215
214
|
if (validationResult.result !== 'valid') {
|
|
216
215
|
return 'rejected';
|
|
@@ -223,46 +222,46 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
223
222
|
}
|
|
224
223
|
async addProtectedTxs(txs, block, opts) {
|
|
225
224
|
const slotNumber = block.globalVariables.slotNumber;
|
|
226
|
-
const newlyAdded = [];
|
|
227
225
|
await this.#store.transactionAsync(async ()=>{
|
|
228
226
|
for (const tx of txs){
|
|
229
227
|
const txHash = tx.getTxHash();
|
|
230
228
|
const txHashStr = txHash.toString();
|
|
231
|
-
const isNew = !this.#
|
|
229
|
+
const isNew = !this.#indices.has(txHashStr);
|
|
232
230
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
233
231
|
if (isNew) {
|
|
234
|
-
// New tx - add as mined or protected
|
|
232
|
+
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
235
233
|
if (minedBlockId) {
|
|
236
|
-
await this.#
|
|
237
|
-
|
|
234
|
+
await this.#addTx(tx, {
|
|
235
|
+
mined: minedBlockId
|
|
236
|
+
}, opts);
|
|
237
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
238
238
|
} else {
|
|
239
|
-
await this.#
|
|
239
|
+
await this.#addTx(tx, {
|
|
240
|
+
protected: slotNumber
|
|
241
|
+
}, opts);
|
|
240
242
|
}
|
|
241
|
-
newlyAdded.push(tx);
|
|
242
243
|
} else {
|
|
243
244
|
// Existing tx - update protection and mined status
|
|
244
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
245
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
245
246
|
if (minedBlockId) {
|
|
246
|
-
this.#
|
|
247
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
248
|
+
this.#indices.markAsMined(meta, minedBlockId);
|
|
247
249
|
}
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
252
|
});
|
|
251
|
-
if (newlyAdded.length > 0) {
|
|
252
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
253
|
-
}
|
|
254
253
|
}
|
|
255
254
|
protectTxs(txHashes, block) {
|
|
256
255
|
const slotNumber = block.globalVariables.slotNumber;
|
|
257
256
|
const missing = [];
|
|
258
257
|
for (const txHash of txHashes){
|
|
259
258
|
const txHashStr = txHash.toString();
|
|
260
|
-
if (this.#
|
|
261
|
-
//
|
|
262
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
259
|
+
if (this.#indices.has(txHashStr)) {
|
|
260
|
+
// Update protection for existing tx
|
|
261
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
263
262
|
} else {
|
|
264
|
-
//
|
|
265
|
-
this.#
|
|
263
|
+
// Pre-record protection for tx we don't have yet
|
|
264
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
266
265
|
missing.push(txHash);
|
|
267
266
|
}
|
|
268
267
|
}
|
|
@@ -271,25 +270,21 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
271
270
|
async addMinedTxs(txs, block, opts) {
|
|
272
271
|
// Step 1: Build block ID
|
|
273
272
|
const blockId = await this.#buildBlockId(block);
|
|
274
|
-
const newlyAdded = [];
|
|
275
273
|
await this.#store.transactionAsync(async ()=>{
|
|
276
274
|
for (const tx of txs){
|
|
277
275
|
const txHashStr = tx.getTxHash().toString();
|
|
278
|
-
const existingMeta = this.#
|
|
276
|
+
const existingMeta = this.#indices.getMetadata(txHashStr);
|
|
279
277
|
if (existingMeta) {
|
|
280
|
-
//
|
|
281
|
-
this.#markAsMined(existingMeta, blockId);
|
|
278
|
+
// Mark existing tx as mined
|
|
279
|
+
this.#indices.markAsMined(existingMeta, blockId);
|
|
282
280
|
} else {
|
|
283
|
-
//
|
|
284
|
-
await this.#
|
|
285
|
-
|
|
281
|
+
// Add new mined tx (callback emitted by #addTx)
|
|
282
|
+
await this.#addTx(tx, {
|
|
283
|
+
mined: blockId
|
|
284
|
+
}, opts);
|
|
286
285
|
}
|
|
287
286
|
}
|
|
288
287
|
});
|
|
289
|
-
// Step 3: Emit events for newly added txs
|
|
290
|
-
if (newlyAdded.length > 0) {
|
|
291
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
292
|
-
}
|
|
293
288
|
}
|
|
294
289
|
async handleMinedBlock(block) {
|
|
295
290
|
// Step 1: Build block ID
|
|
@@ -301,32 +296,33 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
301
296
|
const feePayers = [];
|
|
302
297
|
const found = [];
|
|
303
298
|
for (const txHash of txHashes){
|
|
304
|
-
const meta = this.#
|
|
299
|
+
const meta = this.#indices.getMetadata(txHash.toString());
|
|
305
300
|
if (meta) {
|
|
306
301
|
feePayers.push(meta.feePayer);
|
|
307
302
|
found.push(meta);
|
|
308
303
|
}
|
|
309
304
|
}
|
|
310
305
|
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
311
|
-
|
|
306
|
+
for (const meta of found){
|
|
307
|
+
this.#indices.markAsMined(meta, blockId);
|
|
308
|
+
}
|
|
312
309
|
// Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
|
|
313
310
|
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
314
|
-
this.#callbacks.onTxsRemoved(txHashes.map((h)=>h.toBigInt()));
|
|
315
311
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
316
312
|
}
|
|
317
313
|
async prepareForSlot(slotNumber) {
|
|
318
314
|
// Step 1: Find expired protected txs
|
|
319
|
-
const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
|
|
315
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
320
316
|
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
321
|
-
this.#clearProtection(expiredProtected);
|
|
317
|
+
this.#indices.clearProtection(expiredProtected);
|
|
322
318
|
// Step 3: Filter to only txs that have metadata and are not mined
|
|
323
|
-
const txsToRestore = this.#filterRestorable(expiredProtected);
|
|
319
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
324
320
|
if (txsToRestore.length === 0) {
|
|
325
321
|
return;
|
|
326
322
|
}
|
|
327
323
|
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
328
324
|
// Step 4: Validate for pending pool
|
|
329
|
-
const { valid, invalid } = await this.#
|
|
325
|
+
const { valid, invalid } = await this.#loadAndValidateTxs(txsToRestore, 'during prepareForSlot');
|
|
330
326
|
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
331
327
|
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
332
328
|
// Step 6: Delete invalid and evicted txs
|
|
@@ -345,18 +341,20 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
345
341
|
}
|
|
346
342
|
async handlePrunedBlocks(latestBlock) {
|
|
347
343
|
// Step 1: Find transactions mined after the prune point
|
|
348
|
-
const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
|
|
344
|
+
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
349
345
|
if (txsToUnmine.length === 0) {
|
|
350
346
|
this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
|
|
351
347
|
return;
|
|
352
348
|
}
|
|
353
349
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
354
350
|
// Step 2: Unmine - clear mined status from metadata
|
|
355
|
-
|
|
351
|
+
for (const meta of txsToUnmine){
|
|
352
|
+
this.#indices.markAsUnmined(meta);
|
|
353
|
+
}
|
|
356
354
|
// Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
357
|
-
const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
|
|
355
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
358
356
|
// Step 4: Validate for pending pool
|
|
359
|
-
const { valid, invalid } = await this.#
|
|
357
|
+
const { valid, invalid } = await this.#loadAndValidateTxs(unprotectedTxs, 'during handlePrunedBlocks');
|
|
360
358
|
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
361
359
|
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
362
360
|
// Step 6: Delete invalid and evicted txs
|
|
@@ -369,14 +367,14 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
369
367
|
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
370
368
|
}
|
|
371
369
|
async handleFailedExecution(txHashes) {
|
|
372
|
-
//
|
|
370
|
+
// Delete failed txs
|
|
373
371
|
await this.#deleteTxsBatch(txHashes.map((h)=>h.toString()));
|
|
374
372
|
this.#log.info(`Deleted ${txHashes.length} failed txs`);
|
|
375
373
|
}
|
|
376
374
|
async handleFinalizedBlock(block) {
|
|
377
375
|
const blockNumber = block.globalVariables.blockNumber;
|
|
378
376
|
// Step 1: Find txs mined at or before finalized block
|
|
379
|
-
const txsToFinalize = this.#findTxsMinedAtOrBefore(blockNumber);
|
|
377
|
+
const txsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
380
378
|
if (txsToFinalize.length === 0) {
|
|
381
379
|
return;
|
|
382
380
|
}
|
|
@@ -412,42 +410,32 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
412
410
|
return results;
|
|
413
411
|
}
|
|
414
412
|
hasTxs(txHashes) {
|
|
415
|
-
return txHashes.map((h)=>this.#
|
|
413
|
+
return txHashes.map((h)=>this.#indices.has(h.toString()));
|
|
416
414
|
}
|
|
417
415
|
getTxStatus(txHash) {
|
|
418
|
-
const meta = this.#
|
|
416
|
+
const meta = this.#indices.getMetadata(txHash.toString());
|
|
419
417
|
if (!meta) {
|
|
420
418
|
return undefined;
|
|
421
419
|
}
|
|
422
|
-
return this.#getTxState(meta);
|
|
420
|
+
return this.#indices.getTxState(meta);
|
|
423
421
|
}
|
|
424
422
|
getPendingTxHashes() {
|
|
425
423
|
return [
|
|
426
|
-
...this.#iteratePendingByPriority('desc')
|
|
424
|
+
...this.#indices.iteratePendingByPriority('desc')
|
|
427
425
|
].map((hash)=>TxHash.fromString(hash));
|
|
428
426
|
}
|
|
429
427
|
getPendingTxCount() {
|
|
430
|
-
|
|
431
|
-
for (const hashes of this.#pendingByPriority.values()){
|
|
432
|
-
count += hashes.size;
|
|
433
|
-
}
|
|
434
|
-
return count;
|
|
428
|
+
return this.#indices.getPendingTxCount();
|
|
435
429
|
}
|
|
436
430
|
getMinedTxHashes() {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
TxHash.fromString(txHash),
|
|
442
|
-
meta.minedL2BlockId
|
|
443
|
-
]);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
return result;
|
|
431
|
+
return this.#indices.getMinedTxs().map(([hash, blockId])=>[
|
|
432
|
+
TxHash.fromString(hash),
|
|
433
|
+
blockId
|
|
434
|
+
]);
|
|
447
435
|
}
|
|
448
436
|
getMinedTxCount() {
|
|
449
437
|
let count = 0;
|
|
450
|
-
for (const meta of this.#
|
|
438
|
+
for (const [, meta] of this.#indices.iterateMetadata()){
|
|
451
439
|
if (meta.minedL2BlockId !== undefined) {
|
|
452
440
|
count++;
|
|
453
441
|
}
|
|
@@ -455,26 +443,16 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
455
443
|
return count;
|
|
456
444
|
}
|
|
457
445
|
isEmpty() {
|
|
458
|
-
return this.#
|
|
446
|
+
return this.#indices.isEmpty();
|
|
459
447
|
}
|
|
460
448
|
getTxCount() {
|
|
461
|
-
return this.#
|
|
449
|
+
return this.#indices.getTxCount();
|
|
462
450
|
}
|
|
463
451
|
getArchivedTxByHash(txHash) {
|
|
464
452
|
return this.#archive.getTxByHash(txHash);
|
|
465
453
|
}
|
|
466
454
|
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;
|
|
455
|
+
return this.#indices.getLowestPriorityPending(limit).map((h)=>TxHash.fromString(h));
|
|
478
456
|
}
|
|
479
457
|
// === Configuration ===
|
|
480
458
|
updateConfig(config) {
|
|
@@ -491,132 +469,98 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
491
469
|
// === Pool Read Access ===
|
|
492
470
|
getPoolReadAccess() {
|
|
493
471
|
return {
|
|
494
|
-
getMetadata: (txHash)=>this.#
|
|
495
|
-
getTxHashByNullifier: (nullifier)=>this.#
|
|
496
|
-
getTxHashesByFeePayer: (feePayer)=>this.#
|
|
497
|
-
getPendingTxCount: ()=>this.getPendingTxCount()
|
|
472
|
+
getMetadata: (txHash)=>this.#indices.getMetadata(txHash),
|
|
473
|
+
getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
|
|
474
|
+
getTxHashesByFeePayer: (feePayer)=>this.#indices.getTxHashesByFeePayer(feePayer),
|
|
475
|
+
getPendingTxCount: ()=>this.#indices.getPendingTxCount()
|
|
498
476
|
};
|
|
499
477
|
}
|
|
500
478
|
// === Metrics ===
|
|
501
479
|
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
|
-
};
|
|
480
|
+
return this.#indices.countTxs();
|
|
520
481
|
}
|
|
521
482
|
// ============================================================================
|
|
522
|
-
// PRIVATE
|
|
483
|
+
// PRIVATE HELPERS - Transaction Management
|
|
523
484
|
// ============================================================================
|
|
524
485
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
486
|
+
* Adds a new transaction to the pool with the specified state.
|
|
487
|
+
* Emits onTxsAdded callback immediately after DB write.
|
|
488
|
+
*/ async #addTx(tx, state, opts = {}) {
|
|
489
|
+
const txHashStr = tx.getTxHash().toString();
|
|
490
|
+
const meta = await buildTxMetaData(tx);
|
|
491
|
+
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
492
|
+
this.#callbacks.onTxsAdded([
|
|
493
|
+
tx
|
|
494
|
+
], opts);
|
|
495
|
+
if (state === 'pending') {
|
|
496
|
+
this.#indices.addPending(meta);
|
|
497
|
+
} else if ('protected' in state) {
|
|
498
|
+
this.#indices.addProtected(meta, state.protected);
|
|
535
499
|
} else {
|
|
536
|
-
|
|
500
|
+
meta.minedL2BlockId = state.mined;
|
|
501
|
+
this.#indices.addMined(meta);
|
|
537
502
|
}
|
|
503
|
+
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
504
|
+
this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
|
|
505
|
+
eventName: 'tx-added-to-pool',
|
|
506
|
+
state: stateStr
|
|
507
|
+
});
|
|
508
|
+
return meta;
|
|
538
509
|
}
|
|
539
510
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*/
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
...hashesAtFee
|
|
553
|
-
].sort(hashCompareFn);
|
|
554
|
-
for (const hash of sortedHashes){
|
|
555
|
-
yield hash;
|
|
556
|
-
}
|
|
511
|
+
* Deletes a transaction from both indices and DB.
|
|
512
|
+
* Emits onTxsRemoved callback immediately after DB delete.
|
|
513
|
+
*/ async #deleteTx(txHashStr) {
|
|
514
|
+
this.#indices.remove(txHashStr);
|
|
515
|
+
await this.#txsDB.delete(txHashStr);
|
|
516
|
+
this.#callbacks.onTxsRemoved([
|
|
517
|
+
txHashStr
|
|
518
|
+
]);
|
|
519
|
+
}
|
|
520
|
+
/** Deletes a batch of transactions, emitting callbacks individually for each. */ async #deleteTxsBatch(txHashes) {
|
|
521
|
+
for (const txHashStr of txHashes){
|
|
522
|
+
await this.#deleteTx(txHashStr);
|
|
557
523
|
}
|
|
558
524
|
}
|
|
559
525
|
// ============================================================================
|
|
560
|
-
//
|
|
526
|
+
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
561
527
|
// ============================================================================
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
}
|
|
528
|
+
/** Validates a single transaction, returning true if valid */ async #validateTx(tx, context) {
|
|
529
|
+
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
530
|
+
if (result.result !== 'valid') {
|
|
531
|
+
const contextStr = context ? ` ${context}` : '';
|
|
532
|
+
this.#log.info(`Tx ${tx.getTxHash()}${contextStr} failed validation: ${result.reason?.join(', ')}`);
|
|
533
|
+
return false;
|
|
569
534
|
}
|
|
570
|
-
return
|
|
535
|
+
return true;
|
|
571
536
|
}
|
|
572
|
-
/**
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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);
|
|
537
|
+
/** Loads transactions from DB, returning loaded txs and missing hashes */ async #loadTxsFromDb(metas) {
|
|
538
|
+
const loaded = [];
|
|
539
|
+
const missing = [];
|
|
540
|
+
for (const meta of metas){
|
|
541
|
+
const buffer = await this.#txsDB.getAsync(meta.txHash);
|
|
542
|
+
if (!buffer) {
|
|
543
|
+
this.#log.warn(`Tx ${meta.txHash} not found in DB`);
|
|
544
|
+
missing.push(meta.txHash);
|
|
545
|
+
continue;
|
|
599
546
|
}
|
|
547
|
+
loaded.push({
|
|
548
|
+
tx: Tx.fromBuffer(buffer),
|
|
549
|
+
meta
|
|
550
|
+
});
|
|
600
551
|
}
|
|
601
|
-
return
|
|
552
|
+
return {
|
|
553
|
+
loaded,
|
|
554
|
+
missing
|
|
555
|
+
};
|
|
602
556
|
}
|
|
603
|
-
|
|
604
|
-
/** Validates transactions for pending pool, returning valid and invalid groups */ async #validateForPending(txs) {
|
|
557
|
+
/** Validates a batch of transactions, returning valid and invalid groups */ async #validateTxBatch(txs, context) {
|
|
605
558
|
const valid = [];
|
|
606
559
|
const invalid = [];
|
|
607
|
-
for (const meta of txs){
|
|
608
|
-
|
|
609
|
-
if (!buffer) {
|
|
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') {
|
|
560
|
+
for (const { tx, meta } of txs){
|
|
561
|
+
if (await this.#validateTx(tx, context)) {
|
|
617
562
|
valid.push(meta);
|
|
618
563
|
} else {
|
|
619
|
-
this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
|
|
620
564
|
invalid.push(meta.txHash);
|
|
621
565
|
}
|
|
622
566
|
}
|
|
@@ -625,6 +569,17 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
625
569
|
invalid
|
|
626
570
|
};
|
|
627
571
|
}
|
|
572
|
+
/** Loads transactions from DB and validates them */ async #loadAndValidateTxs(metas, context) {
|
|
573
|
+
const { loaded, missing } = await this.#loadTxsFromDb(metas);
|
|
574
|
+
const { valid, invalid } = await this.#validateTxBatch(loaded, context);
|
|
575
|
+
return {
|
|
576
|
+
valid,
|
|
577
|
+
invalid: [
|
|
578
|
+
...missing,
|
|
579
|
+
...invalid
|
|
580
|
+
]
|
|
581
|
+
};
|
|
582
|
+
}
|
|
628
583
|
/**
|
|
629
584
|
* Resolves nullifier conflicts between incoming txs and existing pending txs.
|
|
630
585
|
* Modifies the pending indices during iteration to maintain consistent state
|
|
@@ -633,7 +588,7 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
633
588
|
const added = [];
|
|
634
589
|
const toEvict = [];
|
|
635
590
|
for (const meta of txs){
|
|
636
|
-
const conflict = checkNullifierConflict(meta, (nullifier)=>this.#
|
|
591
|
+
const conflict = checkNullifierConflict(meta, (nullifier)=>this.#indices.getTxHashByNullifier(nullifier), (txHash)=>this.#indices.getMetadata(txHash));
|
|
637
592
|
if (conflict.shouldIgnore) {
|
|
638
593
|
// Lower priority than existing - don't add, mark for deletion
|
|
639
594
|
toEvict.push(meta.txHash);
|
|
@@ -642,13 +597,13 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
642
597
|
toEvict.push(...conflict.txHashesToEvict);
|
|
643
598
|
// Remove evicted from indices immediately for subsequent checks
|
|
644
599
|
for (const evictHash of conflict.txHashesToEvict){
|
|
645
|
-
const evictMeta = this.#
|
|
600
|
+
const evictMeta = this.#indices.getMetadata(evictHash);
|
|
646
601
|
if (evictMeta) {
|
|
647
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
602
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
648
603
|
}
|
|
649
604
|
}
|
|
650
605
|
// Add to pending indices immediately so subsequent txs in the batch see this tx
|
|
651
|
-
this.#addToPendingIndices(meta);
|
|
606
|
+
this.#indices.addToPendingIndices(meta);
|
|
652
607
|
added.push(meta);
|
|
653
608
|
}
|
|
654
609
|
}
|
|
@@ -657,32 +612,10 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
657
612
|
toEvict
|
|
658
613
|
};
|
|
659
614
|
}
|
|
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) {
|
|
615
|
+
// ============================================================================
|
|
616
|
+
// PRIVATE HELPERS - Block & Hydration
|
|
617
|
+
// ============================================================================
|
|
618
|
+
async #buildBlockId(block) {
|
|
686
619
|
return {
|
|
687
620
|
number: block.globalVariables.blockNumber,
|
|
688
621
|
hash: (await block.hash()).toString()
|
|
@@ -698,36 +631,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
698
631
|
hash: txEffect.l2BlockHash.toString()
|
|
699
632
|
};
|
|
700
633
|
}
|
|
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
634
|
/** Loads all transactions from the database, returning loaded txs and deserialization errors */ async #loadAllTxsFromDb() {
|
|
732
635
|
const loaded = [];
|
|
733
636
|
const errors = [];
|
|
@@ -768,43 +671,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
768
671
|
}
|
|
769
672
|
}
|
|
770
673
|
}
|
|
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
674
|
/**
|
|
809
675
|
* Rebuilds the pending pool by processing each tx through pre-add rules.
|
|
810
676
|
* Starts with an empty pending pool and adds txs one by one, resolving conflicts.
|
|
@@ -824,17 +690,17 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
824
690
|
}
|
|
825
691
|
// Evict any conflicting txs identified by pre-add rules
|
|
826
692
|
for (const evictHashStr of preAddResult.txHashesToEvict){
|
|
827
|
-
const evictMeta = this.#
|
|
693
|
+
const evictMeta = this.#indices.getMetadata(evictHashStr);
|
|
828
694
|
if (evictMeta) {
|
|
829
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
830
|
-
this.#
|
|
695
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
696
|
+
this.#indices.remove(evictHashStr);
|
|
831
697
|
rejected.push(evictHashStr);
|
|
832
698
|
accepted.delete(evictHashStr);
|
|
833
699
|
this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
|
|
834
700
|
}
|
|
835
701
|
}
|
|
836
|
-
// Add to
|
|
837
|
-
this.#
|
|
702
|
+
// Add to indices
|
|
703
|
+
this.#indices.addPending(meta);
|
|
838
704
|
accepted.add(meta.txHash);
|
|
839
705
|
}
|
|
840
706
|
this.#log.info(`Rebuilt pending pool: ${accepted.size} accepted, ${rejected.length} rejected`);
|
|
@@ -845,197 +711,38 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
|
|
|
845
711
|
rejected
|
|
846
712
|
};
|
|
847
713
|
}
|
|
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
714
|
// ============================================================================
|
|
864
|
-
//
|
|
715
|
+
// PRIVATE HELPERS - Pool Access Adapters
|
|
865
716
|
// ============================================================================
|
|
866
|
-
#
|
|
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
|
-
// ============================================================================
|
|
948
|
-
// HELPER FUNCTIONS - Adapters
|
|
949
|
-
// ============================================================================
|
|
950
|
-
/** Gets all pending transactions for a given fee payer. */ #getFeePayerPendingTxs(feePayer) {
|
|
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() {
|
|
717
|
+
#createPoolOperations() {
|
|
967
718
|
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
|
-
}
|
|
719
|
+
getPendingTxs: ()=>this.#indices.getPendingTxs(),
|
|
720
|
+
getPendingFeePayers: ()=>this.#indices.getPendingFeePayers(),
|
|
721
|
+
getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
|
|
722
|
+
getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
|
|
723
|
+
getLowestPriorityPending: (limit)=>this.#indices.getLowestPriorityPending(limit),
|
|
724
|
+
deleteTxs: (txHashes)=>this.#deleteTxsBatch(txHashes)
|
|
1000
725
|
};
|
|
1001
726
|
}
|
|
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() {
|
|
727
|
+
#createPreAddPoolAccess() {
|
|
1006
728
|
return {
|
|
1007
729
|
getMetadata: (txHashStr)=>{
|
|
1008
|
-
const meta = this.#
|
|
1009
|
-
if (!meta || this.#getTxState(meta) !== 'pending') {
|
|
730
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
731
|
+
if (!meta || this.#indices.getTxState(meta) !== 'pending') {
|
|
1010
732
|
return undefined;
|
|
1011
733
|
}
|
|
1012
734
|
return meta;
|
|
1013
735
|
},
|
|
1014
|
-
getTxHashByNullifier: (nullifier)=>
|
|
1015
|
-
return this.#nullifierToTxHash.get(nullifier);
|
|
1016
|
-
},
|
|
736
|
+
getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
|
|
1017
737
|
getFeePayerBalance: async (feePayer)=>{
|
|
1018
738
|
const db = this.#worldStateSynchronizer.getCommitted();
|
|
1019
739
|
const publicStateSource = new DatabasePublicStateSource(db);
|
|
1020
740
|
const balance = await publicStateSource.storageRead(ProtocolContractAddress.FeeJuice, await computeFeePayerBalanceStorageSlot(AztecAddress.fromString(feePayer)));
|
|
1021
741
|
return balance.toBigInt();
|
|
1022
742
|
},
|
|
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
|
-
}
|
|
743
|
+
getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
|
|
744
|
+
getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
|
|
745
|
+
getLowestPriorityPendingTx: ()=>this.#indices.getLowestPriorityPendingTx()
|
|
1039
746
|
};
|
|
1040
747
|
}
|
|
1041
748
|
}
|