@aztec/p2p 0.0.1-commit.db765a8 → 0.0.1-commit.df81a97b5
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 +2 -2
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +21 -8
- package/dest/client/p2p_client.d.ts +1 -1
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +22 -34
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +3 -3
- package/dest/config.d.ts +24 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +66 -7
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +3 -3
- package/dest/mem_pools/attestation_pool/attestation_pool.js +3 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +6 -6
- 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 +2 -1
- 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/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/interfaces.d.ts +9 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +25 -10
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +33 -10
- 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 +26 -43
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +4 -2
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +3 -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 +21 -6
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +5 -4
- package/dest/msg_validators/clock_tolerance.d.ts +1 -1
- package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.js +4 -3
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +5 -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 +5 -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 +12 -9
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +51 -49
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +1 -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 +21 -32
- 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 +23 -4
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +36 -10
- 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 +13 -4
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +39 -9
- 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/metadata_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +21 -1
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +49 -2
- package/dest/services/encoding.d.ts +5 -1
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +7 -1
- package/dest/services/libp2p/libp2p_service.d.ts +4 -9
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +125 -68
- package/dest/services/peer-manager/peer_manager.d.ts +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +4 -2
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +11 -8
- 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 +69 -65
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +3 -2
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +5 -4
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +13 -7
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +3 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +3 -0
- package/dest/services/reqresp/reqresp.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +17 -9
- package/dest/services/tx_collection/fast_tx_collection.d.ts +1 -4
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +57 -73
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +6 -7
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +4 -4
- package/dest/services/tx_collection/request_tracker.d.ts +53 -0
- package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
- package/dest/services/tx_collection/request_tracker.js +84 -0
- package/dest/services/tx_collection/slow_tx_collection.js +1 -1
- package/dest/services/tx_collection/tx_collection.d.ts +3 -6
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.d.ts +1 -1
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +22 -3
- package/dest/testbench/p2p_client_testbench_worker.js +5 -4
- 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 +6 -2
- package/dest/util.d.ts +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +36 -12
- package/src/client/p2p_client.ts +22 -34
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +4 -6
- package/src/config.ts +92 -4
- package/src/mem_pools/attestation_pool/attestation_pool.ts +3 -3
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +6 -6
- 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 +2 -1
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +11 -1
- 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/interfaces.ts +9 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +52 -12
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +29 -43
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +13 -1
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +23 -6
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +5 -4
- package/src/msg_validators/clock_tolerance.ts +4 -3
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +13 -3
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +19 -6
- package/src/msg_validators/proposal_validator/proposal_validator.ts +63 -53
- package/src/msg_validators/tx_validator/README.md +5 -1
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +16 -35
- 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 +43 -3
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +41 -8
- package/src/msg_validators/tx_validator/index.ts +1 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
- package/src/msg_validators/tx_validator/phases_validator.ts +60 -1
- package/src/services/encoding.ts +9 -1
- package/src/services/libp2p/libp2p_service.ts +118 -78
- package/src/services/peer-manager/peer_manager.ts +5 -2
- package/src/services/reqresp/README.md +229 -0
- package/src/services/reqresp/batch-tx-requester/README.md +46 -7
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +64 -69
- package/src/services/reqresp/batch-tx-requester/interface.ts +2 -1
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +13 -6
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +5 -0
- package/src/services/reqresp/reqresp.ts +19 -11
- package/src/services/tx_collection/fast_tx_collection.ts +57 -83
- package/src/services/tx_collection/proposal_tx_collector.ts +8 -13
- package/src/services/tx_collection/request_tracker.ts +127 -0
- package/src/services/tx_collection/slow_tx_collection.ts +1 -1
- package/src/services/tx_collection/tx_collection.ts +3 -5
- package/src/test-helpers/make-test-p2p-clients.ts +1 -1
- package/src/test-helpers/reqresp-nodes.ts +1 -1
- package/src/test-helpers/testbench-utils.ts +29 -3
- package/src/testbench/p2p_client_testbench_worker.ts +5 -6
- package/src/testbench/worker_client_manager.ts +13 -5
- package/src/util.ts +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -24
- 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 -378
- package/dest/services/tx_collection/missing_txs_tracker.d.ts +0 -32
- package/dest/services/tx_collection/missing_txs_tracker.d.ts.map +0 -1
- package/dest/services/tx_collection/missing_txs_tracker.js +0 -27
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -373
- package/src/services/tx_collection/missing_txs_tracker.ts +0 -52
|
@@ -3,11 +3,11 @@ import { SecretValue } from '@aztec/foundation/config';
|
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { sleep } from '@aztec/foundation/sleep';
|
|
5
5
|
import { DateProvider, Timer, executeTimeout } from '@aztec/foundation/timer';
|
|
6
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
7
6
|
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
8
7
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
9
8
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
10
9
|
import type { ClientProtocolCircuitVerifier } from '@aztec/stdlib/interfaces/server';
|
|
10
|
+
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
11
11
|
import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
12
12
|
import type { Tx, TxValidationResult } from '@aztec/stdlib/tx';
|
|
13
13
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
@@ -19,7 +19,7 @@ import type { P2PConfig } from '../../../config.js';
|
|
|
19
19
|
import { BatchTxRequesterCollector, SendBatchRequestCollector } from '../../../services/index.js';
|
|
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
|
-
import {
|
|
22
|
+
import { RequestTracker } from '../../../services/tx_collection/request_tracker.js';
|
|
23
23
|
import {
|
|
24
24
|
AlwaysTrueCircuitVerifier,
|
|
25
25
|
BENCHMARK_CONSTANTS,
|
|
@@ -213,10 +213,9 @@ async function runCollector(cmd: Extract<WorkerCommand, { type: 'RUN_COLLECTOR'
|
|
|
213
213
|
const fetched = await executeTimeout(
|
|
214
214
|
(_signal: AbortSignal) =>
|
|
215
215
|
collector.collectTxs(
|
|
216
|
-
|
|
216
|
+
RequestTracker.create(parsedTxHashes, new Date(Date.now() + internalTimeoutMs)),
|
|
217
217
|
parsedProposal,
|
|
218
218
|
pinnedPeer,
|
|
219
|
-
internalTimeoutMs,
|
|
220
219
|
),
|
|
221
220
|
timeoutMs,
|
|
222
221
|
() => new Error(`Collector timed out after ${timeoutMs}ms`),
|
|
@@ -231,10 +230,9 @@ async function runCollector(cmd: Extract<WorkerCommand, { type: 'RUN_COLLECTOR'
|
|
|
231
230
|
const fetched = await executeTimeout(
|
|
232
231
|
(_signal: AbortSignal) =>
|
|
233
232
|
collector.collectTxs(
|
|
234
|
-
|
|
233
|
+
RequestTracker.create(parsedTxHashes, new Date(Date.now() + internalTimeoutMs)),
|
|
235
234
|
parsedProposal,
|
|
236
235
|
pinnedPeer,
|
|
237
|
-
internalTimeoutMs,
|
|
238
236
|
),
|
|
239
237
|
timeoutMs,
|
|
240
238
|
() => new Error(`Collector timed out after ${timeoutMs}ms`),
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ConfigMappingsType,
|
|
3
3
|
SecretValue,
|
|
4
|
+
bigintConfigHelper,
|
|
4
5
|
booleanConfigHelper,
|
|
5
6
|
getConfigFromMappings,
|
|
6
7
|
getDefaultConfig,
|
|
@@ -10,7 +11,6 @@ import {
|
|
|
10
11
|
secretStringConfigHelper,
|
|
11
12
|
} from '@aztec/foundation/config';
|
|
12
13
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
13
|
-
import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
|
|
14
14
|
import { FunctionSelector } from '@aztec/stdlib/abi/function-selector';
|
|
15
15
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
16
16
|
import {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
chainConfigMappings,
|
|
21
21
|
sharedSequencerConfigMappings,
|
|
22
22
|
} from '@aztec/stdlib/config';
|
|
23
|
+
import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store';
|
|
23
24
|
|
|
24
25
|
import {
|
|
25
26
|
type BatchTxRequesterConfig,
|
|
@@ -39,6 +40,18 @@ export interface P2PConfig
|
|
|
39
40
|
TxCollectionConfig,
|
|
40
41
|
TxFileStoreConfig,
|
|
41
42
|
Pick<SequencerConfig, 'blockDurationMs' | 'expectedBlockProposalsPerSlot' | 'maxTxsPerBlock'> {
|
|
43
|
+
/** Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set. */
|
|
44
|
+
validateMaxTxsPerBlock?: number;
|
|
45
|
+
|
|
46
|
+
/** Maximum transactions per checkpoint for validation. Used as fallback for maxTxsPerBlock when that is not set. */
|
|
47
|
+
validateMaxTxsPerCheckpoint?: number;
|
|
48
|
+
|
|
49
|
+
/** Maximum L2 gas per block for validation. When set, txs exceeding this limit are rejected. */
|
|
50
|
+
validateMaxL2BlockGas?: number;
|
|
51
|
+
|
|
52
|
+
/** Maximum DA gas per block for validation. When set, txs exceeding this limit are rejected. */
|
|
53
|
+
validateMaxDABlockGas?: number;
|
|
54
|
+
|
|
42
55
|
/** A flag dictating whether the P2P subsystem should be enabled. */
|
|
43
56
|
p2pEnabled: boolean;
|
|
44
57
|
|
|
@@ -57,6 +70,9 @@ export interface P2PConfig
|
|
|
57
70
|
/** The frequency in which to check for new peers. */
|
|
58
71
|
peerCheckIntervalMS: number;
|
|
59
72
|
|
|
73
|
+
/** How long to ban a peer after it fails MAX_DIAL_ATTEMPTS dials. */
|
|
74
|
+
peerFailedBanTimeMs: number;
|
|
75
|
+
|
|
60
76
|
/** Size of queue of L2 blocks to store. */
|
|
61
77
|
l2QueueSize: number;
|
|
62
78
|
|
|
@@ -190,11 +206,36 @@ export interface P2PConfig
|
|
|
190
206
|
|
|
191
207
|
/** Minimum age (ms) a transaction must have been in the pool before it's eligible for block building. */
|
|
192
208
|
minTxPoolAgeMs: number;
|
|
209
|
+
|
|
210
|
+
/** Minimum percentage fee increase required to replace an existing tx via RPC (0 = no bump). */
|
|
211
|
+
priceBumpPercentage: bigint;
|
|
193
212
|
}
|
|
194
213
|
|
|
195
214
|
export const DEFAULT_P2P_PORT = 40400;
|
|
196
215
|
|
|
197
216
|
export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
217
|
+
validateMaxTxsPerBlock: {
|
|
218
|
+
env: 'VALIDATOR_MAX_TX_PER_BLOCK',
|
|
219
|
+
description:
|
|
220
|
+
'Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set.',
|
|
221
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
222
|
+
},
|
|
223
|
+
validateMaxTxsPerCheckpoint: {
|
|
224
|
+
env: 'VALIDATOR_MAX_TX_PER_CHECKPOINT',
|
|
225
|
+
description:
|
|
226
|
+
'Maximum transactions per checkpoint for validation. Used as fallback for maxTxsPerBlock when that is not set.',
|
|
227
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
228
|
+
},
|
|
229
|
+
validateMaxL2BlockGas: {
|
|
230
|
+
env: 'VALIDATOR_MAX_L2_BLOCK_GAS',
|
|
231
|
+
description: 'Maximum L2 gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
232
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
233
|
+
},
|
|
234
|
+
validateMaxDABlockGas: {
|
|
235
|
+
env: 'VALIDATOR_MAX_DA_BLOCK_GAS',
|
|
236
|
+
description: 'Maximum DA gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
237
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
238
|
+
},
|
|
198
239
|
p2pEnabled: {
|
|
199
240
|
env: 'P2P_ENABLED',
|
|
200
241
|
description: 'A flag dictating whether the P2P subsystem should be enabled.',
|
|
@@ -225,6 +266,11 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
225
266
|
description: 'The frequency in which to check for new peers.',
|
|
226
267
|
...numberConfigHelper(30_000),
|
|
227
268
|
},
|
|
269
|
+
peerFailedBanTimeMs: {
|
|
270
|
+
env: 'P2P_PEER_FAILED_BAN_TIME_MS',
|
|
271
|
+
description: 'How long to ban a peer after it fails maximum dial attempts.',
|
|
272
|
+
...numberConfigHelper(5 * 60 * 1000),
|
|
273
|
+
},
|
|
228
274
|
l2QueueSize: {
|
|
229
275
|
env: 'P2P_L2_QUEUE_SIZE',
|
|
230
276
|
description: 'Size of queue of L2 blocks to store.',
|
|
@@ -397,9 +443,9 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
397
443
|
env: 'TX_PUBLIC_SETUP_ALLOWLIST',
|
|
398
444
|
parseEnv: (val: string) => parseAllowList(val),
|
|
399
445
|
description:
|
|
400
|
-
'Additional entries to extend the default setup allow list. Format: I:address:selector,C:classId:selector',
|
|
446
|
+
'Additional entries to extend the default setup allow list. Format: I:address:selector[:flags],C:classId:selector[:flags]. Flags: os (onlySelf), rn (rejectNullMsgSender), cl=N (calldataLength), joined with +.',
|
|
401
447
|
printDefault: () =>
|
|
402
|
-
'Default: AuthRegistry._set_authorized,
|
|
448
|
+
'Default: AuthRegistry._set_authorized, AuthRegistry.set_authorized, FeeJuice._increase_public_balance',
|
|
403
449
|
},
|
|
404
450
|
maxPendingTxCount: {
|
|
405
451
|
env: 'P2P_MAX_PENDING_TX_COUNT',
|
|
@@ -465,6 +511,12 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
465
511
|
description: 'Minimum age (ms) a transaction must have been in the pool before it is eligible for block building.',
|
|
466
512
|
...numberConfigHelper(2_000),
|
|
467
513
|
},
|
|
514
|
+
priceBumpPercentage: {
|
|
515
|
+
env: 'P2P_RPC_PRICE_BUMP_PERCENTAGE',
|
|
516
|
+
description:
|
|
517
|
+
'Minimum percentage fee increase required to replace an existing tx via RPC. Even at 0%, replacement still requires paying at least 1 unit more.',
|
|
518
|
+
...bigintConfigHelper(10n),
|
|
519
|
+
},
|
|
468
520
|
...sharedSequencerConfigMappings,
|
|
469
521
|
...p2pReqRespConfigMappings,
|
|
470
522
|
...batchTxRequesterConfigMappings,
|
|
@@ -522,12 +574,45 @@ export const bootnodeConfigMappings = pickConfigMappings(
|
|
|
522
574
|
bootnodeConfigKeys,
|
|
523
575
|
);
|
|
524
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Parses a `+`-separated flags string into validation properties for an allow list entry.
|
|
579
|
+
* Supported flags: `os` (onlySelf), `rn` (rejectNullMsgSender), `cl=N` (calldataLength).
|
|
580
|
+
*/
|
|
581
|
+
function parseFlags(
|
|
582
|
+
flags: string,
|
|
583
|
+
entry: string,
|
|
584
|
+
): { onlySelf?: boolean; rejectNullMsgSender?: boolean; calldataLength?: number } {
|
|
585
|
+
const result: { onlySelf?: boolean; rejectNullMsgSender?: boolean; calldataLength?: number } = {};
|
|
586
|
+
for (const flag of flags.split('+')) {
|
|
587
|
+
if (flag === 'os') {
|
|
588
|
+
result.onlySelf = true;
|
|
589
|
+
} else if (flag === 'rn') {
|
|
590
|
+
result.rejectNullMsgSender = true;
|
|
591
|
+
} else if (flag.startsWith('cl=')) {
|
|
592
|
+
const n = parseInt(flag.slice(3), 10);
|
|
593
|
+
if (isNaN(n) || n < 0) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
`Invalid allow list entry "${entry}": invalid calldataLength in flag "${flag}". Expected a non-negative integer.`,
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
result.calldataLength = n;
|
|
599
|
+
} else {
|
|
600
|
+
throw new Error(`Invalid allow list entry "${entry}": unknown flag "${flag}". Supported flags: os, rn, cl=N.`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
|
|
525
606
|
/**
|
|
526
607
|
* Parses a string to a list of allowed elements.
|
|
527
608
|
* Each entry is expected to be of one of the following formats:
|
|
528
609
|
* `I:${address}:${selector}` — instance (contract address) with function selector
|
|
529
610
|
* `C:${classId}:${selector}` — class with function selector
|
|
530
611
|
*
|
|
612
|
+
* An optional flags segment can be appended after the selector:
|
|
613
|
+
* `I:${address}:${selector}:${flags}` or `C:${classId}:${selector}:${flags}`
|
|
614
|
+
* where flags is a `+`-separated list of: `os` (onlySelf), `rn` (rejectNullMsgSender), `cl=N` (calldataLength).
|
|
615
|
+
*
|
|
531
616
|
* @param value The string to parse
|
|
532
617
|
* @returns A list of allowed elements
|
|
533
618
|
*/
|
|
@@ -543,7 +628,7 @@ export function parseAllowList(value: string): AllowedElement[] {
|
|
|
543
628
|
if (!trimmed) {
|
|
544
629
|
continue;
|
|
545
630
|
}
|
|
546
|
-
const [typeString, identifierString, selectorString] = trimmed.split(':');
|
|
631
|
+
const [typeString, identifierString, selectorString, flagsString] = trimmed.split(':');
|
|
547
632
|
|
|
548
633
|
if (!selectorString) {
|
|
549
634
|
throw new Error(
|
|
@@ -552,16 +637,19 @@ export function parseAllowList(value: string): AllowedElement[] {
|
|
|
552
637
|
}
|
|
553
638
|
|
|
554
639
|
const selector = FunctionSelector.fromString(selectorString);
|
|
640
|
+
const flags = flagsString ? parseFlags(flagsString, trimmed) : {};
|
|
555
641
|
|
|
556
642
|
if (typeString === 'I') {
|
|
557
643
|
entries.push({
|
|
558
644
|
address: AztecAddress.fromString(identifierString),
|
|
559
645
|
selector,
|
|
646
|
+
...flags,
|
|
560
647
|
});
|
|
561
648
|
} else if (typeString === 'C') {
|
|
562
649
|
entries.push({
|
|
563
650
|
classId: Fr.fromHexString(identifierString),
|
|
564
651
|
selector,
|
|
652
|
+
...flags,
|
|
565
653
|
});
|
|
566
654
|
} else {
|
|
567
655
|
throw new Error(
|
|
@@ -26,10 +26,10 @@ export type TryAddResult = {
|
|
|
26
26
|
count: number;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export const MAX_CHECKPOINT_PROPOSALS_PER_SLOT =
|
|
30
|
-
export const MAX_BLOCK_PROPOSALS_PER_POSITION =
|
|
29
|
+
export const MAX_CHECKPOINT_PROPOSALS_PER_SLOT = 2;
|
|
30
|
+
export const MAX_BLOCK_PROPOSALS_PER_POSITION = 2;
|
|
31
31
|
/** Maximum attestations a single signer can make per slot before being rejected. */
|
|
32
|
-
export const MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER =
|
|
32
|
+
export const MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER = 2;
|
|
33
33
|
|
|
34
34
|
/** Public API interface for attestation pools. Used for typing mocks and test implementations. */
|
|
35
35
|
export type AttestationPoolApi = Pick<
|
|
@@ -446,12 +446,12 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
|
|
|
446
446
|
const result2 = await ap.tryAddBlockProposal(proposal2);
|
|
447
447
|
expect(result2.count).toBe(2);
|
|
448
448
|
|
|
449
|
-
//
|
|
449
|
+
// Third proposal for same position should be rejected (cap is 2)
|
|
450
450
|
const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
|
|
451
451
|
const result3 = await ap.tryAddBlockProposal(proposal3);
|
|
452
452
|
|
|
453
|
-
expect(result3.added).toBe(
|
|
454
|
-
expect(result3.count).toBe(
|
|
453
|
+
expect(result3.added).toBe(false);
|
|
454
|
+
expect(result3.count).toBe(2);
|
|
455
455
|
});
|
|
456
456
|
|
|
457
457
|
it('should return added=false when exceeding capacity', async () => {
|
|
@@ -666,12 +666,12 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
|
|
|
666
666
|
const result2 = await ap.tryAddCheckpointProposal(proposal2);
|
|
667
667
|
expect(result2.count).toBe(2);
|
|
668
668
|
|
|
669
|
-
//
|
|
669
|
+
// Third proposal for same slot should be rejected (cap is 2)
|
|
670
670
|
const proposal3 = await mockCheckpointProposalCoreForPool(signers[2], slotNumber);
|
|
671
671
|
const result3 = await ap.tryAddCheckpointProposal(proposal3);
|
|
672
672
|
|
|
673
|
-
expect(result3.added).toBe(
|
|
674
|
-
expect(result3.count).toBe(
|
|
673
|
+
expect(result3.added).toBe(false);
|
|
674
|
+
expect(result3.count).toBe(2);
|
|
675
675
|
});
|
|
676
676
|
|
|
677
677
|
it('should not count attestations as proposals for duplicate detection', async () => {
|
|
@@ -32,10 +32,11 @@ export class FeePayerBalanceEvictionRule implements EvictionRule {
|
|
|
32
32
|
|
|
33
33
|
if (context.event === EvictionEvent.BLOCK_MINED) {
|
|
34
34
|
const blockNumber = context.block.getBlockNumber();
|
|
35
|
+
const blockHash = await context.block.hash();
|
|
35
36
|
// Ensure world state is synced to this block before accessing the snapshot.
|
|
36
37
|
// This handles the race where a block is added to the archiver
|
|
37
38
|
// but the world state hasn't synced it yet.
|
|
38
|
-
await this.worldState.syncImmediate(blockNumber);
|
|
39
|
+
await this.worldState.syncImmediate(blockNumber, blockHash);
|
|
39
40
|
return await this.evictForFeePayers(context.feePayers, this.worldState.getSnapshot(blockNumber), txPool);
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { minBigint } from '@aztec/foundation/bigint';
|
|
1
2
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
2
3
|
import type { Tx } from '@aztec/stdlib/tx';
|
|
3
4
|
|
|
@@ -11,10 +12,9 @@ export function getPendingTxPriority(tx: Tx): string {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* Returns the priority of a tx.
|
|
15
|
+
* Returns the priority of a tx based on the L2 priority fee only, capped by the max fees per gas.
|
|
15
16
|
*/
|
|
16
17
|
export function getTxPriorityFee(tx: Tx): bigint {
|
|
17
|
-
const priorityFees = tx.getGasSettings()
|
|
18
|
-
|
|
19
|
-
return totalFees;
|
|
18
|
+
const { maxPriorityFeesPerGas: priorityFees, maxFeesPerGas } = tx.getGasSettings();
|
|
19
|
+
return minBigint(maxFeesPerGas.feePerL2Gas, priorityFees.feePerL2Gas);
|
|
20
20
|
}
|
|
@@ -204,7 +204,9 @@ export function describeTxPool(getTxPool: () => TxPool) {
|
|
|
204
204
|
|
|
205
205
|
it('returns pending tx hashes sorted by priority', async () => {
|
|
206
206
|
const withPriorityFee = (tx: Tx, fee: number) => {
|
|
207
|
-
unfreeze(tx.data.constants.txContext.gasSettings)
|
|
207
|
+
const gs = unfreeze(tx.data.constants.txContext.gasSettings);
|
|
208
|
+
gs.maxPriorityFeesPerGas = new GasFees(fee, fee);
|
|
209
|
+
gs.maxFeesPerGas = new GasFees(fee, fee);
|
|
208
210
|
return tx;
|
|
209
211
|
};
|
|
210
212
|
|
|
@@ -158,7 +158,7 @@ Checked before adding a transaction to the pending pool:
|
|
|
158
158
|
|
|
159
159
|
| Rule | Purpose |
|
|
160
160
|
|------|---------|
|
|
161
|
-
| `NullifierConflictRule` | Handles transactions with conflicting nullifiers. Higher priority tx wins. |
|
|
161
|
+
| `NullifierConflictRule` | Handles transactions with conflicting nullifiers. Higher priority tx wins. For RPC submissions, a configurable price bump percentage is required. |
|
|
162
162
|
| `FeePayerBalancePreAddRule` | Ensures fee payer has sufficient balance for all their pending txs. |
|
|
163
163
|
| `LowPriorityPreAddRule` | Rejects txs when pool is full and new tx has lowest priority. |
|
|
164
164
|
|
|
@@ -233,6 +233,14 @@ await pool.updateConfig({
|
|
|
233
233
|
});
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
+
### Price Bump (RPC Transaction Replacement)
|
|
237
|
+
|
|
238
|
+
When a transaction is submitted via RPC and clashes on nullifiers with an existing pool transaction, the incoming tx must pay at least `priceBumpPercentage`% more in priority fee (i.e. `>= existingFee + existingFee * bump / 100`) to replace it. This prevents spam via small fee increments. The same bump applies when the pool is full and the incoming tx needs to evict the lowest-priority tx.
|
|
239
|
+
|
|
240
|
+
- **Env var**: `P2P_RPC_PRICE_BUMP_PERCENTAGE` (default: 10)
|
|
241
|
+
- **Scope**: RPC submissions only. P2P gossip uses `comparePriority` (fee + hash tiebreaker) with no bump.
|
|
242
|
+
- Even with a 0% bump, a replacement tx must pay at least 1 unit more than the existing fee.
|
|
243
|
+
|
|
236
244
|
## Return Values
|
|
237
245
|
|
|
238
246
|
### AddTxsResult
|
|
@@ -29,7 +29,8 @@ export class FeePayerBalanceEvictionRule implements EvictionRule {
|
|
|
29
29
|
|
|
30
30
|
if (context.event === EvictionEvent.BLOCK_MINED) {
|
|
31
31
|
const blockNumber = context.block.getBlockNumber();
|
|
32
|
-
await
|
|
32
|
+
const blockHash = await context.block.hash();
|
|
33
|
+
await this.worldState.syncImmediate(blockNumber, blockHash);
|
|
33
34
|
return await this.evictForFeePayers(context.feePayers, this.worldState.getSnapshot(blockNumber), pool);
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -100,7 +100,15 @@ export type TxPoolRejectionError =
|
|
|
100
100
|
availableBalance: bigint;
|
|
101
101
|
feeLimit: bigint;
|
|
102
102
|
}
|
|
103
|
-
| {
|
|
103
|
+
| {
|
|
104
|
+
code: typeof TxPoolRejectionCode.NULLIFIER_CONFLICT;
|
|
105
|
+
message: string;
|
|
106
|
+
conflictingTxHash: string;
|
|
107
|
+
/** Minimum fee needed to replace the conflicting tx (only set when price bump applies). */
|
|
108
|
+
minimumPriceBumpFee?: bigint;
|
|
109
|
+
/** Incoming tx's priority fee. */
|
|
110
|
+
txPriorityFee?: bigint;
|
|
111
|
+
}
|
|
104
112
|
| { code: typeof TxPoolRejectionCode.INTERNAL_ERROR; message: string };
|
|
105
113
|
|
|
106
114
|
/**
|
|
@@ -121,6 +129,8 @@ export interface PreAddResult {
|
|
|
121
129
|
export interface PreAddContext {
|
|
122
130
|
/** If true, compare priority fee only (no tx hash tiebreaker). Used for RPC submissions. */
|
|
123
131
|
feeComparisonOnly?: boolean;
|
|
132
|
+
/** Percentage-based price bump required for tx replacement. Only set for RPC submissions. */
|
|
133
|
+
priceBumpPercentage?: bigint;
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
2
|
|
|
3
|
-
import { type TxMetaData, comparePriority } from '../tx_metadata.js';
|
|
3
|
+
import { type TxMetaData, comparePriority, getMinimumPriceBumpFee } from '../tx_metadata.js';
|
|
4
4
|
import {
|
|
5
5
|
type EvictionConfig,
|
|
6
6
|
type PreAddContext,
|
|
@@ -48,10 +48,14 @@ export class LowPriorityPreAddRule implements PreAddRule {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// Compare incoming tx against lowest priority tx.
|
|
51
|
-
// feeOnly mode (RPC): use strict fee comparison only — avoids churn from hash ordering
|
|
52
|
-
//
|
|
51
|
+
// feeOnly mode (RPC): use strict fee comparison only — avoids churn from hash ordering.
|
|
52
|
+
// When price bump is also set, require the bumped fee threshold.
|
|
53
|
+
// Default (gossip): use full comparePriority (fee + tx hash tiebreaker) for determinism.
|
|
53
54
|
const isHigherPriority = context?.feeComparisonOnly
|
|
54
|
-
?
|
|
55
|
+
? context.priceBumpPercentage !== undefined
|
|
56
|
+
? incomingMeta.priorityFee >=
|
|
57
|
+
getMinimumPriceBumpFee(lowestPriorityMeta.priorityFee, context.priceBumpPercentage)
|
|
58
|
+
: incomingMeta.priorityFee > lowestPriorityMeta.priorityFee
|
|
55
59
|
: comparePriority(incomingMeta, lowestPriorityMeta) > 0;
|
|
56
60
|
|
|
57
61
|
if (isHigherPriority) {
|
|
@@ -66,6 +70,11 @@ export class LowPriorityPreAddRule implements PreAddRule {
|
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
// Incoming tx has equal or lower priority - ignore it (it would be evicted anyway)
|
|
73
|
+
const minimumFee =
|
|
74
|
+
context?.feeComparisonOnly && context.priceBumpPercentage !== undefined
|
|
75
|
+
? getMinimumPriceBumpFee(lowestPriorityMeta.priorityFee, context.priceBumpPercentage)
|
|
76
|
+
: lowestPriorityMeta.priorityFee + 1n;
|
|
77
|
+
|
|
69
78
|
this.log.debug(
|
|
70
79
|
`Pool at capacity (${currentCount}/${this.maxPoolSize}), ignoring ${incomingMeta.txHash} ` +
|
|
71
80
|
`(priority ${incomingMeta.priorityFee}) - lower than existing minimum (priority ${lowestPriorityMeta.priorityFee})`,
|
|
@@ -75,8 +84,8 @@ export class LowPriorityPreAddRule implements PreAddRule {
|
|
|
75
84
|
txHashesToEvict: [],
|
|
76
85
|
reason: {
|
|
77
86
|
code: TxPoolRejectionCode.LOW_PRIORITY_FEE,
|
|
78
|
-
message: `Tx does not meet minimum priority fee. Required: ${
|
|
79
|
-
minimumPriorityFee:
|
|
87
|
+
message: `Tx does not meet minimum priority fee. Required: ${minimumFee}, got: ${incomingMeta.priorityFee}`,
|
|
88
|
+
minimumPriorityFee: minimumFee,
|
|
80
89
|
txPriorityFee: incomingMeta.priorityFee,
|
|
81
90
|
},
|
|
82
91
|
});
|
|
@@ -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) {
|
|
@@ -46,6 +46,8 @@ export type TxPoolV2Config = {
|
|
|
46
46
|
evictedTxCacheSize: number;
|
|
47
47
|
/** The probability (0-1) that a transaction is discarded. 0 disables dropping. For testing purposes only. */
|
|
48
48
|
dropTransactionsProbability: number;
|
|
49
|
+
/** Minimum percentage fee increase required to replace an existing tx via RPC (0 = no bump). */
|
|
50
|
+
priceBumpPercentage: bigint;
|
|
49
51
|
};
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -57,6 +59,7 @@ export const DEFAULT_TX_POOL_V2_CONFIG: TxPoolV2Config = {
|
|
|
57
59
|
minTxPoolAgeMs: 2_000,
|
|
58
60
|
evictedTxCacheSize: 10_000,
|
|
59
61
|
dropTransactionsProbability: 0,
|
|
62
|
+
priceBumpPercentage: 10n,
|
|
60
63
|
};
|
|
61
64
|
|
|
62
65
|
/**
|
|
@@ -69,6 +72,8 @@ export type TxPoolV2Dependencies = {
|
|
|
69
72
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
70
73
|
/** Factory that creates a validator for re-validating pool transactions using metadata */
|
|
71
74
|
createTxValidator: () => Promise<TxValidator<TxMetaData>>;
|
|
75
|
+
/** Checks whether a tx's setup-phase calls are on the allow list. Precomputed at receipt time. */
|
|
76
|
+
checkAllowedSetupCalls: (tx: Tx) => Promise<boolean>;
|
|
72
77
|
};
|
|
73
78
|
|
|
74
79
|
/**
|
|
@@ -155,10 +160,10 @@ export interface TxPoolV2 extends TypedEventEmitter<TxPoolV2Events> {
|
|
|
155
160
|
handleMinedBlock(block: L2Block): Promise<void>;
|
|
156
161
|
|
|
157
162
|
/**
|
|
158
|
-
* Prepares the pool for a new slot
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
163
|
+
* Prepares the pool for a new slot by unprotecting transactions from earlier
|
|
164
|
+
* slots and re-validating them before returning to pending state.
|
|
165
|
+
* @param slotNumber - The pipeline slot we are building for (i.e. the slot
|
|
166
|
+
* the resulting blocks will target on L1).
|
|
162
167
|
*/
|
|
163
168
|
prepareForSlot(slotNumber: SlotNumber): Promise<void>;
|
|
164
169
|
|
|
@@ -67,6 +67,9 @@ export type TxMetaData = {
|
|
|
67
67
|
/** Timestamp by which the transaction must be included (for expiration checks) */
|
|
68
68
|
readonly expirationTimestamp: bigint;
|
|
69
69
|
|
|
70
|
+
/** Whether the tx's setup-phase calls pass the allow list check. Computed at receipt time. */
|
|
71
|
+
readonly allowedSetupCalls: boolean;
|
|
72
|
+
|
|
70
73
|
/** Validator-compatible data, providing the same access patterns as Tx.data */
|
|
71
74
|
readonly data: TxMetaValidationData;
|
|
72
75
|
|
|
@@ -84,8 +87,12 @@ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
|
|
|
84
87
|
* Builds TxMetaData from a full Tx object.
|
|
85
88
|
* Extracts all relevant fields for efficient in-memory storage and querying.
|
|
86
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.
|
|
87
94
|
*/
|
|
88
|
-
export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
95
|
+
export async function buildTxMetaData(tx: Tx, allowedSetupCalls: boolean = true): Promise<TxMetaData> {
|
|
89
96
|
const txHashObj = tx.getTxHash();
|
|
90
97
|
const txHash = txHashObj.toString();
|
|
91
98
|
const txHashBigInt = txHashObj.toBigInt();
|
|
@@ -112,6 +119,7 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
112
119
|
feeLimit,
|
|
113
120
|
nullifiers,
|
|
114
121
|
expirationTimestamp,
|
|
122
|
+
allowedSetupCalls,
|
|
115
123
|
receivedAt: 0,
|
|
116
124
|
estimatedSizeBytes,
|
|
117
125
|
data: {
|
|
@@ -158,13 +166,13 @@ export function txHashFromBigInt(value: bigint): string {
|
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
/** Minimal fields required for priority comparison. */
|
|
161
|
-
type PriorityComparable = Pick<TxMetaData, 'txHashBigInt' | 'priorityFee'>;
|
|
169
|
+
export type PriorityComparable = Pick<TxMetaData, 'txHash' | 'txHashBigInt' | 'priorityFee'>;
|
|
162
170
|
|
|
163
171
|
/**
|
|
164
172
|
* Compares two priority fees in ascending order.
|
|
165
173
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
166
174
|
*/
|
|
167
|
-
export function compareFee(a: bigint, b: bigint):
|
|
175
|
+
export function compareFee(a: bigint, b: bigint): -1 | 0 | 1 {
|
|
168
176
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
169
177
|
}
|
|
170
178
|
|
|
@@ -173,7 +181,7 @@ export function compareFee(a: bigint, b: bigint): number {
|
|
|
173
181
|
* Uses field element comparison for deterministic ordering.
|
|
174
182
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
175
183
|
*/
|
|
176
|
-
export function compareTxHash(a: bigint, b: bigint):
|
|
184
|
+
export function compareTxHash(a: bigint, b: bigint): -1 | 0 | 1 {
|
|
177
185
|
return Fr.cmpAsBigInt(a, b);
|
|
178
186
|
}
|
|
179
187
|
|
|
@@ -182,7 +190,7 @@ export function compareTxHash(a: bigint, b: bigint): number {
|
|
|
182
190
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
183
191
|
* Use with sort() for ascending order, or negate/reverse for descending.
|
|
184
192
|
*/
|
|
185
|
-
export function comparePriority(a: PriorityComparable, b: PriorityComparable):
|
|
193
|
+
export function comparePriority(a: PriorityComparable, b: PriorityComparable): -1 | 0 | 1 {
|
|
186
194
|
const feeComparison = compareFee(a.priorityFee, b.priorityFee);
|
|
187
195
|
if (feeComparison !== 0) {
|
|
188
196
|
return feeComparison;
|
|
@@ -190,21 +198,38 @@ export function comparePriority(a: PriorityComparable, b: PriorityComparable): n
|
|
|
190
198
|
return compareTxHash(a.txHashBigInt, b.txHashBigInt);
|
|
191
199
|
}
|
|
192
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;
|
|
211
|
+
}
|
|
212
|
+
|
|
193
213
|
/**
|
|
194
214
|
* Checks for nullifier conflicts between an incoming transaction and existing pool state.
|
|
195
215
|
*
|
|
196
216
|
* When the incoming tx shares nullifiers with existing pending txs:
|
|
197
|
-
* - If the incoming tx
|
|
198
|
-
* -
|
|
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`.
|
|
199
222
|
*
|
|
200
223
|
* @param incomingMeta - Metadata for the incoming transaction
|
|
201
224
|
* @param getTxHashByNullifier - Accessor to find which tx uses a nullifier
|
|
202
225
|
* @param getMetadata - Accessor to get metadata for a tx hash
|
|
226
|
+
* @param priceBumpPercentage - Optional percentage bump required for fee-based replacement
|
|
203
227
|
*/
|
|
204
228
|
export function checkNullifierConflict(
|
|
205
229
|
incomingMeta: TxMetaData,
|
|
206
230
|
getTxHashByNullifier: (nullifier: string) => string | undefined,
|
|
207
231
|
getMetadata: (txHash: string) => TxMetaData | undefined,
|
|
232
|
+
priceBumpPercentage?: bigint,
|
|
208
233
|
): PreAddResult {
|
|
209
234
|
const txHashesToEvict: string[] = [];
|
|
210
235
|
|
|
@@ -225,19 +250,32 @@ export function checkNullifierConflict(
|
|
|
225
250
|
continue;
|
|
226
251
|
}
|
|
227
252
|
|
|
228
|
-
//
|
|
229
|
-
// Otherwise,
|
|
230
|
-
|
|
231
|
-
|
|
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) {
|
|
232
261
|
txHashesToEvict.push(conflictingHashStr);
|
|
233
262
|
} else {
|
|
263
|
+
const minimumFee =
|
|
264
|
+
priceBumpPercentage !== undefined
|
|
265
|
+
? getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
266
|
+
: undefined;
|
|
234
267
|
return {
|
|
235
268
|
shouldIgnore: true,
|
|
236
269
|
txHashesToEvict: [],
|
|
237
270
|
reason: {
|
|
238
271
|
code: TxPoolRejectionCode.NULLIFIER_CONFLICT,
|
|
239
|
-
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}`,
|
|
240
276
|
conflictingTxHash: conflictingHashStr,
|
|
277
|
+
minimumPriceBumpFee: minimumFee,
|
|
278
|
+
txPriorityFee: minimumFee !== undefined ? incomingMeta.priorityFee : undefined,
|
|
241
279
|
},
|
|
242
280
|
};
|
|
243
281
|
}
|
|
@@ -274,6 +312,7 @@ export function stubTxMetaData(
|
|
|
274
312
|
nullifiers?: string[];
|
|
275
313
|
expirationTimestamp?: bigint;
|
|
276
314
|
anchorBlockHeaderHash?: string;
|
|
315
|
+
allowedSetupCalls?: boolean;
|
|
277
316
|
} = {},
|
|
278
317
|
): TxMetaData {
|
|
279
318
|
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
@@ -290,6 +329,7 @@ export function stubTxMetaData(
|
|
|
290
329
|
feeLimit: overrides.feeLimit ?? 100n,
|
|
291
330
|
nullifiers: overrides.nullifiers ?? [`0x${normalizedTxHash.slice(2)}null1`],
|
|
292
331
|
expirationTimestamp,
|
|
332
|
+
allowedSetupCalls: overrides.allowedSetupCalls ?? true,
|
|
293
333
|
receivedAt: 0,
|
|
294
334
|
estimatedSizeBytes: 0,
|
|
295
335
|
data: stubTxMetaValidationData({ expirationTimestamp }),
|