@aztec/p2p 4.0.0-rc.5 → 4.0.0-rc.7
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 +1 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +8 -20
- package/dest/client/interface.d.ts +6 -11
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +4 -11
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +5 -57
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +2 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +5 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +26 -4
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +48 -7
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +9 -10
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +2 -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_impl.d.ts +2 -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 +37 -28
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
- package/dest/msg_validators/tx_validator/factory.d.ts +114 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +219 -58
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +58 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +72 -35
- package/dest/msg_validators/tx_validator/index.d.ts +2 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
- package/dest/services/dummy_service.d.ts +4 -4
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +4 -4
- package/dest/services/encoding.d.ts +2 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +7 -7
- package/dest/services/libp2p/libp2p_service.d.ts +11 -7
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +60 -70
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
- package/dest/services/service.d.ts +5 -3
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_provider.d.ts +3 -3
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +4 -4
- package/dest/test-helpers/mock-pubsub.d.ts +3 -2
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +6 -0
- package/dest/test-helpers/testbench-utils.d.ts +2 -2
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/util.d.ts +2 -2
- package/dest/util.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +13 -33
- package/src/client/interface.ts +12 -11
- package/src/client/p2p_client.ts +7 -76
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +4 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +65 -10
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +1 -1
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +42 -28
- package/src/msg_validators/tx_validator/README.md +115 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -3
- package/src/msg_validators/tx_validator/factory.ts +353 -77
- package/src/msg_validators/tx_validator/gas_validator.ts +84 -29
- package/src/msg_validators/tx_validator/index.ts +1 -0
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/services/dummy_service.ts +6 -6
- package/src/services/encoding.ts +5 -6
- package/src/services/libp2p/libp2p_service.ts +74 -79
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
- package/src/services/service.ts +11 -2
- package/src/services/tx_provider.ts +2 -2
- package/src/test-helpers/mock-pubsub.ts +10 -0
- package/src/test-helpers/testbench-utils.ts +1 -1
- package/src/util.ts +7 -1
package/src/client/factory.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
4
5
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
5
6
|
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
6
7
|
import { AztecLMDBStoreV2, createStore } from '@aztec/kv-store/lmdb-v2';
|
|
7
|
-
import type {
|
|
8
|
+
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
8
9
|
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
9
10
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
10
11
|
import type { AztecNode, ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
11
12
|
import { P2PClientType } from '@aztec/stdlib/p2p';
|
|
12
|
-
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
13
13
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
14
14
|
|
|
15
15
|
import { P2PClient } from '../client/p2p_client.js';
|
|
@@ -17,11 +17,8 @@ import type { P2PConfig } from '../config.js';
|
|
|
17
17
|
import { AttestationPool, type AttestationPoolApi } from '../mem_pools/attestation_pool/attestation_pool.js';
|
|
18
18
|
import type { MemPools } from '../mem_pools/interface.js';
|
|
19
19
|
import type { TxPoolV2 } from '../mem_pools/tx_pool_v2/interfaces.js';
|
|
20
|
-
import type { TxMetaData } from '../mem_pools/tx_pool_v2/tx_metadata.js';
|
|
21
20
|
import { AztecKVTxPoolV2 } from '../mem_pools/tx_pool_v2/tx_pool_v2.js';
|
|
22
|
-
import {
|
|
23
|
-
import { BlockHeaderTxValidator } from '../msg_validators/tx_validator/block_header_validator.js';
|
|
24
|
-
import { DoubleSpendTxValidator } from '../msg_validators/tx_validator/double_spend_validator.js';
|
|
21
|
+
import { createTxValidatorForTransactionsEnteringPendingTxPool } from '../msg_validators/index.js';
|
|
25
22
|
import { DummyP2PService } from '../services/dummy_service.js';
|
|
26
23
|
import { LibP2PService } from '../services/index.js';
|
|
27
24
|
import { createFileStoreTxSources } from '../services/tx_collection/file_store_tx_source.js';
|
|
@@ -80,32 +77,6 @@ export async function createP2PClient<T extends P2PClientType>(
|
|
|
80
77
|
const rollupAddress = inputConfig.l1Contracts.rollupAddress.toString().toLowerCase().replace(/^0x/, '');
|
|
81
78
|
const txFileStoreBasePath = `aztec-${inputConfig.l1ChainId}-${inputConfig.rollupVersion}-0x${rollupAddress}`;
|
|
82
79
|
|
|
83
|
-
/** Validator factory for pool re-validation (double-spend + block header only). */
|
|
84
|
-
const createPoolTxValidator = async () => {
|
|
85
|
-
await worldStateSynchronizer.syncImmediate();
|
|
86
|
-
return new AggregateTxValidator<TxMetaData>(
|
|
87
|
-
new DoubleSpendTxValidator<TxMetaData>(
|
|
88
|
-
{
|
|
89
|
-
nullifiersExist: async (nullifiers: Buffer[]) => {
|
|
90
|
-
const merkleTree = worldStateSynchronizer.getCommitted();
|
|
91
|
-
const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
92
|
-
return indices.map(index => index !== undefined);
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
bindings,
|
|
96
|
-
),
|
|
97
|
-
new BlockHeaderTxValidator<TxMetaData>(
|
|
98
|
-
{
|
|
99
|
-
getArchiveIndices: (archives: BlockHash[]) => {
|
|
100
|
-
const merkleTree = worldStateSynchronizer.getCommitted();
|
|
101
|
-
return merkleTree.findLeafIndices(MerkleTreeId.ARCHIVE, archives);
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
bindings,
|
|
105
|
-
),
|
|
106
|
-
);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
80
|
const txPool =
|
|
110
81
|
deps.txPool ??
|
|
111
82
|
new AztecKVTxPoolV2(
|
|
@@ -114,7 +85,16 @@ export async function createP2PClient<T extends P2PClientType>(
|
|
|
114
85
|
{
|
|
115
86
|
l2BlockSource: archiver,
|
|
116
87
|
worldStateSynchronizer,
|
|
117
|
-
createTxValidator:
|
|
88
|
+
createTxValidator: async () => {
|
|
89
|
+
// We accept transactions if they are not expired by the next slot and block number (checked based on the ExpirationTimestamp field)
|
|
90
|
+
const currentBlockNumber = await archiver.getBlockNumber();
|
|
91
|
+
const { ts: nextSlotTimestamp } = epochCache.getEpochAndSlotInNextL1Slot();
|
|
92
|
+
return createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
93
|
+
worldStateSynchronizer,
|
|
94
|
+
nextSlotTimestamp,
|
|
95
|
+
BlockNumber(currentBlockNumber + 1),
|
|
96
|
+
);
|
|
97
|
+
},
|
|
118
98
|
},
|
|
119
99
|
telemetry,
|
|
120
100
|
{
|
package/src/client/interface.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { EthAddress, L2BlockId } from '@aztec/stdlib/block';
|
|
3
3
|
import type { ITxProvider, P2PApiFull } from '@aztec/stdlib/interfaces/server';
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
BlockProposal,
|
|
6
|
+
CheckpointAttestation,
|
|
7
|
+
CheckpointProposal,
|
|
8
|
+
P2PClientType,
|
|
9
|
+
TopicType,
|
|
10
|
+
} from '@aztec/stdlib/p2p';
|
|
5
11
|
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
|
|
6
12
|
|
|
7
13
|
import type { PeerId } from '@libp2p/interface';
|
|
@@ -134,14 +140,6 @@ export type P2P<T extends P2PClientType = P2PClientType.Full> = P2PApiFull<T> &
|
|
|
134
140
|
*/
|
|
135
141
|
hasTxsInPool(txHashes: TxHash[]): Promise<boolean[]>;
|
|
136
142
|
|
|
137
|
-
/**
|
|
138
|
-
* Returns transactions in the transaction pool by hash, requesting from the network if not found.
|
|
139
|
-
* @param txHashes - Hashes of tx to return.
|
|
140
|
-
* @param pinnedPeerId - An optional peer id that will be used to request the tx from (in addition to other random peers).
|
|
141
|
-
* @returns An array of tx or undefined.
|
|
142
|
-
*/
|
|
143
|
-
getTxsByHash(txHashes: TxHash[], pinnedPeerId: PeerId | undefined): Promise<(Tx | undefined)[]>;
|
|
144
|
-
|
|
145
143
|
/**
|
|
146
144
|
* Returns an archived transaction from the transaction pool by its hash.
|
|
147
145
|
* @param txHash - Hash of tx to return.
|
|
@@ -218,8 +216,8 @@ export type P2P<T extends P2PClientType = P2PClientType.Full> = P2PApiFull<T> &
|
|
|
218
216
|
|
|
219
217
|
updateP2PConfig(config: Partial<P2PConfig>): Promise<void>;
|
|
220
218
|
|
|
221
|
-
/** Validates a set of txs. */
|
|
222
|
-
|
|
219
|
+
/** Validates a set of txs received in a block proposal. */
|
|
220
|
+
validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void>;
|
|
223
221
|
|
|
224
222
|
/** Clears the db. */
|
|
225
223
|
clear(): Promise<void>;
|
|
@@ -237,4 +235,7 @@ export type P2P<T extends P2PClientType = P2PClientType.Full> = P2PApiFull<T> &
|
|
|
237
235
|
|
|
238
236
|
/** If node running this P2P stack is validator, passes in validator address to P2P layer */
|
|
239
237
|
registerThisValidatorAddresses(address: EthAddress[]): void;
|
|
238
|
+
|
|
239
|
+
/** Returns the number of peers in the GossipSub mesh for a given topic type. */
|
|
240
|
+
getGossipMeshPeerCount(topicType: TopicType): Promise<number>;
|
|
240
241
|
};
|
package/src/client/p2p_client.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
CheckpointAttestation,
|
|
26
26
|
type CheckpointProposal,
|
|
27
27
|
type P2PClientType,
|
|
28
|
+
type TopicType,
|
|
28
29
|
} from '@aztec/stdlib/p2p';
|
|
29
30
|
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
|
|
30
31
|
import { Attributes, type TelemetryClient, WithTracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
@@ -43,7 +44,6 @@ import {
|
|
|
43
44
|
type ReqRespSubProtocolHandler,
|
|
44
45
|
type ReqRespSubProtocolValidators,
|
|
45
46
|
} from '../services/reqresp/interface.js';
|
|
46
|
-
import { chunkTxHashesRequest } from '../services/reqresp/protocols/tx.js';
|
|
47
47
|
import type {
|
|
48
48
|
DuplicateAttestationInfo,
|
|
49
49
|
DuplicateProposalInfo,
|
|
@@ -167,6 +167,10 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
|
|
|
167
167
|
return Promise.resolve(this.p2pService.getPeers(includePending));
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
public getGossipMeshPeerCount(topicType: TopicType): Promise<number> {
|
|
171
|
+
return Promise.resolve(this.p2pService.getGossipMeshPeerCount(topicType));
|
|
172
|
+
}
|
|
173
|
+
|
|
170
174
|
public getL2BlockHash(number: BlockNumber): Promise<string | undefined> {
|
|
171
175
|
return this.l2Tips.getL2BlockHash(number);
|
|
172
176
|
}
|
|
@@ -428,36 +432,6 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
|
|
|
428
432
|
this.p2pService.registerDuplicateAttestationCallback(callback);
|
|
429
433
|
}
|
|
430
434
|
|
|
431
|
-
/**
|
|
432
|
-
* Uses the batched Request Response protocol to request a set of transactions from the network.
|
|
433
|
-
*/
|
|
434
|
-
private async requestTxsByHash(txHashes: TxHash[], pinnedPeerId: PeerId | undefined): Promise<Tx[]> {
|
|
435
|
-
const timeoutMs = 8000; // Longer timeout for now
|
|
436
|
-
const maxRetryAttempts = 10; // Keep retrying within the timeout
|
|
437
|
-
const requests = chunkTxHashesRequest(txHashes);
|
|
438
|
-
const maxPeers = Math.min(Math.ceil(requests.length / 3), 10);
|
|
439
|
-
|
|
440
|
-
const txBatches = await this.p2pService.sendBatchRequest(
|
|
441
|
-
ReqRespSubProtocol.TX,
|
|
442
|
-
requests,
|
|
443
|
-
pinnedPeerId,
|
|
444
|
-
timeoutMs,
|
|
445
|
-
maxPeers,
|
|
446
|
-
maxRetryAttempts,
|
|
447
|
-
);
|
|
448
|
-
|
|
449
|
-
const txs = txBatches.flat();
|
|
450
|
-
if (txs.length > 0) {
|
|
451
|
-
await this.txPool.addPendingTxs(txs);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
const txHashesStr = txHashes.map(tx => tx.toString()).join(', ');
|
|
455
|
-
this.log.debug(`Requested txs ${txHashesStr} (${txs.length} / ${txHashes.length}) from peers`);
|
|
456
|
-
|
|
457
|
-
// We return all transactions, even the not found ones to the caller, such they can handle missing items themselves.
|
|
458
|
-
return txs;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
435
|
public async getPendingTxs(limit?: number, after?: TxHash): Promise<Tx[]> {
|
|
462
436
|
if (limit !== undefined && limit <= 0) {
|
|
463
437
|
throw new TypeError('limit must be greater than 0');
|
|
@@ -525,49 +499,6 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
|
|
|
525
499
|
return this.txPool.hasTxs(txHashes);
|
|
526
500
|
}
|
|
527
501
|
|
|
528
|
-
/**
|
|
529
|
-
* Returns transactions in the transaction pool by hash.
|
|
530
|
-
* If a transaction is not in the pool, it will be requested from the network.
|
|
531
|
-
* @param txHashes - Hashes of the transactions to look for.
|
|
532
|
-
* @returns The txs found, or undefined if not found in the order requested.
|
|
533
|
-
*/
|
|
534
|
-
async getTxsByHash(txHashes: TxHash[], pinnedPeerId: PeerId | undefined): Promise<(Tx | undefined)[]> {
|
|
535
|
-
const txs = await Promise.all(txHashes.map(txHash => this.txPool.getTxByHash(txHash)));
|
|
536
|
-
const missingTxHashes = txs
|
|
537
|
-
.map((tx, index) => [tx, index] as const)
|
|
538
|
-
.filter(([tx, _index]) => !tx)
|
|
539
|
-
.map(([_tx, index]) => txHashes[index]);
|
|
540
|
-
|
|
541
|
-
if (missingTxHashes.length === 0) {
|
|
542
|
-
return txs as Tx[];
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const missingTxs = await this.requestTxsByHash(missingTxHashes, pinnedPeerId);
|
|
546
|
-
// TODO: optimize
|
|
547
|
-
// Merge the found txs in order
|
|
548
|
-
const mergingTxs = txHashes.map(txHash => {
|
|
549
|
-
// Is it in the txs list from the mempool?
|
|
550
|
-
for (const tx of txs) {
|
|
551
|
-
if (tx !== undefined && tx.getTxHash().equals(txHash)) {
|
|
552
|
-
return tx;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Is it in the fetched missing txs?
|
|
557
|
-
// Note: this is an O(n^2) operation, but we expect the number of missing txs to be small.
|
|
558
|
-
for (const tx of missingTxs) {
|
|
559
|
-
if (tx.getTxHash().equals(txHash)) {
|
|
560
|
-
return tx;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Otherwise return undefined
|
|
565
|
-
return undefined;
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
return mergingTxs;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
502
|
/**
|
|
572
503
|
* Returns an archived transaction in the transaction pool by its hash.
|
|
573
504
|
* @param txHash - Hash of the archived transaction to look for.
|
|
@@ -834,8 +765,8 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
|
|
|
834
765
|
this.log.debug(`Moved from state ${P2PClientState[oldState]} to ${P2PClientState[this.currentState]}`);
|
|
835
766
|
}
|
|
836
767
|
|
|
837
|
-
public
|
|
838
|
-
return this.p2pService.
|
|
768
|
+
public validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
|
|
769
|
+
return this.p2pService.validateTxsReceivedInBlockProposal(txs);
|
|
839
770
|
}
|
|
840
771
|
|
|
841
772
|
/**
|
|
@@ -20,8 +20,8 @@ import { BatchTxRequesterCollector, SendBatchRequestCollector } from '../../../s
|
|
|
20
20
|
import type { IBatchRequestTxValidator } from '../../../services/reqresp/batch-tx-requester/tx_validator.js';
|
|
21
21
|
import { RateLimitStatus } from '../../../services/reqresp/rate-limiter/rate_limiter.js';
|
|
22
22
|
import { MissingTxsTracker } from '../../../services/tx_collection/missing_txs_tracker.js';
|
|
23
|
-
import { AlwaysTrueCircuitVerifier } from '../../../test-helpers/index.js';
|
|
24
23
|
import {
|
|
24
|
+
AlwaysTrueCircuitVerifier,
|
|
25
25
|
BENCHMARK_CONSTANTS,
|
|
26
26
|
InMemoryAttestationPool,
|
|
27
27
|
InMemoryTxPool,
|
|
@@ -35,6 +35,7 @@ export class FeePayerBalancePreAddRule implements PreAddRule {
|
|
|
35
35
|
// Create combined list with incoming tx
|
|
36
36
|
const allTxs: Array<{
|
|
37
37
|
txHash: string;
|
|
38
|
+
txHashBigInt: bigint;
|
|
38
39
|
priorityFee: bigint;
|
|
39
40
|
feeLimit: bigint;
|
|
40
41
|
claimAmount: bigint;
|
|
@@ -42,6 +43,7 @@ export class FeePayerBalancePreAddRule implements PreAddRule {
|
|
|
42
43
|
}> = [
|
|
43
44
|
...existingTxs.map(t => ({
|
|
44
45
|
txHash: t.txHash,
|
|
46
|
+
txHashBigInt: t.txHashBigInt,
|
|
45
47
|
priorityFee: t.priorityFee,
|
|
46
48
|
feeLimit: t.feeLimit,
|
|
47
49
|
claimAmount: t.claimAmount,
|
|
@@ -49,6 +51,7 @@ export class FeePayerBalancePreAddRule implements PreAddRule {
|
|
|
49
51
|
})),
|
|
50
52
|
{
|
|
51
53
|
txHash: incomingMeta.txHash,
|
|
54
|
+
txHashBigInt: incomingMeta.txHashBigInt,
|
|
52
55
|
priorityFee: incomingMeta.priorityFee,
|
|
53
56
|
feeLimit: incomingMeta.feeLimit,
|
|
54
57
|
claimAmount: incomingMeta.claimAmount,
|
|
@@ -7,6 +7,6 @@ export {
|
|
|
7
7
|
type PoolReadAccess,
|
|
8
8
|
DEFAULT_TX_POOL_V2_CONFIG,
|
|
9
9
|
} from './interfaces.js';
|
|
10
|
-
export { type TxMetaData, type TxState, buildTxMetaData, comparePriority } from './tx_metadata.js';
|
|
10
|
+
export { type TxMetaData, type TxState, buildTxMetaData, comparePriority, stubTxMetaData } from './tx_metadata.js';
|
|
11
11
|
export { TxArchive } from './archive/index.js';
|
|
12
12
|
export { DeletedPool } from './deleted_pool.js';
|
|
@@ -107,12 +107,12 @@ export interface TxPoolV2 extends TypedEventEmitter<TxPoolV2Events> {
|
|
|
107
107
|
addPendingTxs(txs: Tx[], opts?: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult>;
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
* Checks if
|
|
111
|
-
*
|
|
110
|
+
* Checks if the pool would accept a transaction without modifying state.
|
|
111
|
+
* Used as a pre-check before expensive proof verification.
|
|
112
112
|
* @param tx - Transaction to check
|
|
113
|
-
* @returns
|
|
113
|
+
* @returns 'accepted' if the pool would accept, 'ignored' if already in pool or undesirable
|
|
114
114
|
*/
|
|
115
|
-
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
115
|
+
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'>;
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
118
|
* Adds transactions as immediately protected for a given slot.
|
|
@@ -2,7 +2,8 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
4
4
|
import { BlockHash, type L2BlockId } from '@aztec/stdlib/block';
|
|
5
|
-
import
|
|
5
|
+
import { Gas } from '@aztec/stdlib/gas';
|
|
6
|
+
import { type Tx, TxHash } from '@aztec/stdlib/tx';
|
|
6
7
|
|
|
7
8
|
import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
|
|
8
9
|
import { getTxPriorityFee } from '../tx_pool/priority.js';
|
|
@@ -12,6 +13,8 @@ import { type PreAddResult, TxPoolRejectionCode } from './eviction/interfaces.js
|
|
|
12
13
|
export type TxMetaValidationData = {
|
|
13
14
|
getNonEmptyNullifiers(): Fr[];
|
|
14
15
|
expirationTimestamp: bigint;
|
|
16
|
+
/** Whether the tx has public calls. Used to select the correct L2 gas minimum. */
|
|
17
|
+
forPublic?: unknown;
|
|
15
18
|
constants: {
|
|
16
19
|
anchorBlockHeader: {
|
|
17
20
|
hash(): Promise<BlockHash>;
|
|
@@ -19,6 +22,9 @@ export type TxMetaValidationData = {
|
|
|
19
22
|
blockNumber: BlockNumber;
|
|
20
23
|
};
|
|
21
24
|
};
|
|
25
|
+
txContext: {
|
|
26
|
+
gasSettings: { gasLimits: Gas };
|
|
27
|
+
};
|
|
22
28
|
};
|
|
23
29
|
};
|
|
24
30
|
|
|
@@ -34,6 +40,9 @@ export type TxMetaData = {
|
|
|
34
40
|
/** The transaction hash as hex string */
|
|
35
41
|
readonly txHash: string;
|
|
36
42
|
|
|
43
|
+
/** The transaction hash as bigint (for efficient Fr conversion in comparisons) */
|
|
44
|
+
readonly txHashBigInt: bigint;
|
|
45
|
+
|
|
37
46
|
/** Block ID (number and hash) in which the transaction was mined (undefined if not mined) */
|
|
38
47
|
minedL2BlockId?: L2BlockId;
|
|
39
48
|
|
|
@@ -77,7 +86,9 @@ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
|
|
|
77
86
|
* Fr values are captured in closures for zero-cost re-validation.
|
|
78
87
|
*/
|
|
79
88
|
export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
80
|
-
const
|
|
89
|
+
const txHashObj = tx.getTxHash();
|
|
90
|
+
const txHash = txHashObj.toString();
|
|
91
|
+
const txHashBigInt = txHashObj.toBigInt();
|
|
81
92
|
const nullifierFrs = tx.data.getNonEmptyNullifiers();
|
|
82
93
|
const nullifiers = nullifierFrs.map(n => n.toString());
|
|
83
94
|
const anchorBlockHeaderHashFr = await tx.data.constants.anchorBlockHeader.hash();
|
|
@@ -93,6 +104,7 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
93
104
|
|
|
94
105
|
return {
|
|
95
106
|
txHash,
|
|
107
|
+
txHashBigInt,
|
|
96
108
|
anchorBlockHeaderHash,
|
|
97
109
|
priorityFee,
|
|
98
110
|
feePayer,
|
|
@@ -105,11 +117,15 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
105
117
|
data: {
|
|
106
118
|
getNonEmptyNullifiers: () => nullifierFrs,
|
|
107
119
|
expirationTimestamp,
|
|
120
|
+
forPublic: !!tx.data.forPublic,
|
|
108
121
|
constants: {
|
|
109
122
|
anchorBlockHeader: {
|
|
110
123
|
hash: () => Promise.resolve(anchorBlockHeaderHashFr),
|
|
111
124
|
globalVariables: { blockNumber: anchorBlockNumber },
|
|
112
125
|
},
|
|
126
|
+
txContext: {
|
|
127
|
+
gasSettings: { gasLimits: tx.data.constants.txContext.gasSettings.gasLimits },
|
|
128
|
+
},
|
|
113
129
|
},
|
|
114
130
|
},
|
|
115
131
|
};
|
|
@@ -124,11 +140,11 @@ const HEX_STRING_BYTES = 98;
|
|
|
124
140
|
const BIGINT_BYTES = 32;
|
|
125
141
|
const FR_BYTES = 80;
|
|
126
142
|
// Fixed cost: object shell + txHash + anchorBlockHeaderHash + feePayer (3 hex strings)
|
|
127
|
-
// + priorityFee + claimAmount + feeLimit + includeByTimestamp (
|
|
143
|
+
// + txHashBigInt + priorityFee + claimAmount + feeLimit + includeByTimestamp (5 bigints)
|
|
128
144
|
// + receivedAt (number, 8 bytes) + estimatedSizeBytes (number, 8 bytes)
|
|
129
145
|
// + data closure object (~OBJECT_OVERHEAD + anchorBlockHeaderHashFr Fr + anchorBlockNumber number)
|
|
130
146
|
const FIXED_METADATA_BYTES =
|
|
131
|
-
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES +
|
|
147
|
+
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES + 5 * BIGINT_BYTES + 8 + 8 + OBJECT_OVERHEAD + FR_BYTES + 8;
|
|
132
148
|
|
|
133
149
|
/** Estimates the in-memory size of a TxMetaData object based on the number of nullifiers. */
|
|
134
150
|
function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
@@ -136,8 +152,13 @@ function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
|
136
152
|
return FIXED_METADATA_BYTES + nullifierCount * (HEX_STRING_BYTES + FR_BYTES);
|
|
137
153
|
}
|
|
138
154
|
|
|
155
|
+
/** Converts a txHash bigint back to the canonical 0x-prefixed 64-char hex string. */
|
|
156
|
+
export function txHashFromBigInt(value: bigint): string {
|
|
157
|
+
return TxHash.fromBigInt(value).toString();
|
|
158
|
+
}
|
|
159
|
+
|
|
139
160
|
/** Minimal fields required for priority comparison. */
|
|
140
|
-
type PriorityComparable = Pick<TxMetaData, '
|
|
161
|
+
type PriorityComparable = Pick<TxMetaData, 'txHashBigInt' | 'priorityFee'>;
|
|
141
162
|
|
|
142
163
|
/**
|
|
143
164
|
* Compares two priority fees in ascending order.
|
|
@@ -152,10 +173,8 @@ export function compareFee(a: bigint, b: bigint): number {
|
|
|
152
173
|
* Uses field element comparison for deterministic ordering.
|
|
153
174
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
154
175
|
*/
|
|
155
|
-
export function compareTxHash(a:
|
|
156
|
-
|
|
157
|
-
const fieldB = Fr.fromHexString(b);
|
|
158
|
-
return fieldA.cmp(fieldB);
|
|
176
|
+
export function compareTxHash(a: bigint, b: bigint): number {
|
|
177
|
+
return Fr.cmpAsBigInt(a, b);
|
|
159
178
|
}
|
|
160
179
|
|
|
161
180
|
/**
|
|
@@ -168,7 +187,7 @@ export function comparePriority(a: PriorityComparable, b: PriorityComparable): n
|
|
|
168
187
|
if (feeComparison !== 0) {
|
|
169
188
|
return feeComparison;
|
|
170
189
|
}
|
|
171
|
-
return compareTxHash(a.
|
|
190
|
+
return compareTxHash(a.txHashBigInt, b.txHashBigInt);
|
|
172
191
|
}
|
|
173
192
|
|
|
174
193
|
/**
|
|
@@ -237,6 +256,42 @@ export function stubTxMetaValidationData(overrides: { expirationTimestamp?: bigi
|
|
|
237
256
|
hash: () => Promise.resolve(new BlockHash(Fr.ZERO)),
|
|
238
257
|
globalVariables: { blockNumber: BlockNumber(0) },
|
|
239
258
|
},
|
|
259
|
+
txContext: {
|
|
260
|
+
gasSettings: { gasLimits: Gas.empty() },
|
|
261
|
+
},
|
|
240
262
|
},
|
|
241
263
|
};
|
|
242
264
|
}
|
|
265
|
+
|
|
266
|
+
/** Creates a stub TxMetaData for tests. All fields have sensible defaults and can be overridden. */
|
|
267
|
+
export function stubTxMetaData(
|
|
268
|
+
txHash: string,
|
|
269
|
+
overrides: {
|
|
270
|
+
priorityFee?: bigint;
|
|
271
|
+
feePayer?: string;
|
|
272
|
+
claimAmount?: bigint;
|
|
273
|
+
feeLimit?: bigint;
|
|
274
|
+
nullifiers?: string[];
|
|
275
|
+
expirationTimestamp?: bigint;
|
|
276
|
+
anchorBlockHeaderHash?: string;
|
|
277
|
+
} = {},
|
|
278
|
+
): TxMetaData {
|
|
279
|
+
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
280
|
+
// Normalize to canonical zero-padded hex so txHashFromBigInt(txHashBigInt) === normalizedTxHash
|
|
281
|
+
const normalizedTxHash = txHashFromBigInt(txHashBigInt);
|
|
282
|
+
const expirationTimestamp = overrides.expirationTimestamp ?? 0n;
|
|
283
|
+
return {
|
|
284
|
+
txHash: normalizedTxHash,
|
|
285
|
+
txHashBigInt,
|
|
286
|
+
anchorBlockHeaderHash: overrides.anchorBlockHeaderHash ?? '0x1234',
|
|
287
|
+
priorityFee: overrides.priorityFee ?? 100n,
|
|
288
|
+
feePayer: overrides.feePayer ?? '0xfeepayer',
|
|
289
|
+
claimAmount: overrides.claimAmount ?? 0n,
|
|
290
|
+
feeLimit: overrides.feeLimit ?? 100n,
|
|
291
|
+
nullifiers: overrides.nullifiers ?? [`0x${normalizedTxHash.slice(2)}null1`],
|
|
292
|
+
expirationTimestamp,
|
|
293
|
+
receivedAt: 0,
|
|
294
|
+
estimatedSizeBytes: 0,
|
|
295
|
+
data: stubTxMetaValidationData({ expirationTimestamp }),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { L2BlockId } from '@aztec/stdlib/block';
|
|
3
3
|
|
|
4
|
-
import { type TxMetaData, type TxState, compareFee, compareTxHash } from './tx_metadata.js';
|
|
4
|
+
import { type TxMetaData, type TxState, compareFee, compareTxHash, txHashFromBigInt } from './tx_metadata.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Manages in-memory indices for the transaction pool.
|
|
@@ -22,8 +22,8 @@ export class TxPoolIndices {
|
|
|
22
22
|
#nullifierToTxHash: Map<string, string> = new Map();
|
|
23
23
|
/** Fee payer to txHashes index (pending txs only) */
|
|
24
24
|
#feePayerToTxHashes: Map<string, Set<string>> = new Map();
|
|
25
|
-
/** Pending
|
|
26
|
-
#pendingByPriority: Map<bigint, Set<
|
|
25
|
+
/** Pending txHash bigints grouped by priority fee */
|
|
26
|
+
#pendingByPriority: Map<bigint, Set<bigint>> = new Map();
|
|
27
27
|
/** Protected transactions: txHash -> slotNumber */
|
|
28
28
|
#protectedTransactions: Map<string, SlotNumber> = new Map();
|
|
29
29
|
|
|
@@ -73,17 +73,17 @@ export class TxPoolIndices {
|
|
|
73
73
|
* @param order - 'desc' for highest priority first, 'asc' for lowest priority first
|
|
74
74
|
*/
|
|
75
75
|
*iteratePendingByPriority(order: 'asc' | 'desc', filter?: (hash: string) => boolean): Generator<string> {
|
|
76
|
-
// Use compareFee from tx_metadata, swap args for descending order
|
|
77
76
|
const feeCompareFn = order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : compareFee;
|
|
78
|
-
const hashCompareFn =
|
|
77
|
+
const hashCompareFn =
|
|
78
|
+
order === 'desc' ? (a: bigint, b: bigint) => compareTxHash(b, a) : (a: bigint, b: bigint) => compareTxHash(a, b);
|
|
79
79
|
|
|
80
80
|
const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
|
|
81
81
|
|
|
82
82
|
for (const fee of sortedFees) {
|
|
83
83
|
const hashesAtFee = this.#pendingByPriority.get(fee)!;
|
|
84
|
-
// Use compareTxHash from tx_metadata, swap args for descending order
|
|
85
84
|
const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
|
|
86
|
-
for (const
|
|
85
|
+
for (const hashBigInt of sortedHashes) {
|
|
86
|
+
const hash = txHashFromBigInt(hashBigInt);
|
|
87
87
|
if (filter === undefined || filter(hash)) {
|
|
88
88
|
yield hash;
|
|
89
89
|
}
|
|
@@ -265,8 +265,8 @@ export class TxPoolIndices {
|
|
|
265
265
|
getPendingTxs(): TxMetaData[] {
|
|
266
266
|
const result: TxMetaData[] = [];
|
|
267
267
|
for (const hashSet of this.#pendingByPriority.values()) {
|
|
268
|
-
for (const
|
|
269
|
-
const meta = this.#metadata.get(
|
|
268
|
+
for (const txHashBigInt of hashSet) {
|
|
269
|
+
const meta = this.#metadata.get(txHashFromBigInt(txHashBigInt));
|
|
270
270
|
if (meta) {
|
|
271
271
|
result.push(meta);
|
|
272
272
|
}
|
|
@@ -414,7 +414,7 @@ export class TxPoolIndices {
|
|
|
414
414
|
prioritySet = new Set();
|
|
415
415
|
this.#pendingByPriority.set(meta.priorityFee, prioritySet);
|
|
416
416
|
}
|
|
417
|
-
prioritySet.add(meta.
|
|
417
|
+
prioritySet.add(meta.txHashBigInt);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
#removeFromPendingIndices(meta: TxMetaData): void {
|
|
@@ -435,7 +435,7 @@ export class TxPoolIndices {
|
|
|
435
435
|
// Remove from priority map
|
|
436
436
|
const hashSet = this.#pendingByPriority.get(meta.priorityFee);
|
|
437
437
|
if (hashSet) {
|
|
438
|
-
hashSet.delete(meta.
|
|
438
|
+
hashSet.delete(meta.txHashBigInt);
|
|
439
439
|
if (hashSet.size === 0) {
|
|
440
440
|
this.#pendingByPriority.delete(meta.priorityFee);
|
|
441
441
|
}
|
|
@@ -74,7 +74,7 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
|
|
|
74
74
|
return this.#queue.put(() => this.#impl.addPendingTxs(txs, opts));
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
77
|
+
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
78
78
|
return this.#queue.put(() => this.#impl.canAddPendingTx(tx));
|
|
79
79
|
}
|
|
80
80
|
|