@aztec/p2p 0.0.1-commit.f650c0a5c → 0.0.1-commit.f7ea82942
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/client/factory.d.ts +3 -2
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +16 -15
- 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 +9 -2
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +4 -1
- package/dest/config.d.ts +103 -99
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -6
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +4 -2
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +5 -3
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +4 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +8 -5
- 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 +2 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +5 -2
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +17 -9
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +4 -2
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +2 -2
- package/dest/msg_validators/clock_tolerance.d.ts +12 -1
- package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.js +50 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +16 -8
- package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
- package/dest/msg_validators/tx_validator/factory.d.ts +2 -2
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +3 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +36 -4
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +46 -31
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/dummy_service.d.ts +2 -1
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +1 -0
- package/dest/services/gossipsub/topic_score_params.d.ts +13 -2
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +21 -4
- package/dest/services/libp2p/instrumentation.d.ts +3 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +14 -0
- package/dest/services/libp2p/libp2p_service.d.ts +5 -3
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +33 -20
- 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 +15 -2
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/interface.d.ts +14 -1
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +10 -0
- package/dest/services/reqresp/reqresp.d.ts +4 -2
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +10 -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/make-test-p2p-clients.js +4 -1
- package/dest/test-helpers/mock-pubsub.d.ts +11 -3
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +36 -11
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +5 -1
- package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +7 -2
- package/package.json +14 -14
- package/src/client/factory.ts +23 -18
- package/src/client/p2p_client.ts +11 -3
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +2 -0
- package/src/config.ts +19 -7
- package/src/mem_pools/attestation_pool/attestation_pool.ts +5 -3
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/interfaces.ts +3 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +13 -7
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +2 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +18 -7
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +4 -1
- package/src/msg_validators/clock_tolerance.ts +68 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +4 -1
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +4 -1
- package/src/msg_validators/proposal_validator/proposal_validator.ts +13 -7
- package/src/msg_validators/tx_validator/README.md +11 -3
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/factory.ts +3 -1
- package/src/msg_validators/tx_validator/gas_validator.ts +64 -31
- package/src/services/data_store.ts +5 -13
- package/src/services/dummy_service.ts +1 -0
- package/src/services/gossipsub/topic_score_params.ts +36 -4
- package/src/services/libp2p/instrumentation.ts +14 -0
- package/src/services/libp2p/libp2p_service.ts +31 -15
- package/src/services/peer-manager/peer_manager.ts +17 -2
- package/src/services/reqresp/config.ts +2 -2
- package/src/services/reqresp/interface.ts +21 -0
- package/src/services/reqresp/reqresp.ts +17 -0
- package/src/test-helpers/make-test-p2p-clients.ts +2 -0
- package/src/test-helpers/mock-pubsub.ts +34 -5
- package/src/test-helpers/reqresp-nodes.ts +4 -0
- package/src/testbench/p2p_client_testbench_worker.ts +3 -0
|
@@ -6,6 +6,7 @@ import { DateProvider, Timer, executeTimeout } from '@aztec/foundation/timer';
|
|
|
6
6
|
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
7
7
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
8
8
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
9
|
+
import { GasFees } from '@aztec/stdlib/gas';
|
|
9
10
|
import type { ClientProtocolCircuitVerifier } from '@aztec/stdlib/interfaces/server';
|
|
10
11
|
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
11
12
|
import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
@@ -119,6 +120,7 @@ async function startClient(config: P2PConfig, clientIndex: number) {
|
|
|
119
120
|
proofVerifier as ClientProtocolCircuitVerifier,
|
|
120
121
|
worldState,
|
|
121
122
|
epochCache,
|
|
123
|
+
{ getCurrentMinFees: () => Promise.resolve(GasFees.empty()) },
|
|
122
124
|
'proposal-tx-collector-bench-worker',
|
|
123
125
|
new DateProvider(),
|
|
124
126
|
telemetry as TelemetryClient,
|
package/src/config.ts
CHANGED
|
@@ -39,7 +39,14 @@ export interface P2PConfig
|
|
|
39
39
|
ChainConfig,
|
|
40
40
|
TxCollectionConfig,
|
|
41
41
|
TxFileStoreConfig,
|
|
42
|
-
Pick<
|
|
42
|
+
Pick<
|
|
43
|
+
SequencerConfig,
|
|
44
|
+
| 'blockDurationMs'
|
|
45
|
+
| 'expectedBlockProposalsPerSlot'
|
|
46
|
+
| 'l1PublishingTime'
|
|
47
|
+
| 'maxTxsPerBlock'
|
|
48
|
+
| 'attestationPropagationTime'
|
|
49
|
+
> {
|
|
43
50
|
/** Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set. */
|
|
44
51
|
validateMaxTxsPerBlock?: number;
|
|
45
52
|
|
|
@@ -218,23 +225,23 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
218
225
|
env: 'VALIDATOR_MAX_TX_PER_BLOCK',
|
|
219
226
|
description:
|
|
220
227
|
'Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set.',
|
|
221
|
-
parseEnv: (val: string) =>
|
|
228
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
222
229
|
},
|
|
223
230
|
validateMaxTxsPerCheckpoint: {
|
|
224
231
|
env: 'VALIDATOR_MAX_TX_PER_CHECKPOINT',
|
|
225
232
|
description:
|
|
226
233
|
'Maximum transactions per checkpoint for validation. Used as fallback for maxTxsPerBlock when that is not set.',
|
|
227
|
-
parseEnv: (val: string) =>
|
|
234
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
228
235
|
},
|
|
229
236
|
validateMaxL2BlockGas: {
|
|
230
237
|
env: 'VALIDATOR_MAX_L2_BLOCK_GAS',
|
|
231
238
|
description: 'Maximum L2 gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
232
|
-
parseEnv: (val: string) =>
|
|
239
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
233
240
|
},
|
|
234
241
|
validateMaxDABlockGas: {
|
|
235
242
|
env: 'VALIDATOR_MAX_DA_BLOCK_GAS',
|
|
236
243
|
description: 'Maximum DA gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
237
|
-
parseEnv: (val: string) =>
|
|
244
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
238
245
|
},
|
|
239
246
|
p2pEnabled: {
|
|
240
247
|
env: 'P2P_ENABLED',
|
|
@@ -364,7 +371,7 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
364
371
|
gossipsubMcacheLength: {
|
|
365
372
|
env: 'P2P_GOSSIPSUB_MCACHE_LENGTH',
|
|
366
373
|
description: 'The number of gossipsub interval message cache windows to keep.',
|
|
367
|
-
...numberConfigHelper(
|
|
374
|
+
...numberConfigHelper(12),
|
|
368
375
|
},
|
|
369
376
|
gossipsubMcacheGossip: {
|
|
370
377
|
env: 'P2P_GOSSIPSUB_MCACHE_GOSSIP',
|
|
@@ -436,7 +443,7 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
436
443
|
},
|
|
437
444
|
p2pStoreMapSizeKb: {
|
|
438
445
|
env: 'P2P_STORE_MAP_SIZE_KB',
|
|
439
|
-
parseEnv: (val: string
|
|
446
|
+
parseEnv: (val: string) => +val,
|
|
440
447
|
description: 'The maximum possible size of the P2P DB in KB. Overwrites the general dataStoreMapSizeKb.',
|
|
441
448
|
},
|
|
442
449
|
txPublicSetupAllowListExtend: {
|
|
@@ -495,6 +502,11 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
495
502
|
description: 'Alters the format of p2p messages to include things like broadcast timestamp FOR TESTING ONLY',
|
|
496
503
|
...booleanConfigHelper(false),
|
|
497
504
|
},
|
|
505
|
+
l1PublishingTime: {
|
|
506
|
+
env: 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT',
|
|
507
|
+
description: 'How much time (in seconds) we allow in the slot for publishing the L1 tx (defaults to 1 L1 slot).',
|
|
508
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
509
|
+
},
|
|
498
510
|
fishermanMode: {
|
|
499
511
|
env: 'FISHERMAN_MODE',
|
|
500
512
|
description:
|
|
@@ -154,14 +154,16 @@ export class AttestationPool {
|
|
|
154
154
|
/** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
|
|
155
155
|
private static readonly MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
|
|
156
156
|
|
|
157
|
-
/** Creates a position key for block proposals:
|
|
157
|
+
/** Creates a position key for block proposals: slot * 1024 + indexWithinCheckpoint.
|
|
158
|
+
* Uses multiplication instead of bit-shift to avoid 32-bit signed integer overflow
|
|
159
|
+
* (bit-shift overflows after slot ~2^21, roughly 278 days of uptime). */
|
|
158
160
|
private getBlockPositionKey(slot: number, indexWithinCheckpoint: number): number {
|
|
159
161
|
if (indexWithinCheckpoint > AttestationPool.MAX_INDEX) {
|
|
160
162
|
throw new Error(
|
|
161
163
|
`Value for indexWithinCheckpoint ${indexWithinCheckpoint} exceeds maximum ${AttestationPool.MAX_INDEX}`,
|
|
162
164
|
);
|
|
163
165
|
}
|
|
164
|
-
return
|
|
166
|
+
return slot * (1 << AttestationPool.INDEX_BITS) + indexWithinCheckpoint;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/**
|
|
@@ -454,7 +456,7 @@ export class AttestationPool {
|
|
|
454
456
|
|
|
455
457
|
// Delete block proposals for slots < oldestSlot, using blockProposalsForSlotAndIndex as index
|
|
456
458
|
// Key format: (slot << INDEX_BITS) | indexWithinCheckpoint
|
|
457
|
-
const blockPositionEndKey = oldestSlot << AttestationPool.INDEX_BITS;
|
|
459
|
+
const blockPositionEndKey = oldestSlot * (1 << AttestationPool.INDEX_BITS);
|
|
458
460
|
for await (const positionKey of this.blockProposalsForSlotAndIndex.keysAsync({ end: blockPositionEndKey })) {
|
|
459
461
|
const proposalIds = await toArray(this.blockProposalsForSlotAndIndex.getValuesAsync(positionKey));
|
|
460
462
|
for (const proposalId of proposalIds) {
|
|
@@ -21,6 +21,7 @@ export { FeePayerBalancePreAddRule } from './fee_payer_balance_pre_add_rule.js';
|
|
|
21
21
|
export { LowPriorityPreAddRule } from './low_priority_pre_add_rule.js';
|
|
22
22
|
|
|
23
23
|
// Post-event eviction rules
|
|
24
|
+
export { InsufficientFeePerGasEvictionRule } from './insufficient_fee_per_gas_eviction_rule.js';
|
|
24
25
|
export { InvalidTxsAfterMiningRule } from './invalid_txs_after_mining_rule.js';
|
|
25
26
|
export { InvalidTxsAfterReorgRule } from './invalid_txs_after_reorg_rule.js';
|
|
26
27
|
export { FeePayerBalanceEvictionRule } from './fee_payer_balance_eviction_rule.js';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import type { BlockMinFeesProvider } from '@aztec/stdlib/gas';
|
|
3
|
+
|
|
4
|
+
import type { EvictionContext, EvictionResult, EvictionRule, PoolOperations } from './interfaces.js';
|
|
5
|
+
import { EvictionEvent } from './interfaces.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Eviction rule that removes transactions whose maxFeesPerGas no longer meets
|
|
9
|
+
* the projected minimum gas fees after a new block is mined.
|
|
10
|
+
* Uses the BlockMinFeesProvider (forward-looking) to get the projected minimum fees.
|
|
11
|
+
* Only triggers on BLOCK_MINED events.
|
|
12
|
+
*/
|
|
13
|
+
export class InsufficientFeePerGasEvictionRule implements EvictionRule {
|
|
14
|
+
public readonly name = 'InsufficientFeePerGas';
|
|
15
|
+
|
|
16
|
+
private log = createLogger('p2p:tx_pool_v2:insufficient_fee_per_gas_eviction_rule');
|
|
17
|
+
|
|
18
|
+
constructor(private blockMinFeesProvider: BlockMinFeesProvider) {}
|
|
19
|
+
|
|
20
|
+
async evict(context: EvictionContext, pool: PoolOperations): Promise<EvictionResult> {
|
|
21
|
+
if (context.event !== EvictionEvent.BLOCK_MINED) {
|
|
22
|
+
return {
|
|
23
|
+
reason: 'insufficient_fee_per_gas',
|
|
24
|
+
success: true,
|
|
25
|
+
txsEvicted: [],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const gasFees = await this.blockMinFeesProvider.getCurrentMinFees();
|
|
31
|
+
const txsToEvict: string[] = [];
|
|
32
|
+
const pendingTxs = pool.getPendingTxs();
|
|
33
|
+
|
|
34
|
+
for (const meta of pendingTxs) {
|
|
35
|
+
const maxFeesPerGas = meta.data.constants.txContext.gasSettings.maxFeesPerGas;
|
|
36
|
+
if (maxFeesPerGas.feePerDaGas < gasFees.feePerDaGas || maxFeesPerGas.feePerL2Gas < gasFees.feePerL2Gas) {
|
|
37
|
+
this.log.verbose(`Evicting tx ${meta.txHash} from pool due to insufficient fee per gas`, {
|
|
38
|
+
txMaxFeesPerGas: maxFeesPerGas.toInspect(),
|
|
39
|
+
blockGasFees: gasFees.toInspect(),
|
|
40
|
+
});
|
|
41
|
+
txsToEvict.push(meta.txHash);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (txsToEvict.length > 0) {
|
|
46
|
+
this.log.info(`Evicted ${txsToEvict.length} txs with insufficient fee per gas after block mined`);
|
|
47
|
+
await pool.deleteTxs(txsToEvict, this.name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
reason: 'insufficient_fee_per_gas',
|
|
52
|
+
success: true,
|
|
53
|
+
txsEvicted: txsToEvict,
|
|
54
|
+
};
|
|
55
|
+
} catch (err) {
|
|
56
|
+
this.log.error('Failed to evict transactions with insufficient fee per gas', { err });
|
|
57
|
+
return {
|
|
58
|
+
reason: 'insufficient_fee_per_gas',
|
|
59
|
+
success: false,
|
|
60
|
+
txsEvicted: [],
|
|
61
|
+
error: new Error('Failed to evict txs with insufficient fee per gas', { cause: err }),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { BlockHash } from '@aztec/stdlib/block';
|
|
3
3
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
4
4
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
5
5
|
|
|
@@ -33,14 +33,14 @@ export class InvalidTxsAfterReorgRule implements EvictionRule {
|
|
|
33
33
|
const pendingTxs = pool.getPendingTxs();
|
|
34
34
|
|
|
35
35
|
// Deduplicate block hashes to reduce redundant DB lookups
|
|
36
|
-
const uniqueBlockHashes = new Map<string,
|
|
36
|
+
const uniqueBlockHashes = new Map<string, BlockHash>();
|
|
37
37
|
const txsByBlockHash = new Map<string, string[]>();
|
|
38
38
|
|
|
39
39
|
for (const meta of pendingTxs) {
|
|
40
40
|
const blockHashStr = meta.anchorBlockHeaderHash;
|
|
41
41
|
if (!txsByBlockHash.has(blockHashStr)) {
|
|
42
42
|
txsByBlockHash.set(blockHashStr, []);
|
|
43
|
-
uniqueBlockHashes.set(blockHashStr,
|
|
43
|
+
uniqueBlockHashes.set(blockHashStr, BlockHash.fromString(blockHashStr));
|
|
44
44
|
}
|
|
45
45
|
txsByBlockHash.get(blockHashStr)!.push(meta.txHash);
|
|
46
46
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
3
3
|
import type { L2Block, L2BlockId, L2BlockSource } from '@aztec/stdlib/block';
|
|
4
|
+
import type { BlockMinFeesProvider } from '@aztec/stdlib/gas';
|
|
4
5
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
5
6
|
import type { BlockHeader, Tx, TxHash, TxValidator } from '@aztec/stdlib/tx';
|
|
6
7
|
|
|
@@ -74,6 +75,8 @@ export type TxPoolV2Dependencies = {
|
|
|
74
75
|
createTxValidator: () => Promise<TxValidator<TxMetaData>>;
|
|
75
76
|
/** Checks whether a tx's setup-phase calls are on the allow list. Precomputed at receipt time. */
|
|
76
77
|
checkAllowedSetupCalls: (tx: Tx) => Promise<boolean>;
|
|
78
|
+
/** Provides projected minimum fees for the next block. Used by eviction rules instead of stale block header fees. */
|
|
79
|
+
blockMinFeesProvider: BlockMinFeesProvider;
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
/**
|
|
@@ -3,7 +3,7 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
5
5
|
import { BlockHash, type L2BlockId } from '@aztec/stdlib/block';
|
|
6
|
-
import { Gas } from '@aztec/stdlib/gas';
|
|
6
|
+
import { Gas, GasFees } from '@aztec/stdlib/gas';
|
|
7
7
|
import { type Tx, TxHash } from '@aztec/stdlib/tx';
|
|
8
8
|
|
|
9
9
|
import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
|
|
@@ -23,7 +23,7 @@ export type TxMetaValidationData = {
|
|
|
23
23
|
};
|
|
24
24
|
};
|
|
25
25
|
txContext: {
|
|
26
|
-
gasSettings: { gasLimits: Gas };
|
|
26
|
+
gasSettings: { gasLimits: Gas; maxFeesPerGas: GasFees };
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
29
|
};
|
|
@@ -132,7 +132,10 @@ export async function buildTxMetaData(tx: Tx, allowedSetupCalls: boolean = true)
|
|
|
132
132
|
globalVariables: { blockNumber: anchorBlockNumber },
|
|
133
133
|
},
|
|
134
134
|
txContext: {
|
|
135
|
-
gasSettings: {
|
|
135
|
+
gasSettings: {
|
|
136
|
+
gasLimits: tx.data.constants.txContext.gasSettings.gasLimits,
|
|
137
|
+
maxFeesPerGas: tx.data.constants.txContext.gasSettings.maxFeesPerGas,
|
|
138
|
+
},
|
|
136
139
|
},
|
|
137
140
|
},
|
|
138
141
|
},
|
|
@@ -285,17 +288,19 @@ export function checkNullifierConflict(
|
|
|
285
288
|
}
|
|
286
289
|
|
|
287
290
|
/** Creates a stub TxMetaValidationData for tests that don't exercise validators. */
|
|
288
|
-
export function stubTxMetaValidationData(
|
|
291
|
+
export function stubTxMetaValidationData(
|
|
292
|
+
overrides: { expirationTimestamp?: bigint; maxFeesPerGas?: GasFees } = {},
|
|
293
|
+
): TxMetaValidationData {
|
|
289
294
|
return {
|
|
290
295
|
getNonEmptyNullifiers: () => [],
|
|
291
296
|
expirationTimestamp: overrides.expirationTimestamp ?? 0n,
|
|
292
297
|
constants: {
|
|
293
298
|
anchorBlockHeader: {
|
|
294
|
-
hash: () => Promise.resolve(
|
|
299
|
+
hash: () => Promise.resolve(BlockHash.ZERO),
|
|
295
300
|
globalVariables: { blockNumber: BlockNumber(0) },
|
|
296
301
|
},
|
|
297
302
|
txContext: {
|
|
298
|
-
gasSettings: { gasLimits: Gas.empty() },
|
|
303
|
+
gasSettings: { gasLimits: Gas.empty(), maxFeesPerGas: overrides.maxFeesPerGas ?? GasFees.empty() },
|
|
299
304
|
},
|
|
300
305
|
},
|
|
301
306
|
};
|
|
@@ -313,6 +318,7 @@ export function stubTxMetaData(
|
|
|
313
318
|
expirationTimestamp?: bigint;
|
|
314
319
|
anchorBlockHeaderHash?: string;
|
|
315
320
|
allowedSetupCalls?: boolean;
|
|
321
|
+
maxFeesPerGas?: GasFees;
|
|
316
322
|
} = {},
|
|
317
323
|
): TxMetaData {
|
|
318
324
|
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
@@ -332,7 +338,7 @@ export function stubTxMetaData(
|
|
|
332
338
|
allowedSetupCalls: overrides.allowedSetupCalls ?? true,
|
|
333
339
|
receivedAt: 0,
|
|
334
340
|
estimatedSizeBytes: 0,
|
|
335
|
-
data: stubTxMetaValidationData({ expirationTimestamp }),
|
|
341
|
+
data: stubTxMetaValidationData({ expirationTimestamp, maxFeesPerGas: overrides.maxFeesPerGas }),
|
|
336
342
|
};
|
|
337
343
|
}
|
|
338
344
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
EvictionManager,
|
|
18
18
|
FeePayerBalanceEvictionRule,
|
|
19
19
|
FeePayerBalancePreAddRule,
|
|
20
|
+
InsufficientFeePerGasEvictionRule,
|
|
20
21
|
InvalidTxsAfterMiningRule,
|
|
21
22
|
InvalidTxsAfterReorgRule,
|
|
22
23
|
LowPriorityEvictionRule,
|
|
@@ -116,6 +117,7 @@ export class TxPoolV2Impl {
|
|
|
116
117
|
|
|
117
118
|
// Post-event eviction rules (run after events to check ALL pending txs)
|
|
118
119
|
this.#evictionManager.registerRule(new InvalidTxsAfterMiningRule());
|
|
120
|
+
this.#evictionManager.registerRule(new InsufficientFeePerGasEvictionRule(deps.blockMinFeesProvider));
|
|
119
121
|
this.#evictionManager.registerRule(new InvalidTxsAfterReorgRule(deps.worldStateSynchronizer));
|
|
120
122
|
this.#evictionManager.registerRule(new FeePayerBalanceEvictionRule(deps.worldStateSynchronizer));
|
|
121
123
|
// LowPriorityEvictionRule handles cases where txs become pending via prepareForSlot (unprotect)
|
|
@@ -8,14 +8,21 @@ import {
|
|
|
8
8
|
type ValidationResult,
|
|
9
9
|
} from '@aztec/stdlib/p2p';
|
|
10
10
|
|
|
11
|
-
import { isWithinClockTolerance } from '../clock_tolerance.js';
|
|
11
|
+
import { PipeliningWindow, isWithinClockTolerance } from '../clock_tolerance.js';
|
|
12
12
|
|
|
13
13
|
export class CheckpointAttestationValidator implements P2PValidator<CheckpointAttestation> {
|
|
14
14
|
protected epochCache: EpochCacheInterface;
|
|
15
15
|
protected logger: Logger;
|
|
16
|
+
private readonly pipeliningWindow: PipeliningWindow;
|
|
16
17
|
|
|
17
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
epochCache: EpochCacheInterface,
|
|
20
|
+
opts: {
|
|
21
|
+
l1PublishingTime?: number;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
18
24
|
this.epochCache = epochCache;
|
|
25
|
+
this.pipeliningWindow = new PipeliningWindow(epochCache, { l1PublishingTime: opts.l1PublishingTime });
|
|
19
26
|
this.logger = createLogger('p2p:checkpoint-attestation-validator');
|
|
20
27
|
}
|
|
21
28
|
|
|
@@ -23,19 +30,23 @@ export class CheckpointAttestationValidator implements P2PValidator<CheckpointAt
|
|
|
23
30
|
const slotNumber = message.payload.header.slotNumber;
|
|
24
31
|
|
|
25
32
|
try {
|
|
26
|
-
// Use target slots since proposals target pipeline slots (slot + 1 when pipelining)
|
|
33
|
+
// Use target slots since proposals target pipeline slots (slot + 1 when pipelining).
|
|
27
34
|
const { targetSlot, nextSlot } = this.epochCache.getTargetAndNextSlot();
|
|
28
35
|
|
|
29
36
|
if (slotNumber !== targetSlot && slotNumber !== nextSlot) {
|
|
30
|
-
//
|
|
31
|
-
|
|
37
|
+
// When pipelining, accept attestations for the current slot (built in the previous slot)
|
|
38
|
+
// until the target slot reaches its L1 publish cutoff.
|
|
39
|
+
if (this.pipeliningWindow.acceptsAttestation(slotNumber)) {
|
|
40
|
+
// Fall through to remaining validation (signature, committee, etc.)
|
|
41
|
+
} else if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
|
|
32
42
|
this.logger.warn(
|
|
33
43
|
`Checkpoint attestation slot ${slotNumber} is not current (${targetSlot}) or next (${nextSlot}) slot`,
|
|
34
44
|
);
|
|
35
45
|
return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
|
|
46
|
+
} else {
|
|
47
|
+
this.logger.debug(`Ignoring checkpoint attestation for previous slot ${slotNumber} within clock tolerance`);
|
|
48
|
+
return { result: 'ignore' };
|
|
36
49
|
}
|
|
37
|
-
this.logger.debug(`Ignoring checkpoint attestation for previous slot ${slotNumber} within clock tolerance`);
|
|
38
|
-
return { result: 'ignore' };
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
// Verify the signature is valid
|
|
@@ -20,8 +20,11 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
|
|
|
20
20
|
epochCache: EpochCacheInterface,
|
|
21
21
|
private attestationPool: AttestationPoolApi,
|
|
22
22
|
telemetryClient: TelemetryClient,
|
|
23
|
+
opts: {
|
|
24
|
+
l1PublishingTime?: number;
|
|
25
|
+
} = {},
|
|
23
26
|
) {
|
|
24
|
-
super(epochCache);
|
|
27
|
+
super(epochCache, opts);
|
|
25
28
|
this.logger = this.logger.createChild('[FISHERMAN]');
|
|
26
29
|
|
|
27
30
|
const meter = telemetryClient.getMeter('FishermanAttestationValidator');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
2
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { DEFAULT_P2P_PROPAGATION_TIME, createPipelinedCheckpointTimingModel } from '@aztec/stdlib/timetable';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Maximum clock disparity tolerance for P2P message validation (in milliseconds).
|
|
@@ -50,3 +51,70 @@ export function isWithinClockTolerance(
|
|
|
50
51
|
|
|
51
52
|
return elapsedMs < MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Checks if a message should be accepted under the pipelining grace period.
|
|
57
|
+
*
|
|
58
|
+
* When pipelining is enabled, `targetSlot = slotNow + 1`. A proposal built in slot N-1
|
|
59
|
+
* for slot N arrives when validators are in slot N, so their `targetSlot = N+1`.
|
|
60
|
+
* This function accepts proposals for the current wallclock slot if we're within the
|
|
61
|
+
* first `windowSeconds` seconds of the slot (the pipelining grace period). - see stdlib/timetable/index.ts
|
|
62
|
+
*
|
|
63
|
+
* @param messageSlot - The slot number from the received message
|
|
64
|
+
* @param epochCache - EpochCache to get timing and pipelining state
|
|
65
|
+
* @param windowSeconds - The window grace period allowed for attestations into the next slot
|
|
66
|
+
* @returns true if pipelining is enabled, the message is for the current slot, and we're within the grace period
|
|
67
|
+
*/
|
|
68
|
+
function isWithinPipeliningWindow(
|
|
69
|
+
messageSlot: SlotNumber,
|
|
70
|
+
epochCache: EpochCacheInterface,
|
|
71
|
+
windowSeconds: number,
|
|
72
|
+
): boolean {
|
|
73
|
+
if (!epochCache.isProposerPipeliningEnabled()) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const currentSlot = epochCache.getSlotNow();
|
|
78
|
+
if (messageSlot !== currentSlot) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
|
|
83
|
+
const slotStartMs = slotStartTs * 1000n;
|
|
84
|
+
const elapsedMs = Number(nowMs - slotStartMs);
|
|
85
|
+
const windowMs = windowSeconds * 1000 + MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
86
|
+
|
|
87
|
+
return elapsedMs < windowMs;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class PipeliningWindow {
|
|
91
|
+
private readonly proposalWindowIntoTargetSlot: number;
|
|
92
|
+
private readonly attestationWindowIntoTargetSlot: number;
|
|
93
|
+
|
|
94
|
+
constructor(
|
|
95
|
+
private readonly epochCache: EpochCacheInterface,
|
|
96
|
+
opts: {
|
|
97
|
+
p2pPropagationTime?: number;
|
|
98
|
+
l1PublishingTime?: number;
|
|
99
|
+
} = {},
|
|
100
|
+
) {
|
|
101
|
+
const l1Constants = epochCache.getL1Constants();
|
|
102
|
+
const checkpointTiming = createPipelinedCheckpointTimingModel({
|
|
103
|
+
aztecSlotDuration: l1Constants.slotDuration,
|
|
104
|
+
ethereumSlotDuration: l1Constants.ethereumSlotDuration,
|
|
105
|
+
l1PublishingTime: opts.l1PublishingTime ?? l1Constants.ethereumSlotDuration,
|
|
106
|
+
p2pPropagationTime: opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.proposalWindowIntoTargetSlot = checkpointTiming.proposalWindowIntoTargetSlot;
|
|
110
|
+
this.attestationWindowIntoTargetSlot = checkpointTiming.attestationWindowIntoTargetSlot;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public acceptsProposal(messageSlot: SlotNumber): boolean {
|
|
114
|
+
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.proposalWindowIntoTargetSlot);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public acceptsAttestation(messageSlot: SlotNumber): boolean {
|
|
118
|
+
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.attestationWindowIntoTargetSlot);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -6,7 +6,10 @@ import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
|
6
6
|
export class BlockProposalValidator implements P2PValidator<BlockProposal> {
|
|
7
7
|
private proposalValidator: ProposalValidator;
|
|
8
8
|
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
epochCache: EpochCacheInterface,
|
|
11
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
|
|
12
|
+
) {
|
|
10
13
|
this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:block_proposal_validator');
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -6,7 +6,10 @@ import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
|
6
6
|
export class CheckpointProposalValidator implements P2PValidator<CheckpointProposal> {
|
|
7
7
|
private proposalValidator: ProposalValidator;
|
|
8
8
|
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
epochCache: EpochCacheInterface,
|
|
11
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
|
|
12
|
+
) {
|
|
10
13
|
this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:checkpoint_proposal_validator');
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type ValidationResult,
|
|
9
9
|
} from '@aztec/stdlib/p2p';
|
|
10
10
|
|
|
11
|
-
import { isWithinClockTolerance } from '../clock_tolerance.js';
|
|
11
|
+
import { PipeliningWindow, isWithinClockTolerance } from '../clock_tolerance.js';
|
|
12
12
|
|
|
13
13
|
/** Validates header-level and tx-level fields of block and checkpoint proposals. */
|
|
14
14
|
export class ProposalValidator {
|
|
@@ -16,33 +16,39 @@ export class ProposalValidator {
|
|
|
16
16
|
private logger: Logger;
|
|
17
17
|
private txsPermitted: boolean;
|
|
18
18
|
private maxTxsPerBlock?: number;
|
|
19
|
+
private pipeliningWindow: PipeliningWindow;
|
|
19
20
|
|
|
20
21
|
constructor(
|
|
21
22
|
epochCache: EpochCacheInterface,
|
|
22
|
-
opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
|
|
23
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
|
|
23
24
|
loggerName: string,
|
|
24
25
|
) {
|
|
25
26
|
this.epochCache = epochCache;
|
|
26
27
|
this.txsPermitted = opts.txsPermitted;
|
|
27
28
|
this.maxTxsPerBlock = opts.maxTxsPerBlock;
|
|
29
|
+
this.pipeliningWindow = new PipeliningWindow(epochCache, { p2pPropagationTime: opts.p2pPropagationTime });
|
|
28
30
|
this.logger = createLogger(loggerName);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/** Validates header-level fields: slot, signature, and proposer. */
|
|
32
34
|
public async validate(proposal: BlockProposal | CheckpointProposalCore): Promise<ValidationResult> {
|
|
33
35
|
try {
|
|
34
|
-
// Slot check: use target slots since proposals target pipeline slots (slot + 1 when pipelining)
|
|
36
|
+
// Slot check: use target slots since proposals target pipeline slots (slot + 1 when pipelining).
|
|
35
37
|
const { targetSlot, nextSlot } = this.epochCache.getTargetAndNextSlot();
|
|
36
38
|
|
|
37
39
|
const slotNumber = proposal.slotNumber;
|
|
38
40
|
if (slotNumber !== targetSlot && slotNumber !== nextSlot) {
|
|
39
|
-
//
|
|
40
|
-
if
|
|
41
|
+
// When pipelining, accept proposals for the current slot (built in the previous slot)
|
|
42
|
+
// if they're still within the shared proposal acceptance window.
|
|
43
|
+
if (this.pipeliningWindow.acceptsProposal(slotNumber)) {
|
|
44
|
+
// Fall through to remaining validation (signature, proposer, etc.)
|
|
45
|
+
} else if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
|
|
41
46
|
this.logger.warn(`Penalizing peer for invalid slot number ${slotNumber}`, { targetSlot, nextSlot });
|
|
42
47
|
return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
|
|
48
|
+
} else {
|
|
49
|
+
this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
|
|
50
|
+
return { result: 'ignore' };
|
|
43
51
|
}
|
|
44
|
-
this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
|
|
45
|
-
return { result: 'ignore' };
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
// Signature validity
|
|
@@ -75,7 +75,7 @@ This validator is invoked on **every** transaction potentially entering the pend
|
|
|
75
75
|
- Startup hydration — revalidating persisted non-mined txs on node restart
|
|
76
76
|
|
|
77
77
|
Runs:
|
|
78
|
-
- DoubleSpend, BlockHeader, GasLimits, Timestamp, AllowedSetupCalls
|
|
78
|
+
- DoubleSpend, BlockHeader, GasLimits, MaxFeePerGas, Timestamp, AllowedSetupCalls
|
|
79
79
|
|
|
80
80
|
Operates on `TxMetaData` (pre-built by the pool) rather than full `Tx` objects.
|
|
81
81
|
|
|
@@ -91,8 +91,9 @@ The `AllowedSetupCallsMetaValidator` checks a precomputed boolean flag (`TxMetaD
|
|
|
91
91
|
| `MetadataTxValidator` | Chain ID, rollup version, protocol contracts hash, VK tree root | 4.18 us |
|
|
92
92
|
| `TimestampTxValidator` | Transaction has not expired (expiration timestamp vs next slot) | 1.56 us |
|
|
93
93
|
| `DoubleSpendTxValidator` | Nullifiers do not already exist in the nullifier tree | 106.08 us |
|
|
94
|
-
| `GasTxValidator` | Gas limits are within bounds (delegates to `GasLimitsValidator`), max fee per gas meets current block fees, and fee payer has sufficient FeeJuice balance | 1.02 ms |
|
|
94
|
+
| `GasTxValidator` | Gas limits are within bounds (delegates to `GasLimitsValidator`), max fee per gas meets current block fees (delegates to `MaxFeePerGasValidator`), and fee payer has sufficient FeeJuice balance | 1.02 ms |
|
|
95
95
|
| `GasLimitsValidator` | Gas limits are >= fixed minimums and <= AVM max processable L2 gas. Used standalone in pool migration; also called internally by `GasTxValidator` | 3–10 us |
|
|
96
|
+
| `MaxFeePerGasValidator` | Max fee per gas >= current block gas fees on both dimensions (DA and L2). Used standalone in pool migration; also called internally by `GasTxValidator` | 3–10 us |
|
|
96
97
|
| `PhasesTxValidator` | Public function calls in setup phase are on the allow list | 10.12–13.12 us |
|
|
97
98
|
| `AllowedSetupCallsMetaValidator` | Checks the precomputed `allowedSetupCalls` flag on `TxMetaData`. Used in pool migration instead of the full `PhasesTxValidator` | — |
|
|
98
99
|
| `BlockHeaderTxValidator` | Transaction's anchor block hash exists in the archive tree | 98.88 us |
|
|
@@ -110,10 +111,17 @@ The `AllowedSetupCallsMetaValidator` checks a precomputed boolean flag (`TxMetaD
|
|
|
110
111
|
| DoubleSpend | Stage 1 | Yes | — | Yes | Yes |
|
|
111
112
|
| Gas (balance + limits) | Stage 1 | Optional* | — | Yes | — |
|
|
112
113
|
| GasLimits (standalone) | — | — | — | — | Yes |
|
|
114
|
+
| MaxFeePerGas (standalone) | — | — | — | — | Yes |
|
|
113
115
|
| Phases | Stage 1 | Yes | — | Yes | — |
|
|
114
116
|
| AllowedSetupCalls | — | — | — | — | Yes |
|
|
115
117
|
| BlockHeader | Stage 1 | Yes | — | Yes | Yes |
|
|
116
118
|
| Proof | Stage 2 | Optional** | Yes | — | — |
|
|
117
119
|
|
|
118
|
-
\* Gas balance check is skipped when `skipFeeEnforcement` is set (testing/dev). `GasTxValidator` internally delegates to `GasLimitsValidator` as its first
|
|
120
|
+
\* Gas balance check is skipped when `skipFeeEnforcement` is set (testing/dev). `GasTxValidator` internally delegates to `GasLimitsValidator` and `MaxFeePerGasValidator` as its first steps, so gas limits and fee-per-gas are checked wherever `GasTxValidator` runs. Pool migration uses `GasLimitsValidator` and `MaxFeePerGasValidator` standalone because it doesn't need the balance check.
|
|
119
121
|
\** Proof verification is skipped for simulations (no verifier provided).
|
|
122
|
+
|
|
123
|
+
## Fee-Per-Gas Rejection Strategy
|
|
124
|
+
|
|
125
|
+
The `MaxFeePerGasValidator` and `InsufficientFeePerGasEvictionRule` reject and evict transactions whose `maxFeesPerGas` falls below the current block's gas fees. This is a simple strategy: if a tx can't pay the current fees, it gets rejected on entry and evicted after each new block.
|
|
126
|
+
|
|
127
|
+
**Caveat**: This may evict transactions that would become valid again if block fees drop. A more nuanced approach would be to define a threshold (e.g., 50%) and only reject/evict when the tx's max fee falls below that fraction of the current fees. The current approach is simpler and ensures the pool doesn't accumulate transactions with low max fees that are unlikely to be mined soon.
|
|
@@ -15,7 +15,7 @@ export class ArchiveCache implements ArchiveSource {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
public async getArchiveIndices(archives: BlockHash[]): Promise<(bigint | undefined)[]> {
|
|
18
|
-
const toCheckDb = archives.filter(n => !this.archives.has(n.toString()))
|
|
18
|
+
const toCheckDb = archives.filter(n => !this.archives.has(n.toString()));
|
|
19
19
|
const dbHits = await this.db.findLeafIndices(MerkleTreeId.ARCHIVE, toCheckDb);
|
|
20
20
|
dbHits.forEach((x, index) => {
|
|
21
21
|
if (x !== undefined) {
|
|
@@ -56,7 +56,7 @@ import { type ArchiveSource, BlockHeaderTxValidator } from './block_header_valid
|
|
|
56
56
|
import { ContractInstanceTxValidator } from './contract_instance_validator.js';
|
|
57
57
|
import { DataTxValidator } from './data_validator.js';
|
|
58
58
|
import { DoubleSpendTxValidator, type NullifierSource } from './double_spend_validator.js';
|
|
59
|
-
import { GasLimitsValidator, GasTxValidator } from './gas_validator.js';
|
|
59
|
+
import { GasLimitsValidator, GasTxValidator, MaxFeePerGasValidator } from './gas_validator.js';
|
|
60
60
|
import { MetadataTxValidator } from './metadata_validator.js';
|
|
61
61
|
import { NullifierCache } from './nullifier_cache.js';
|
|
62
62
|
import { AllowedSetupCallsMetaValidator, PhasesTxValidator } from './phases_validator.js';
|
|
@@ -423,6 +423,7 @@ export async function createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
|
423
423
|
timestamp: bigint,
|
|
424
424
|
blockNumber: BlockNumber,
|
|
425
425
|
gasLimitOpts: { rollupManaLimit?: number; maxBlockL2Gas?: number; maxBlockDAGas?: number },
|
|
426
|
+
gasFees: GasFees,
|
|
426
427
|
bindings?: LoggerBindings,
|
|
427
428
|
): Promise<TxValidator<TxMetaData>> {
|
|
428
429
|
await worldStateSynchronizer.syncImmediate();
|
|
@@ -440,6 +441,7 @@ export async function createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
|
440
441
|
};
|
|
441
442
|
return new AggregateTxValidator<TxMetaData>(
|
|
442
443
|
new GasLimitsValidator<TxMetaData>({ ...gasLimitOpts, bindings }),
|
|
444
|
+
new MaxFeePerGasValidator<TxMetaData>(gasFees, bindings),
|
|
443
445
|
new TimestampTxValidator<TxMetaData>({ timestamp, blockNumber }, bindings),
|
|
444
446
|
new DoubleSpendTxValidator<TxMetaData>(nullifierSource, bindings),
|
|
445
447
|
new BlockHeaderTxValidator<TxMetaData>(archiveSource, bindings),
|