@aztec/p2p 0.0.1-commit.ff7989d6c → 0.0.1-commit.fff30aa
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/README.md +129 -3
- package/dest/client/factory.d.ts +4 -5
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +29 -28
- package/dest/client/interface.d.ts +6 -13
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +5 -13
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +19 -88
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
- package/dest/config.d.ts +32 -11
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +85 -31
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +2 -1
- package/dest/mem_pools/tx_pool/priority.d.ts +2 -2
- package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/priority.js +4 -4
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +3 -1
- 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 -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/eviction/interfaces.d.ts +7 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +8 -6
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +2 -2
- 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 +9 -7
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +46 -8
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +81 -17
- 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 +5 -3
- 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 +3 -0
- 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 +50 -40
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +10 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +16 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -8
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +48 -36
- 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/allowed_public_setup.d.ts +2 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +24 -20
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
- package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +35 -2
- package/dest/msg_validators/tx_validator/factory.d.ts +133 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +247 -60
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +67 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +104 -37
- package/dest/msg_validators/tx_validator/index.d.ts +3 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +2 -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/msg_validators/tx_validator/phases_validator.d.ts +22 -2
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +72 -24
- package/dest/services/dummy_service.d.ts +2 -3
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +1 -4
- package/dest/services/encoding.d.ts +6 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +14 -8
- package/dest/services/libp2p/libp2p_service.d.ts +15 -13
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +92 -92
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
- 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 +17 -9
- package/dest/services/service.d.ts +2 -2
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +39 -29
- package/dest/services/tx_collection/tx_source.d.ts +6 -5
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +9 -7
- 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/make-test-p2p-clients.d.ts +5 -6
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +1 -2
- package/dest/test-helpers/mock-pubsub.d.ts +2 -3
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +2 -2
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +2 -2
- package/dest/test-helpers/testbench-utils.d.ts +2 -2
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +2 -1
- package/dest/testbench/p2p_client_testbench_worker.js +7 -6
- package/dest/testbench/worker_client_manager.d.ts +3 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +4 -1
- package/package.json +14 -14
- package/src/client/factory.ts +49 -46
- package/src/client/interface.ts +5 -19
- package/src/client/p2p_client.ts +20 -118
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -2
- package/src/config.ts +124 -34
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +2 -1
- package/src/mem_pools/tx_pool/priority.ts +4 -4
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +3 -1
- package/src/mem_pools/tx_pool_v2/README.md +9 -1
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +3 -2
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +11 -1
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +2 -2
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +15 -6
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +2 -1
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +9 -7
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +113 -18
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +14 -2
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +57 -39
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +14 -4
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +20 -7
- package/src/msg_validators/proposal_validator/proposal_validator.ts +63 -40
- package/src/msg_validators/tx_validator/README.md +119 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -3
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
- package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/data_validator.ts +42 -1
- package/src/msg_validators/tx_validator/factory.ts +394 -78
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +123 -27
- package/src/msg_validators/tx_validator/index.ts +2 -0
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +82 -27
- package/src/services/dummy_service.ts +1 -5
- package/src/services/encoding.ts +14 -7
- package/src/services/libp2p/libp2p_service.ts +106 -101
- package/src/services/reqresp/README.md +229 -0
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
- package/src/services/reqresp/reqresp.ts +19 -11
- package/src/services/service.ts +1 -1
- package/src/services/tx_collection/file_store_tx_source.ts +43 -31
- package/src/services/tx_collection/tx_source.ts +8 -7
- package/src/services/tx_provider.ts +2 -2
- package/src/test-helpers/make-test-p2p-clients.ts +0 -2
- package/src/test-helpers/mock-pubsub.ts +3 -6
- package/src/test-helpers/reqresp-nodes.ts +2 -5
- package/src/test-helpers/testbench-utils.ts +2 -1
- package/src/testbench/p2p_client_testbench_worker.ts +3 -6
- package/src/testbench/worker_client_manager.ts +11 -4
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
|
@@ -15,11 +15,12 @@ export class NullifierConflictRule implements PreAddRule {
|
|
|
15
15
|
|
|
16
16
|
private log = createLogger('p2p:tx_pool_v2:nullifier_conflict_rule');
|
|
17
17
|
|
|
18
|
-
check(incomingMeta: TxMetaData, poolAccess: PreAddPoolAccess,
|
|
18
|
+
check(incomingMeta: TxMetaData, poolAccess: PreAddPoolAccess, context?: PreAddContext): Promise<PreAddResult> {
|
|
19
19
|
const result = checkNullifierConflict(
|
|
20
20
|
incomingMeta,
|
|
21
21
|
nullifier => poolAccess.getTxHashByNullifier(nullifier),
|
|
22
22
|
txHash => poolAccess.getMetadata(txHash),
|
|
23
|
+
context?.priceBumpPercentage,
|
|
23
24
|
);
|
|
24
25
|
|
|
25
26
|
if (result.shouldIgnore) {
|
|
@@ -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';
|
|
@@ -44,8 +44,8 @@ export type TxPoolV2Config = {
|
|
|
44
44
|
minTxPoolAgeMs: number;
|
|
45
45
|
/** Maximum number of evicted tx hashes to remember for metrics tracking */
|
|
46
46
|
evictedTxCacheSize: number;
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/** Minimum percentage fee increase required to replace an existing tx via RPC (0 = no bump). */
|
|
48
|
+
priceBumpPercentage: bigint;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -56,7 +56,7 @@ export const DEFAULT_TX_POOL_V2_CONFIG: TxPoolV2Config = {
|
|
|
56
56
|
archivedTxLimit: 0, // 0 = disabled
|
|
57
57
|
minTxPoolAgeMs: 2_000,
|
|
58
58
|
evictedTxCacheSize: 10_000,
|
|
59
|
-
|
|
59
|
+
priceBumpPercentage: 10n,
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -69,6 +69,8 @@ export type TxPoolV2Dependencies = {
|
|
|
69
69
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
70
70
|
/** Factory that creates a validator for re-validating pool transactions using metadata */
|
|
71
71
|
createTxValidator: () => Promise<TxValidator<TxMetaData>>;
|
|
72
|
+
/** Checks whether a tx's setup-phase calls are on the allow list. Precomputed at receipt time. */
|
|
73
|
+
checkAllowedSetupCalls: (tx: Tx) => Promise<boolean>;
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
/**
|
|
@@ -110,12 +112,12 @@ export interface TxPoolV2 extends TypedEventEmitter<TxPoolV2Events> {
|
|
|
110
112
|
addPendingTxs(txs: Tx[], opts?: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult>;
|
|
111
113
|
|
|
112
114
|
/**
|
|
113
|
-
* Checks if
|
|
114
|
-
*
|
|
115
|
+
* Checks if the pool would accept a transaction without modifying state.
|
|
116
|
+
* Used as a pre-check before expensive proof verification.
|
|
115
117
|
* @param tx - Transaction to check
|
|
116
|
-
* @returns
|
|
118
|
+
* @returns 'accepted' if the pool would accept, 'ignored' if already in pool or undesirable
|
|
117
119
|
*/
|
|
118
|
-
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
120
|
+
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'>;
|
|
119
121
|
|
|
120
122
|
/**
|
|
121
123
|
* 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
|
|
|
@@ -58,6 +67,9 @@ export type TxMetaData = {
|
|
|
58
67
|
/** Timestamp by which the transaction must be included (for expiration checks) */
|
|
59
68
|
readonly expirationTimestamp: bigint;
|
|
60
69
|
|
|
70
|
+
/** Whether the tx's setup-phase calls pass the allow list check. Computed at receipt time. */
|
|
71
|
+
readonly allowedSetupCalls: boolean;
|
|
72
|
+
|
|
61
73
|
/** Validator-compatible data, providing the same access patterns as Tx.data */
|
|
62
74
|
readonly data: TxMetaValidationData;
|
|
63
75
|
|
|
@@ -75,9 +87,15 @@ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
|
|
|
75
87
|
* Builds TxMetaData from a full Tx object.
|
|
76
88
|
* Extracts all relevant fields for efficient in-memory storage and querying.
|
|
77
89
|
* Fr values are captured in closures for zero-cost re-validation.
|
|
90
|
+
*
|
|
91
|
+
* @param allowedSetupCalls - Whether the tx's setup-phase calls pass the allow list.
|
|
92
|
+
* For gossip/RPC txs this is always `true` (already validated by PhasesTxValidator).
|
|
93
|
+
* For req/resp txs this should be computed by the caller using the phases validator.
|
|
78
94
|
*/
|
|
79
|
-
export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
80
|
-
const
|
|
95
|
+
export async function buildTxMetaData(tx: Tx, allowedSetupCalls: boolean = true): Promise<TxMetaData> {
|
|
96
|
+
const txHashObj = tx.getTxHash();
|
|
97
|
+
const txHash = txHashObj.toString();
|
|
98
|
+
const txHashBigInt = txHashObj.toBigInt();
|
|
81
99
|
const nullifierFrs = tx.data.getNonEmptyNullifiers();
|
|
82
100
|
const nullifiers = nullifierFrs.map(n => n.toString());
|
|
83
101
|
const anchorBlockHeaderHashFr = await tx.data.constants.anchorBlockHeader.hash();
|
|
@@ -93,6 +111,7 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
93
111
|
|
|
94
112
|
return {
|
|
95
113
|
txHash,
|
|
114
|
+
txHashBigInt,
|
|
96
115
|
anchorBlockHeaderHash,
|
|
97
116
|
priorityFee,
|
|
98
117
|
feePayer,
|
|
@@ -100,16 +119,21 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
100
119
|
feeLimit,
|
|
101
120
|
nullifiers,
|
|
102
121
|
expirationTimestamp,
|
|
122
|
+
allowedSetupCalls,
|
|
103
123
|
receivedAt: 0,
|
|
104
124
|
estimatedSizeBytes,
|
|
105
125
|
data: {
|
|
106
126
|
getNonEmptyNullifiers: () => nullifierFrs,
|
|
107
127
|
expirationTimestamp,
|
|
128
|
+
forPublic: !!tx.data.forPublic,
|
|
108
129
|
constants: {
|
|
109
130
|
anchorBlockHeader: {
|
|
110
131
|
hash: () => Promise.resolve(anchorBlockHeaderHashFr),
|
|
111
132
|
globalVariables: { blockNumber: anchorBlockNumber },
|
|
112
133
|
},
|
|
134
|
+
txContext: {
|
|
135
|
+
gasSettings: { gasLimits: tx.data.constants.txContext.gasSettings.gasLimits },
|
|
136
|
+
},
|
|
113
137
|
},
|
|
114
138
|
},
|
|
115
139
|
};
|
|
@@ -124,11 +148,11 @@ const HEX_STRING_BYTES = 98;
|
|
|
124
148
|
const BIGINT_BYTES = 32;
|
|
125
149
|
const FR_BYTES = 80;
|
|
126
150
|
// Fixed cost: object shell + txHash + anchorBlockHeaderHash + feePayer (3 hex strings)
|
|
127
|
-
// + priorityFee + claimAmount + feeLimit + includeByTimestamp (
|
|
151
|
+
// + txHashBigInt + priorityFee + claimAmount + feeLimit + includeByTimestamp (5 bigints)
|
|
128
152
|
// + receivedAt (number, 8 bytes) + estimatedSizeBytes (number, 8 bytes)
|
|
129
153
|
// + data closure object (~OBJECT_OVERHEAD + anchorBlockHeaderHashFr Fr + anchorBlockNumber number)
|
|
130
154
|
const FIXED_METADATA_BYTES =
|
|
131
|
-
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES +
|
|
155
|
+
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES + 5 * BIGINT_BYTES + 8 + 8 + OBJECT_OVERHEAD + FR_BYTES + 8;
|
|
132
156
|
|
|
133
157
|
/** Estimates the in-memory size of a TxMetaData object based on the number of nullifiers. */
|
|
134
158
|
function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
@@ -136,8 +160,13 @@ function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
|
136
160
|
return FIXED_METADATA_BYTES + nullifierCount * (HEX_STRING_BYTES + FR_BYTES);
|
|
137
161
|
}
|
|
138
162
|
|
|
163
|
+
/** Converts a txHash bigint back to the canonical 0x-prefixed 64-char hex string. */
|
|
164
|
+
export function txHashFromBigInt(value: bigint): string {
|
|
165
|
+
return TxHash.fromBigInt(value).toString();
|
|
166
|
+
}
|
|
167
|
+
|
|
139
168
|
/** Minimal fields required for priority comparison. */
|
|
140
|
-
type PriorityComparable = Pick<TxMetaData, '
|
|
169
|
+
type PriorityComparable = Pick<TxMetaData, 'txHashBigInt' | 'priorityFee'>;
|
|
141
170
|
|
|
142
171
|
/**
|
|
143
172
|
* Compares two priority fees in ascending order.
|
|
@@ -152,10 +181,8 @@ export function compareFee(a: bigint, b: bigint): number {
|
|
|
152
181
|
* Uses field element comparison for deterministic ordering.
|
|
153
182
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
154
183
|
*/
|
|
155
|
-
export function compareTxHash(a:
|
|
156
|
-
|
|
157
|
-
const fieldB = Fr.fromHexString(b);
|
|
158
|
-
return fieldA.cmp(fieldB);
|
|
184
|
+
export function compareTxHash(a: bigint, b: bigint): number {
|
|
185
|
+
return Fr.cmpAsBigInt(a, b);
|
|
159
186
|
}
|
|
160
187
|
|
|
161
188
|
/**
|
|
@@ -168,24 +195,41 @@ export function comparePriority(a: PriorityComparable, b: PriorityComparable): n
|
|
|
168
195
|
if (feeComparison !== 0) {
|
|
169
196
|
return feeComparison;
|
|
170
197
|
}
|
|
171
|
-
return compareTxHash(a.
|
|
198
|
+
return compareTxHash(a.txHashBigInt, b.txHashBigInt);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Returns the minimum fee required to replace an existing tx with the given price bump percentage.
|
|
203
|
+
* Uses integer arithmetic: `existingFee + existingFee * priceBumpPercentage / 100`.
|
|
204
|
+
*/
|
|
205
|
+
export function getMinimumPriceBumpFee(existingFee: bigint, priceBumpPercentage: bigint): bigint {
|
|
206
|
+
const bump = (existingFee * priceBumpPercentage) / 100n;
|
|
207
|
+
// Ensure the minimum bump is at least 1, so that replacement always requires
|
|
208
|
+
// paying strictly more — even with 0% bump or zero existing fee.
|
|
209
|
+
const effectiveBump = bump > 0n ? bump : 1n;
|
|
210
|
+
return existingFee + effectiveBump;
|
|
172
211
|
}
|
|
173
212
|
|
|
174
213
|
/**
|
|
175
214
|
* Checks for nullifier conflicts between an incoming transaction and existing pool state.
|
|
176
215
|
*
|
|
177
216
|
* When the incoming tx shares nullifiers with existing pending txs:
|
|
178
|
-
* - If the incoming tx
|
|
179
|
-
* -
|
|
217
|
+
* - If the incoming tx meets or exceeds the required priority, mark conflicting txs for eviction
|
|
218
|
+
* - Otherwise, ignore the incoming tx
|
|
219
|
+
*
|
|
220
|
+
* When `priceBumpPercentage` is provided (RPC path), uses fee-only comparison with the
|
|
221
|
+
* percentage bump instead of `comparePriority`.
|
|
180
222
|
*
|
|
181
223
|
* @param incomingMeta - Metadata for the incoming transaction
|
|
182
224
|
* @param getTxHashByNullifier - Accessor to find which tx uses a nullifier
|
|
183
225
|
* @param getMetadata - Accessor to get metadata for a tx hash
|
|
226
|
+
* @param priceBumpPercentage - Optional percentage bump required for fee-based replacement
|
|
184
227
|
*/
|
|
185
228
|
export function checkNullifierConflict(
|
|
186
229
|
incomingMeta: TxMetaData,
|
|
187
230
|
getTxHashByNullifier: (nullifier: string) => string | undefined,
|
|
188
231
|
getMetadata: (txHash: string) => TxMetaData | undefined,
|
|
232
|
+
priceBumpPercentage?: bigint,
|
|
189
233
|
): PreAddResult {
|
|
190
234
|
const txHashesToEvict: string[] = [];
|
|
191
235
|
|
|
@@ -206,19 +250,32 @@ export function checkNullifierConflict(
|
|
|
206
250
|
continue;
|
|
207
251
|
}
|
|
208
252
|
|
|
209
|
-
//
|
|
210
|
-
// Otherwise,
|
|
211
|
-
|
|
212
|
-
|
|
253
|
+
// When price bump is set (RPC path), require the incoming fee to meet the bumped threshold.
|
|
254
|
+
// Otherwise (P2P path), use full comparePriority with tx hash tiebreaker.
|
|
255
|
+
const isHigherPriority =
|
|
256
|
+
priceBumpPercentage !== undefined
|
|
257
|
+
? incomingMeta.priorityFee >= getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
258
|
+
: comparePriority(incomingMeta, conflictingMeta) > 0;
|
|
259
|
+
|
|
260
|
+
if (isHigherPriority) {
|
|
213
261
|
txHashesToEvict.push(conflictingHashStr);
|
|
214
262
|
} else {
|
|
263
|
+
const minimumFee =
|
|
264
|
+
priceBumpPercentage !== undefined
|
|
265
|
+
? getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
266
|
+
: undefined;
|
|
215
267
|
return {
|
|
216
268
|
shouldIgnore: true,
|
|
217
269
|
txHashesToEvict: [],
|
|
218
270
|
reason: {
|
|
219
271
|
code: TxPoolRejectionCode.NULLIFIER_CONFLICT,
|
|
220
|
-
message:
|
|
272
|
+
message:
|
|
273
|
+
minimumFee !== undefined
|
|
274
|
+
? `Nullifier conflict with existing tx ${conflictingHashStr}. Minimum required fee: ${minimumFee}, got: ${incomingMeta.priorityFee}`
|
|
275
|
+
: `Nullifier conflict with existing tx ${conflictingHashStr}`,
|
|
221
276
|
conflictingTxHash: conflictingHashStr,
|
|
277
|
+
minimumPriceBumpFee: minimumFee,
|
|
278
|
+
txPriorityFee: minimumFee !== undefined ? incomingMeta.priorityFee : undefined,
|
|
222
279
|
},
|
|
223
280
|
};
|
|
224
281
|
}
|
|
@@ -237,6 +294,44 @@ export function stubTxMetaValidationData(overrides: { expirationTimestamp?: bigi
|
|
|
237
294
|
hash: () => Promise.resolve(new BlockHash(Fr.ZERO)),
|
|
238
295
|
globalVariables: { blockNumber: BlockNumber(0) },
|
|
239
296
|
},
|
|
297
|
+
txContext: {
|
|
298
|
+
gasSettings: { gasLimits: Gas.empty() },
|
|
299
|
+
},
|
|
240
300
|
},
|
|
241
301
|
};
|
|
242
302
|
}
|
|
303
|
+
|
|
304
|
+
/** Creates a stub TxMetaData for tests. All fields have sensible defaults and can be overridden. */
|
|
305
|
+
export function stubTxMetaData(
|
|
306
|
+
txHash: string,
|
|
307
|
+
overrides: {
|
|
308
|
+
priorityFee?: bigint;
|
|
309
|
+
feePayer?: string;
|
|
310
|
+
claimAmount?: bigint;
|
|
311
|
+
feeLimit?: bigint;
|
|
312
|
+
nullifiers?: string[];
|
|
313
|
+
expirationTimestamp?: bigint;
|
|
314
|
+
anchorBlockHeaderHash?: string;
|
|
315
|
+
allowedSetupCalls?: boolean;
|
|
316
|
+
} = {},
|
|
317
|
+
): TxMetaData {
|
|
318
|
+
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
319
|
+
// Normalize to canonical zero-padded hex so txHashFromBigInt(txHashBigInt) === normalizedTxHash
|
|
320
|
+
const normalizedTxHash = txHashFromBigInt(txHashBigInt);
|
|
321
|
+
const expirationTimestamp = overrides.expirationTimestamp ?? 0n;
|
|
322
|
+
return {
|
|
323
|
+
txHash: normalizedTxHash,
|
|
324
|
+
txHashBigInt,
|
|
325
|
+
anchorBlockHeaderHash: overrides.anchorBlockHeaderHash ?? '0x1234',
|
|
326
|
+
priorityFee: overrides.priorityFee ?? 100n,
|
|
327
|
+
feePayer: overrides.feePayer ?? '0xfeepayer',
|
|
328
|
+
claimAmount: overrides.claimAmount ?? 0n,
|
|
329
|
+
feeLimit: overrides.feeLimit ?? 100n,
|
|
330
|
+
nullifiers: overrides.nullifiers ?? [`0x${normalizedTxHash.slice(2)}null1`],
|
|
331
|
+
expirationTimestamp,
|
|
332
|
+
allowedSetupCalls: overrides.allowedSetupCalls ?? true,
|
|
333
|
+
receivedAt: 0,
|
|
334
|
+
estimatedSizeBytes: 0,
|
|
335
|
+
data: stubTxMetaValidationData({ expirationTimestamp }),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
@@ -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
|
}
|
|
@@ -11,7 +11,14 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien
|
|
|
11
11
|
import EventEmitter from 'node:events';
|
|
12
12
|
|
|
13
13
|
import { PoolInstrumentation, PoolName } from '../instrumentation.js';
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
AddTxsResult,
|
|
16
|
+
PoolReadAccess,
|
|
17
|
+
TxPoolV2,
|
|
18
|
+
TxPoolV2Config,
|
|
19
|
+
TxPoolV2Dependencies,
|
|
20
|
+
TxPoolV2Events,
|
|
21
|
+
} from './interfaces.js';
|
|
15
22
|
import type { TxState } from './tx_metadata.js';
|
|
16
23
|
import { TxPoolV2Impl } from './tx_pool_v2_impl.js';
|
|
17
24
|
|
|
@@ -74,7 +81,7 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
|
|
|
74
81
|
return this.#queue.put(() => this.#impl.addPendingTxs(txs, opts));
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
84
|
+
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
78
85
|
return this.#queue.put(() => this.#impl.canAddPendingTx(tx));
|
|
79
86
|
}
|
|
80
87
|
|
|
@@ -162,6 +169,11 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
|
|
|
162
169
|
return this.#queue.put(() => Promise.resolve(this.#impl.getLowestPriorityPending(limit)));
|
|
163
170
|
}
|
|
164
171
|
|
|
172
|
+
/** Returns read-only access to the pool. Used for testing. */
|
|
173
|
+
getPoolReadAccess(): PoolReadAccess {
|
|
174
|
+
return this.#impl.getPoolReadAccess();
|
|
175
|
+
}
|
|
176
|
+
|
|
165
177
|
// === Configuration ===
|
|
166
178
|
|
|
167
179
|
updateConfig(config: Partial<TxPoolV2Config>): Promise<void> {
|
|
@@ -61,6 +61,7 @@ export class TxPoolV2Impl {
|
|
|
61
61
|
#l2BlockSource: L2BlockSource;
|
|
62
62
|
#worldStateSynchronizer: WorldStateSynchronizer;
|
|
63
63
|
#createTxValidator: TxPoolV2Dependencies['createTxValidator'];
|
|
64
|
+
#checkAllowedSetupCalls: TxPoolV2Dependencies['checkAllowedSetupCalls'];
|
|
64
65
|
|
|
65
66
|
// === In-Memory Indices ===
|
|
66
67
|
#indices: TxPoolIndices = new TxPoolIndices();
|
|
@@ -92,6 +93,7 @@ export class TxPoolV2Impl {
|
|
|
92
93
|
this.#l2BlockSource = deps.l2BlockSource;
|
|
93
94
|
this.#worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
94
95
|
this.#createTxValidator = deps.createTxValidator;
|
|
96
|
+
this.#checkAllowedSetupCalls = deps.checkAllowedSetupCalls;
|
|
95
97
|
|
|
96
98
|
this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
|
|
97
99
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
@@ -187,9 +189,35 @@ export class TxPoolV2Impl {
|
|
|
187
189
|
const errors = new Map<string, TxPoolRejectionError>();
|
|
188
190
|
const acceptedPending = new Set<string>();
|
|
189
191
|
|
|
192
|
+
// Phase 1: Pre-compute all throwable I/O outside the transaction.
|
|
193
|
+
// If any pre-computation throws, the entire call fails before mutations happen.
|
|
194
|
+
const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
|
|
195
|
+
|
|
196
|
+
const validator = await this.#createTxValidator();
|
|
197
|
+
|
|
198
|
+
for (const tx of txs) {
|
|
199
|
+
const txHash = tx.getTxHash();
|
|
200
|
+
const txHashStr = txHash.toString();
|
|
201
|
+
|
|
202
|
+
const meta = await buildTxMetaData(tx);
|
|
203
|
+
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
204
|
+
|
|
205
|
+
// Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
|
|
206
|
+
let isValid = true;
|
|
207
|
+
if (!minedBlockId) {
|
|
208
|
+
isValid = await this.#validateMeta(meta, validator);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
precomputed.set(txHashStr, { meta, minedBlockId, isValid });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Phase 2: Apply mutations inside the transaction using only pre-computed results,
|
|
215
|
+
// in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
|
|
190
216
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
191
217
|
const preAddContext: PreAddContext | undefined =
|
|
192
|
-
opts.feeComparisonOnly !== undefined
|
|
218
|
+
opts.feeComparisonOnly !== undefined
|
|
219
|
+
? { feeComparisonOnly: opts.feeComparisonOnly, priceBumpPercentage: this.#config.priceBumpPercentage }
|
|
220
|
+
: undefined;
|
|
193
221
|
|
|
194
222
|
await this.#store.transactionAsync(async () => {
|
|
195
223
|
for (const tx of txs) {
|
|
@@ -202,22 +230,25 @@ export class TxPoolV2Impl {
|
|
|
202
230
|
continue;
|
|
203
231
|
}
|
|
204
232
|
|
|
205
|
-
|
|
206
|
-
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
233
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
207
234
|
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
208
235
|
|
|
209
236
|
if (minedBlockId) {
|
|
210
237
|
// Already mined - add directly (protection already set if pre-protected)
|
|
211
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
238
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
212
239
|
accepted.push(txHash);
|
|
213
240
|
} else if (preProtectedSlot !== undefined) {
|
|
214
241
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
215
|
-
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
242
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
216
243
|
accepted.push(txHash);
|
|
244
|
+
} else if (!isValid) {
|
|
245
|
+
// Failed pre-computed validation
|
|
246
|
+
rejected.push(txHash);
|
|
217
247
|
} else {
|
|
218
|
-
// Regular pending tx -
|
|
248
|
+
// Regular pending tx - run pre-add rules using pre-computed metadata
|
|
219
249
|
const result = await this.#tryAddRegularPendingTx(
|
|
220
250
|
tx,
|
|
251
|
+
meta,
|
|
221
252
|
opts,
|
|
222
253
|
poolAccess,
|
|
223
254
|
acceptedPending,
|
|
@@ -227,8 +258,6 @@ export class TxPoolV2Impl {
|
|
|
227
258
|
);
|
|
228
259
|
if (result.status === 'accepted') {
|
|
229
260
|
acceptedPending.add(txHashStr);
|
|
230
|
-
} else if (result.status === 'rejected') {
|
|
231
|
-
rejected.push(txHash);
|
|
232
261
|
} else {
|
|
233
262
|
ignored.push(txHash);
|
|
234
263
|
}
|
|
@@ -259,27 +288,21 @@ export class TxPoolV2Impl {
|
|
|
259
288
|
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
260
289
|
}
|
|
261
290
|
|
|
262
|
-
/**
|
|
291
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
263
292
|
async #tryAddRegularPendingTx(
|
|
264
293
|
tx: Tx,
|
|
294
|
+
precomputedMeta: TxMetaData,
|
|
265
295
|
opts: { source?: string },
|
|
266
296
|
poolAccess: PreAddPoolAccess,
|
|
267
297
|
acceptedPending: Set<string>,
|
|
268
298
|
ignored: TxHash[],
|
|
269
299
|
errors: Map<string, TxPoolRejectionError>,
|
|
270
300
|
preAddContext?: PreAddContext,
|
|
271
|
-
): Promise<{ status: 'accepted' | 'ignored'
|
|
272
|
-
const
|
|
273
|
-
const txHashStr = txHash.toString();
|
|
274
|
-
|
|
275
|
-
// Build metadata and validate using metadata
|
|
276
|
-
const meta = await buildTxMetaData(tx);
|
|
277
|
-
if (!(await this.#validateMeta(meta))) {
|
|
278
|
-
return { status: 'rejected' };
|
|
279
|
-
}
|
|
301
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
302
|
+
const txHashStr = tx.getTxHash().toString();
|
|
280
303
|
|
|
281
304
|
// Run pre-add rules
|
|
282
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(
|
|
305
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
283
306
|
|
|
284
307
|
if (preAddResult.shouldIgnore) {
|
|
285
308
|
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
|
|
@@ -316,18 +339,12 @@ export class TxPoolV2Impl {
|
|
|
316
339
|
}
|
|
317
340
|
}
|
|
318
341
|
|
|
319
|
-
// Randomly drop the transaction for testing purposes (report as accepted so it propagates)
|
|
320
|
-
if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
|
|
321
|
-
this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
|
|
322
|
-
return { status: 'accepted' };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
342
|
// Add the transaction
|
|
326
|
-
await this.#addTx(tx, 'pending', opts);
|
|
343
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
327
344
|
return { status: 'accepted' };
|
|
328
345
|
}
|
|
329
346
|
|
|
330
|
-
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
347
|
+
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
331
348
|
const txHashStr = tx.getTxHash().toString();
|
|
332
349
|
|
|
333
350
|
// Check if already in pool
|
|
@@ -335,14 +352,8 @@ export class TxPoolV2Impl {
|
|
|
335
352
|
return 'ignored';
|
|
336
353
|
}
|
|
337
354
|
|
|
338
|
-
// Build metadata and
|
|
355
|
+
// Build metadata and check pre-add rules
|
|
339
356
|
const meta = await buildTxMetaData(tx);
|
|
340
|
-
const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
|
|
341
|
-
if (validationResult !== true) {
|
|
342
|
-
return 'rejected';
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Use pre-add rules
|
|
346
357
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
347
358
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
348
359
|
|
|
@@ -352,20 +363,25 @@ export class TxPoolV2Impl {
|
|
|
352
363
|
async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
353
364
|
const slotNumber = block.globalVariables.slotNumber;
|
|
354
365
|
|
|
366
|
+
// Precompute setup-call allow-list flags outside the store transaction
|
|
367
|
+
const allowedFlags = await Promise.all(txs.map(tx => this.#checkAllowedSetupCalls(tx)));
|
|
368
|
+
|
|
355
369
|
await this.#store.transactionAsync(async () => {
|
|
356
|
-
for (
|
|
370
|
+
for (let i = 0; i < txs.length; i++) {
|
|
371
|
+
const tx = txs[i];
|
|
357
372
|
const txHash = tx.getTxHash();
|
|
358
373
|
const txHashStr = txHash.toString();
|
|
359
374
|
const isNew = !this.#indices.has(txHashStr);
|
|
360
375
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
361
376
|
|
|
362
377
|
if (isNew) {
|
|
378
|
+
const meta = await buildTxMetaData(tx, allowedFlags[i]);
|
|
363
379
|
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
364
380
|
if (minedBlockId) {
|
|
365
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
381
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
366
382
|
this.#indices.setProtection(txHashStr, slotNumber);
|
|
367
383
|
} else {
|
|
368
|
-
await this.#addTx(tx, { protected: slotNumber }, opts);
|
|
384
|
+
await this.#addTx(tx, { protected: slotNumber }, opts, meta);
|
|
369
385
|
}
|
|
370
386
|
} else {
|
|
371
387
|
// Existing tx - update protection and mined status
|
|
@@ -771,9 +787,10 @@ export class TxPoolV2Impl {
|
|
|
771
787
|
tx: Tx,
|
|
772
788
|
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
773
789
|
opts: { source?: string } = {},
|
|
790
|
+
precomputedMeta?: TxMetaData,
|
|
774
791
|
): Promise<TxMetaData> {
|
|
775
792
|
const txHashStr = tx.getTxHash().toString();
|
|
776
|
-
const meta = await buildTxMetaData(tx);
|
|
793
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
777
794
|
meta.receivedAt = this.#dateProvider.now();
|
|
778
795
|
|
|
779
796
|
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
@@ -955,7 +972,8 @@ export class TxPoolV2Impl {
|
|
|
955
972
|
|
|
956
973
|
try {
|
|
957
974
|
const tx = Tx.fromBuffer(buffer);
|
|
958
|
-
const
|
|
975
|
+
const allowedSetupCalls = await this.#checkAllowedSetupCalls(tx);
|
|
976
|
+
const meta = await buildTxMetaData(tx, allowedSetupCalls);
|
|
959
977
|
loaded.push({ tx, meta });
|
|
960
978
|
} catch (err) {
|
|
961
979
|
this.#log.warn(`Failed to deserialize tx ${txHashStr}, deleting`, { err });
|