@aztec/p2p 0.0.1-commit.3895657bc → 0.0.1-commit.3e3d0c9cd
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 +1 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +19 -7
- 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 +16 -6
- package/dest/config.d.ts +7 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -0
- 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/interfaces.d.ts +3 -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 +9 -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 +7 -1
- 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 +19 -5
- 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 +28 -8
- 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/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 +27 -0
- package/dest/services/libp2p/libp2p_service.d.ts +1 -1
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +30 -6
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +8 -2
- 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 +26 -9
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +3 -1
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +2 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +6 -0
- 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.js +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 +2 -1
- package/package.json +14 -14
- package/src/client/factory.ts +34 -11
- package/src/client/p2p_client.ts +16 -8
- package/src/config.ts +16 -0
- 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/eviction/fee_payer_balance_eviction_rule.ts +2 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +2 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +11 -1
- 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 +20 -5
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/tx_validator/README.md +5 -1
- package/src/msg_validators/tx_validator/factory.ts +36 -3
- package/src/msg_validators/tx_validator/gas_validator.ts +41 -8
- package/src/msg_validators/tx_validator/phases_validator.ts +30 -0
- package/src/services/libp2p/libp2p_service.ts +28 -6
- package/src/services/reqresp/README.md +229 -0
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +29 -9
- package/src/services/reqresp/batch-tx-requester/interface.ts +2 -0
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +7 -0
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +5 -0
- package/src/services/reqresp/reqresp.ts +1 -1
- package/src/test-helpers/testbench-utils.ts +1 -0
package/src/client/factory.ts
CHANGED
|
@@ -17,7 +17,11 @@ import { AttestationPool, type AttestationPoolApi } from '../mem_pools/attestati
|
|
|
17
17
|
import type { MemPools } from '../mem_pools/interface.js';
|
|
18
18
|
import type { TxPoolV2 } from '../mem_pools/tx_pool_v2/interfaces.js';
|
|
19
19
|
import { AztecKVTxPoolV2 } from '../mem_pools/tx_pool_v2/tx_pool_v2.js';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createCheckAllowedSetupCalls,
|
|
22
|
+
createTxValidatorForTransactionsEnteringPendingTxPool,
|
|
23
|
+
getDefaultAllowedSetupFunctions,
|
|
24
|
+
} from '../msg_validators/index.js';
|
|
21
25
|
import { DummyP2PService } from '../services/dummy_service.js';
|
|
22
26
|
import { LibP2PService } from '../services/index.js';
|
|
23
27
|
import { createFileStoreTxSources } from '../services/tx_collection/file_store_tx_source.js';
|
|
@@ -75,6 +79,33 @@ export async function createP2PClient(
|
|
|
75
79
|
const rollupAddress = inputConfig.l1Contracts.rollupAddress.toString().toLowerCase().replace(/^0x/, '');
|
|
76
80
|
const txFileStoreBasePath = `aztec-${inputConfig.l1ChainId}-${inputConfig.rollupVersion}-0x${rollupAddress}`;
|
|
77
81
|
|
|
82
|
+
const allowedInSetup = [
|
|
83
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
84
|
+
...(inputConfig.txPublicSetupAllowListExtend ?? []),
|
|
85
|
+
];
|
|
86
|
+
const checkAllowedSetupCalls = createCheckAllowedSetupCalls(
|
|
87
|
+
archiver,
|
|
88
|
+
allowedInSetup,
|
|
89
|
+
() => epochCache.getEpochAndSlotInNextL1Slot().ts,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const createTxValidator = async () => {
|
|
93
|
+
// We accept transactions if they are not expired by the next slot and block number (checked based on the ExpirationTimestamp field)
|
|
94
|
+
const currentBlockNumber = await archiver.getBlockNumber();
|
|
95
|
+
const { ts: nextSlotTimestamp } = epochCache.getEpochAndSlotInNextL1Slot();
|
|
96
|
+
const l1Constants = await archiver.getL1Constants();
|
|
97
|
+
return createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
98
|
+
worldStateSynchronizer,
|
|
99
|
+
nextSlotTimestamp,
|
|
100
|
+
BlockNumber(currentBlockNumber + 1),
|
|
101
|
+
{
|
|
102
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
103
|
+
maxBlockL2Gas: config.validateMaxL2BlockGas,
|
|
104
|
+
maxBlockDAGas: config.validateMaxDABlockGas,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
78
109
|
const txPool =
|
|
79
110
|
deps.txPool ??
|
|
80
111
|
new AztecKVTxPoolV2(
|
|
@@ -83,16 +114,8 @@ export async function createP2PClient(
|
|
|
83
114
|
{
|
|
84
115
|
l2BlockSource: archiver,
|
|
85
116
|
worldStateSynchronizer,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const currentBlockNumber = await archiver.getBlockNumber();
|
|
89
|
-
const { ts: nextSlotTimestamp } = epochCache.getEpochAndSlotInNextL1Slot();
|
|
90
|
-
return createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
91
|
-
worldStateSynchronizer,
|
|
92
|
-
nextSlotTimestamp,
|
|
93
|
-
BlockNumber(currentBlockNumber + 1),
|
|
94
|
-
);
|
|
95
|
-
},
|
|
117
|
+
checkAllowedSetupCalls,
|
|
118
|
+
createTxValidator,
|
|
96
119
|
},
|
|
97
120
|
telemetry,
|
|
98
121
|
{
|
package/src/client/p2p_client.ts
CHANGED
|
@@ -669,20 +669,28 @@ export class P2PClient extends WithTracer implements P2P {
|
|
|
669
669
|
}
|
|
670
670
|
|
|
671
671
|
/**
|
|
672
|
-
* Returns true if the prune
|
|
673
|
-
* If the
|
|
674
|
-
* If they differ, the prune spans across checkpoints (epoch prune).
|
|
672
|
+
* Returns true if the prune is an epoch prune (new checkpoint number is less than old).
|
|
673
|
+
* If the checkpoint number stays the same or increases, the prune is within a checkpoint.
|
|
675
674
|
*/
|
|
676
675
|
private async isEpochPrune(newCheckpoint: CheckpointId): Promise<boolean> {
|
|
677
676
|
const tips = await this.l2Tips.getL2Tips();
|
|
678
677
|
const oldCheckpointNumber = tips.checkpointed.checkpoint.number;
|
|
679
|
-
if (oldCheckpointNumber <= CheckpointNumber.
|
|
678
|
+
if (oldCheckpointNumber <= CheckpointNumber.INITIAL) {
|
|
680
679
|
return false;
|
|
681
680
|
}
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
681
|
+
const newCheckpointNumber = newCheckpoint.number;
|
|
682
|
+
// We check that the new checkpoint number is less than the old checkpoint number in order to consider it an epoch prune.
|
|
683
|
+
// To be more certain that it is an epoch prune, we will check that at least 2 checkpoints were removed.
|
|
684
|
+
// This means we should avoid thinking checkpoints removed by L1 re-orgs are epoch prunes
|
|
685
|
+
const thresholdForEpochPrune = CheckpointNumber(oldCheckpointNumber - 2);
|
|
686
|
+
const isEpochPrune = newCheckpointNumber <= thresholdForEpochPrune;
|
|
687
|
+
if (isEpochPrune) {
|
|
688
|
+
this.log.info(`Detected epoch prune to ${newCheckpointNumber}`, {
|
|
689
|
+
oldCheckpointNumber,
|
|
690
|
+
newCheckpointNumber,
|
|
691
|
+
thresholdForEpochPrune,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
686
694
|
return isEpochPrune;
|
|
687
695
|
}
|
|
688
696
|
|
package/src/config.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface P2PConfig
|
|
|
43
43
|
/** Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set. */
|
|
44
44
|
validateMaxTxsPerBlock?: number;
|
|
45
45
|
|
|
46
|
+
/** Maximum L2 gas per block for validation. When set, txs exceeding this limit are rejected. */
|
|
47
|
+
validateMaxL2BlockGas?: number;
|
|
48
|
+
|
|
49
|
+
/** Maximum DA gas per block for validation. When set, txs exceeding this limit are rejected. */
|
|
50
|
+
validateMaxDABlockGas?: number;
|
|
51
|
+
|
|
46
52
|
/** A flag dictating whether the P2P subsystem should be enabled. */
|
|
47
53
|
p2pEnabled: boolean;
|
|
48
54
|
|
|
@@ -208,6 +214,16 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
208
214
|
'Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set.',
|
|
209
215
|
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
210
216
|
},
|
|
217
|
+
validateMaxL2BlockGas: {
|
|
218
|
+
env: 'VALIDATOR_MAX_L2_BLOCK_GAS',
|
|
219
|
+
description: 'Maximum L2 gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
220
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
221
|
+
},
|
|
222
|
+
validateMaxDABlockGas: {
|
|
223
|
+
env: 'VALIDATOR_MAX_DA_BLOCK_GAS',
|
|
224
|
+
description: 'Maximum DA gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
225
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
226
|
+
},
|
|
211
227
|
p2pEnabled: {
|
|
212
228
|
env: 'P2P_ENABLED',
|
|
213
229
|
description: 'A flag dictating whether the P2P subsystem should be enabled.',
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -72,6 +72,8 @@ export type TxPoolV2Dependencies = {
|
|
|
72
72
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
73
73
|
/** Factory that creates a validator for re-validating pool transactions using metadata */
|
|
74
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>;
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
/**
|
|
@@ -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: {
|
|
@@ -304,6 +312,7 @@ export function stubTxMetaData(
|
|
|
304
312
|
nullifiers?: string[];
|
|
305
313
|
expirationTimestamp?: bigint;
|
|
306
314
|
anchorBlockHeaderHash?: string;
|
|
315
|
+
allowedSetupCalls?: boolean;
|
|
307
316
|
} = {},
|
|
308
317
|
): TxMetaData {
|
|
309
318
|
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
@@ -320,6 +329,7 @@ export function stubTxMetaData(
|
|
|
320
329
|
feeLimit: overrides.feeLimit ?? 100n,
|
|
321
330
|
nullifiers: overrides.nullifiers ?? [`0x${normalizedTxHash.slice(2)}null1`],
|
|
322
331
|
expirationTimestamp,
|
|
332
|
+
allowedSetupCalls: overrides.allowedSetupCalls ?? true,
|
|
323
333
|
receivedAt: 0,
|
|
324
334
|
estimatedSizeBytes: 0,
|
|
325
335
|
data: stubTxMetaValidationData({ expirationTimestamp }),
|
|
@@ -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
|
|
|
@@ -165,6 +172,11 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
|
|
|
165
172
|
return this.#queue.put(() => Promise.resolve(this.#impl.getLowestPriorityPending(limit)));
|
|
166
173
|
}
|
|
167
174
|
|
|
175
|
+
/** Returns read-only access to the pool. Used for testing. */
|
|
176
|
+
getPoolReadAccess(): PoolReadAccess {
|
|
177
|
+
return this.#impl.getPoolReadAccess();
|
|
178
|
+
}
|
|
179
|
+
|
|
168
180
|
// === Configuration ===
|
|
169
181
|
|
|
170
182
|
updateConfig(config: Partial<TxPoolV2Config>): Promise<void> {
|
|
@@ -62,6 +62,7 @@ export class TxPoolV2Impl {
|
|
|
62
62
|
#l2BlockSource: L2BlockSource;
|
|
63
63
|
#worldStateSynchronizer: WorldStateSynchronizer;
|
|
64
64
|
#createTxValidator: TxPoolV2Dependencies['createTxValidator'];
|
|
65
|
+
#checkAllowedSetupCalls: TxPoolV2Dependencies['checkAllowedSetupCalls'];
|
|
65
66
|
|
|
66
67
|
// === In-Memory Indices ===
|
|
67
68
|
#indices: TxPoolIndices = new TxPoolIndices();
|
|
@@ -93,6 +94,7 @@ export class TxPoolV2Impl {
|
|
|
93
94
|
this.#l2BlockSource = deps.l2BlockSource;
|
|
94
95
|
this.#worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
95
96
|
this.#createTxValidator = deps.createTxValidator;
|
|
97
|
+
this.#checkAllowedSetupCalls = deps.checkAllowedSetupCalls;
|
|
96
98
|
|
|
97
99
|
this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
|
|
98
100
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
@@ -354,6 +356,7 @@ export class TxPoolV2Impl {
|
|
|
354
356
|
|
|
355
357
|
// Check if already in pool
|
|
356
358
|
if (this.#indices.has(txHashStr)) {
|
|
359
|
+
this.#log.verbose(`canAddPendingTx: tx ${txHashStr} already in pool`);
|
|
357
360
|
return 'ignored';
|
|
358
361
|
}
|
|
359
362
|
|
|
@@ -362,26 +365,37 @@ export class TxPoolV2Impl {
|
|
|
362
365
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
363
366
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
364
367
|
|
|
365
|
-
|
|
368
|
+
if (preAddResult.shouldIgnore) {
|
|
369
|
+
this.#log.verbose(`canAddPendingTx: tx ${txHashStr} ignored by pre-add rule`, {
|
|
370
|
+
reason: preAddResult.reason?.message ?? 'no reason provided',
|
|
371
|
+
});
|
|
372
|
+
return 'ignored';
|
|
373
|
+
}
|
|
374
|
+
return 'accepted';
|
|
366
375
|
}
|
|
367
376
|
|
|
368
377
|
async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
369
378
|
const slotNumber = block.globalVariables.slotNumber;
|
|
370
379
|
|
|
380
|
+
// Precompute setup-call allow-list flags outside the store transaction
|
|
381
|
+
const allowedFlags = await Promise.all(txs.map(tx => this.#checkAllowedSetupCalls(tx)));
|
|
382
|
+
|
|
371
383
|
await this.#store.transactionAsync(async () => {
|
|
372
|
-
for (
|
|
384
|
+
for (let i = 0; i < txs.length; i++) {
|
|
385
|
+
const tx = txs[i];
|
|
373
386
|
const txHash = tx.getTxHash();
|
|
374
387
|
const txHashStr = txHash.toString();
|
|
375
388
|
const isNew = !this.#indices.has(txHashStr);
|
|
376
389
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
377
390
|
|
|
378
391
|
if (isNew) {
|
|
392
|
+
const meta = await buildTxMetaData(tx, allowedFlags[i]);
|
|
379
393
|
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
380
394
|
if (minedBlockId) {
|
|
381
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
395
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
382
396
|
this.#indices.setProtection(txHashStr, slotNumber);
|
|
383
397
|
} else {
|
|
384
|
-
await this.#addTx(tx, { protected: slotNumber }, opts);
|
|
398
|
+
await this.#addTx(tx, { protected: slotNumber }, opts, meta);
|
|
385
399
|
}
|
|
386
400
|
} else {
|
|
387
401
|
// Existing tx - update protection and mined status
|
|
@@ -976,7 +990,8 @@ export class TxPoolV2Impl {
|
|
|
976
990
|
|
|
977
991
|
try {
|
|
978
992
|
const tx = Tx.fromBuffer(buffer);
|
|
979
|
-
const
|
|
993
|
+
const allowedSetupCalls = await this.#checkAllowedSetupCalls(tx);
|
|
994
|
+
const meta = await buildTxMetaData(tx, allowedSetupCalls);
|
|
980
995
|
loaded.push({ tx, meta });
|
|
981
996
|
} catch (err) {
|
|
982
997
|
this.#log.warn(`Failed to deserialize tx ${txHashStr}, deleting`, { err });
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Attestation Validation
|
|
2
|
+
|
|
3
|
+
This module validates `CheckpointAttestation` gossipsub messages. Attestations are signatures from committee members endorsing a checkpoint proposal.
|
|
4
|
+
|
|
5
|
+
**Topic**: `checkpoint_attestation` | **Snappy size limit**: 5 KB
|
|
6
|
+
|
|
7
|
+
## Stage 1: AttestationValidator (Gossipsub Validation)
|
|
8
|
+
|
|
9
|
+
| # | Rule | Consequence | Severity | File |
|
|
10
|
+
|---|------|-------------|----------|------|
|
|
11
|
+
| 1 | **Slot timeliness**: `currentSlot` or `nextSlot`. Previous slot within 500ms: IGNORE. Older: REJECT. | REJECT or IGNORE | HighToleranceError | `attestation_validator.ts` |
|
|
12
|
+
| 2 | **Attester signature**: `getSender()` must recover valid address | REJECT | LowToleranceError | same |
|
|
13
|
+
| 3 | **Attester in committee**: recovered address in committee for slot | REJECT | HighToleranceError | same |
|
|
14
|
+
| 4 | **Proposer exists**: `getProposerAttesterAddressInSlot` must return defined | REJECT | HighToleranceError | same |
|
|
15
|
+
| 5 | **Proposer signature**: `getProposer()` must recover valid address | REJECT | LowToleranceError | same |
|
|
16
|
+
| 6 | **Proposer matches expected**: recovered proposer = expected for slot | REJECT | HighToleranceError | same |
|
|
17
|
+
| 7 | **NoCommitteeError**: committee unavailable | REJECT | LowToleranceError | same |
|
|
18
|
+
|
|
19
|
+
**Fisherman mode extension** (`FishermanAttestationValidator`): if a checkpoint proposal for the same archive exists in pool, the attestation's `ConsensusPayload` must `.equals()` the stored proposal's payload. On mismatch: REJECT + LowToleranceError.
|
|
20
|
+
|
|
21
|
+
## Stage 2: Pool Admission
|
|
22
|
+
|
|
23
|
+
| # | Rule | Consequence |
|
|
24
|
+
|---|------|-------------|
|
|
25
|
+
| 8 | Sender recoverable (pool-side) | Silent drop |
|
|
26
|
+
| 9 | Not a duplicate (same slot + proposalId + signer) | IGNORE |
|
|
27
|
+
| 10 | Per-signer cap: `MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER` = 3 | IGNORE |
|
|
28
|
+
|
|
29
|
+
Own attestations added via `addOwnCheckpointAttestations` bypass the per-signer cap.
|
|
30
|
+
|
|
31
|
+
## Stage 3: Equivocation Detection
|
|
32
|
+
|
|
33
|
+
When a signer's attestation count for a slot reaches exactly 2 (different proposals): `duplicateAttestationCallback` fires -> `WANT_TO_SLASH_EVENT` with `OffenseType.DUPLICATE_ATTESTATION`. Attestation still ACCEPTED and rebroadcast. Callback fires once (not again at count 3+).
|
|
34
|
+
|
|
35
|
+
## Validation at L1 Checkpoint Submission (Archiver)
|
|
36
|
+
|
|
37
|
+
| Rule | Consequence | File |
|
|
38
|
+
|------|-------------|------|
|
|
39
|
+
| Each attestation must have recoverable signature (or address-only is allowed but does not count toward quorum) | Checkpoint rejected as invalid | `archiver/src/modules/validation.ts` |
|
|
40
|
+
| Attestation at index `i` must correspond to committee member at index `i` | Checkpoint rejected as invalid | same |
|
|
41
|
+
| Valid attestation count >= floor(committee * 2/3) + 1 | Checkpoint rejected as invalid | same |
|
|
42
|
+
| No committee / escape hatch open | Accepted unconditionally | same |
|
|
43
|
+
|
|
44
|
+
Note: `skipValidateCheckpointAttestations` config flag bypasses all archiver attestation validation.
|
|
45
|
+
|
|
46
|
+
## Gossipsub Topic Scoring
|
|
47
|
+
|
|
48
|
+
P3 enabled with expected messages per slot = `targetCommitteeSize`. Conservative threshold (30% of convergence value). Max P3 penalty = -34 per topic.
|
|
49
|
+
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Proposal Validation
|
|
2
|
+
|
|
3
|
+
This module validates `BlockProposal` and `CheckpointProposal` gossipsub messages. Both share the same base `ProposalValidator` (neither subclass overrides `validate()`), with checkpoint-specific logic layered on top in the gossipsub handler.
|
|
4
|
+
|
|
5
|
+
## BlockProposal
|
|
6
|
+
|
|
7
|
+
**Topic**: `block_proposal` | **Snappy size limit**: 10 MB
|
|
8
|
+
|
|
9
|
+
### Stage 1: Gossipsub Validation (ProposalValidator)
|
|
10
|
+
|
|
11
|
+
File: `proposal_validator.ts`
|
|
12
|
+
|
|
13
|
+
| # | Rule | Consequence | Severity |
|
|
14
|
+
|---|------|-------------|----------|
|
|
15
|
+
| 1 | **Slot check**: must be `currentSlot` or `nextSlot`. Previous slot within 500ms tolerance: IGNORE. | REJECT | HighToleranceError |
|
|
16
|
+
| 2 | **Signature**: `getSender()` must recover a valid address. If `signedTxs` present, its recovered sender must match. | REJECT | MidToleranceError |
|
|
17
|
+
| 3 | **Txs permitted**: if `disableTransactions`, must have 0 txHashes and 0 embedded txs | REJECT | MidToleranceError |
|
|
18
|
+
| 4 | **Max txs**: `txHashes.length <= maxTxsPerBlock` | REJECT | MidToleranceError |
|
|
19
|
+
| 5 | **Embedded txs in txHashes**: every embedded tx's hash must appear in `txHashes` | REJECT | MidToleranceError |
|
|
20
|
+
| 6 | **Proposer check**: signer must match expected proposer for slot (skipped if committee size = 0) | REJECT | MidToleranceError |
|
|
21
|
+
| 7 | **Tx hash integrity**: each embedded tx's recomputed hash must match declared hash | REJECT | LowToleranceError |
|
|
22
|
+
| 8 | **NoCommitteeError**: epoch cache cannot determine committee | REJECT | LowToleranceError |
|
|
23
|
+
|
|
24
|
+
Deserialization guards: `BlockProposal.fromBuffer` and `SignedTxs.fromBuffer` both enforce `txCount <= MAX_TXS_PER_BLOCK` (65536). Violation -> REJECT + LowToleranceError.
|
|
25
|
+
|
|
26
|
+
### Stage 2: Mempool (Attestation Pool)
|
|
27
|
+
|
|
28
|
+
| # | Rule | Consequence |
|
|
29
|
+
|---|------|-------------|
|
|
30
|
+
| 9 | **Duplicate**: same archive root already stored | IGNORE (no penalty) |
|
|
31
|
+
| 10 | **Per-position cap**: max 3 proposals per (slot, indexWithinCheckpoint) | REJECT + HighToleranceError |
|
|
32
|
+
| 11 | **Equivocation**: >1 distinct proposal for same (slot, index) | ACCEPT (rebroadcast for detection). At count=2: `duplicateProposalCallback` fires -> slash event (`OffenseType.DUPLICATE_PROPOSAL`, configured via `slashDuplicateProposalPenalty`) |
|
|
33
|
+
|
|
34
|
+
### Stage 3: Validator-Client Processing (BlockProposalHandler)
|
|
35
|
+
|
|
36
|
+
Only runs on validator nodes. Non-validator nodes use a default handler that triggers tx collection without deep validation.
|
|
37
|
+
|
|
38
|
+
| # | Rule | Failure Reason |
|
|
39
|
+
|---|------|----------------|
|
|
40
|
+
| 12 | Signature re-check | `invalid_proposal` |
|
|
41
|
+
| 13 | ProposalValidator re-run | `invalid_proposal` |
|
|
42
|
+
| 14 | Self-proposal filter | Ignored silently |
|
|
43
|
+
| 15 | Parent block exists (`lastArchive.root` matches known block or genesis) | `parent_block_not_found` |
|
|
44
|
+
| 16 | Parent block slot <= proposal slot | `parent_block_wrong_slot` |
|
|
45
|
+
| 17 | Block number not already in archiver | `block_number_already_exists` |
|
|
46
|
+
| 18 | Checkpoint number consistency (multiple sub-rules for first/non-first blocks) | `invalid_proposal` |
|
|
47
|
+
| 19 | Global variables consistency (non-first block: chainId, version, slot, timestamp, coinbase, feeRecipient, gasFees match parent) | `global_variables_mismatch` |
|
|
48
|
+
| 20 | L1-to-L2 message hash matches `proposal.inHash` | `in_hash_mismatch` |
|
|
49
|
+
| 21 | All txs referenced by `txHashes` obtainable | `txs_not_available` |
|
|
50
|
+
| 22 | **Re-execution**: processed tx count matches `txHashes.length` | `timeout` (ReExTimeoutError) |
|
|
51
|
+
| 23 | **Re-execution**: no failed txs | `failed_txs` (ReExFailedTxsError) -- **SLASHABLE** |
|
|
52
|
+
| 24 | **Re-execution**: archive root and header match proposal | `state_mismatch` (ReExStateMismatchError) -- **SLASHABLE** |
|
|
53
|
+
|
|
54
|
+
**Escape hatch**: during escape hatch periods (`isEscapeHatchOpenAtSlot`), re-execution and slashing are both disabled, and the proposal is rejected locally.
|
|
55
|
+
|
|
56
|
+
**Conditional re-execution**: rules 22-24 only run when at least one condition is true: `fishermanMode` enabled, `slashBroadcastedInvalidBlockPenalty > 0` with `validatorReexecute`, committee membership with `validatorReexecute`, `alwaysReexecuteBlockProposals`, or `blobClient.canUpload()`.
|
|
57
|
+
|
|
58
|
+
**Slashing**: only `state_mismatch` and `failed_txs` trigger on-chain slashing (`OffenseType.BROADCASTED_INVALID_BLOCK_PROPOSAL`, gated by `slashBroadcastedInvalidBlockPenalty > 0`). Unknown errors during re-execution do NOT slash.
|
|
59
|
+
|
|
60
|
+
**Embedded tx validation**: txs in `signedTxs` are validated via `createTxValidatorForBlockProposalReceivedTxs` (well-formedness only) when stored in the tx pool. Invalid embedded txs are rejected from the pool but do not cause the block proposal itself to be rejected at gossipsub level.
|
|
61
|
+
|
|
62
|
+
### Gossipsub Topic Scoring
|
|
63
|
+
|
|
64
|
+
| Parameter | Effect |
|
|
65
|
+
|-----------|--------|
|
|
66
|
+
| P4 (invalidMessageDeliveries) | weight = -20, decay over 4 slots |
|
|
67
|
+
| P3 (meshMessageDeliveries) | Enabled only when `expectedBlockProposalsPerSlot > 0` (MBPS mode) |
|
|
68
|
+
| P1/P2 | Only active when P3 is enabled |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## CheckpointProposal
|
|
73
|
+
|
|
74
|
+
**Topic**: `checkpoint_proposal` | **Snappy size limit**: 10 MB
|
|
75
|
+
|
|
76
|
+
### Stage 1: Gossipsub Validation (ProposalValidator)
|
|
77
|
+
|
|
78
|
+
Same `ProposalValidator.validate()` as BlockProposal (shared implementation, neither subclass overrides it). See BlockProposal Stage 1 rules 1-8.
|
|
79
|
+
|
|
80
|
+
### Stage 2: Embedded Block Proposal Validation (if `lastBlock` present)
|
|
81
|
+
|
|
82
|
+
The checkpoint's embedded `lastBlock` is extracted via `getBlockProposal()` and validated through `BlockProposalValidator.validate()` plus block mempool checks.
|
|
83
|
+
|
|
84
|
+
| Rule | Consequence | File |
|
|
85
|
+
|------|-------------|------|
|
|
86
|
+
| Block proposal must pass `BlockProposalValidator.validate()` | If REJECT: entire checkpoint REJECTED | `libp2p_service.ts` |
|
|
87
|
+
| Block proposal must not exceed per-position cap (3) | Checkpoint REJECTED + HighToleranceError | same |
|
|
88
|
+
| Block equivocation detected (>1 proposals for same slot+index) | Checkpoint REJECTED (block itself is ACCEPT for re-broadcast) | same |
|
|
89
|
+
|
|
90
|
+
### Stage 3: Mempool (Attestation Pool)
|
|
91
|
+
|
|
92
|
+
| Rule | Consequence | File |
|
|
93
|
+
|------|-------------|------|
|
|
94
|
+
| Duplicate (same archive ID) | IGNORE (no penalty). Embedded block still processed if valid. | `attestation_pool.ts` |
|
|
95
|
+
| Per-slot cap: `MAX_CHECKPOINT_PROPOSALS_PER_SLOT` = 5 | REJECT + HighToleranceError. Embedded block still processed. | same |
|
|
96
|
+
|
|
97
|
+
### Stage 4: Equivocation Detection
|
|
98
|
+
|
|
99
|
+
When >1 checkpoint proposals exist for same slot (count > 1): ACCEPT (re-broadcast). At count == 2 (exactly): `duplicateProposalCallback` fires. Proposal NOT further processed. Callback fires only once per equivocation pair.
|
|
100
|
+
|
|
101
|
+
### Stage 5: Validator-Client Consensus Validation
|
|
102
|
+
|
|
103
|
+
Determines whether the validator signs an attestation.
|
|
104
|
+
|
|
105
|
+
| Rule | Consequence | File |
|
|
106
|
+
|------|-------------|------|
|
|
107
|
+
| Escape hatch open | No attestation | `validator-client/src/validator.ts` |
|
|
108
|
+
| Signature invalid (re-check) | No attestation | same |
|
|
109
|
+
| Self-proposal | No attestation (ignored) | same |
|
|
110
|
+
| `feeAssetPriceModifier` outside [-100, +100] bps | No attestation | same |
|
|
111
|
+
| Not in committee (unless fisherman mode) | No attestation | same |
|
|
112
|
+
| Checkpoint header mismatch (computed vs proposal) | No attestation | same |
|
|
113
|
+
| Archive root mismatch | No attestation | same |
|
|
114
|
+
| Epoch out hash mismatch | No attestation | same |
|
|
115
|
+
| Last block not found / not matching | No attestation | same |
|
|
116
|
+
| Already attested to this or earlier slot | No attestation (unless `attestToEquivocatedProposals`) | same |
|
|
117
|
+
|
|
118
|
+
**`skipCheckpointProposalValidation` config**: when true, the re-execution checks (header/archive/epoch hash) are all skipped. Signature, fee modifier, committee, escape hatch, and equivocation checks still apply.
|
|
119
|
+
|
|
120
|
+
### Gossipsub Topic Scoring
|
|
121
|
+
|
|
122
|
+
P3 enabled with expected rate of 1 message per slot. P4 weight = -20, max P3 penalty = -34 per topic.
|
|
123
|
+
|
|
@@ -75,10 +75,12 @@ 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
|
|
78
|
+
- DoubleSpend, BlockHeader, GasLimits, Timestamp, AllowedSetupCalls
|
|
79
79
|
|
|
80
80
|
Operates on `TxMetaData` (pre-built by the pool) rather than full `Tx` objects.
|
|
81
81
|
|
|
82
|
+
The `AllowedSetupCallsMetaValidator` checks a precomputed boolean flag (`TxMetaData.allowedSetupCalls`) rather than re-running the full `PhasesTxValidator`. This flag is computed by `createCheckAllowedSetupCalls` when the tx first enters the pool (via `addProtectedTxs` or startup hydration), so the pool migration validator can reject txs with disallowed setup calls without needing the full `Tx` object or its dependencies.
|
|
83
|
+
|
|
82
84
|
## Individual Validators
|
|
83
85
|
|
|
84
86
|
| Validator | What it checks | Benchmarked verification duration |
|
|
@@ -92,6 +94,7 @@ Operates on `TxMetaData` (pre-built by the pool) rather than full `Tx` objects.
|
|
|
92
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 |
|
|
93
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 |
|
|
94
96
|
| `PhasesTxValidator` | Public function calls in setup phase are on the allow list | 10.12–13.12 us |
|
|
97
|
+
| `AllowedSetupCallsMetaValidator` | Checks the precomputed `allowedSetupCalls` flag on `TxMetaData`. Used in pool migration instead of the full `PhasesTxValidator` | — |
|
|
95
98
|
| `BlockHeaderTxValidator` | Transaction's anchor block hash exists in the archive tree | 98.88 us |
|
|
96
99
|
| `TxProofValidator` | Client proof verifies correctly | ~250ms |
|
|
97
100
|
|
|
@@ -108,6 +111,7 @@ Operates on `TxMetaData` (pre-built by the pool) rather than full `Tx` objects.
|
|
|
108
111
|
| Gas (balance + limits) | Stage 1 | Optional* | — | Yes | — |
|
|
109
112
|
| GasLimits (standalone) | — | — | — | — | Yes |
|
|
110
113
|
| Phases | Stage 1 | Yes | — | Yes | — |
|
|
114
|
+
| AllowedSetupCalls | — | — | — | — | Yes |
|
|
111
115
|
| BlockHeader | Stage 1 | Yes | — | Yes | Yes |
|
|
112
116
|
| Proof | Stage 2 | Optional** | Yes | — | — |
|
|
113
117
|
|
|
@@ -58,7 +58,7 @@ import { DoubleSpendTxValidator, type NullifierSource } from './double_spend_val
|
|
|
58
58
|
import { GasLimitsValidator, GasTxValidator } from './gas_validator.js';
|
|
59
59
|
import { MetadataTxValidator } from './metadata_validator.js';
|
|
60
60
|
import { NullifierCache } from './nullifier_cache.js';
|
|
61
|
-
import { PhasesTxValidator } from './phases_validator.js';
|
|
61
|
+
import { AllowedSetupCallsMetaValidator, PhasesTxValidator } from './phases_validator.js';
|
|
62
62
|
import { SizeTxValidator } from './size_validator.js';
|
|
63
63
|
import { TimestampTxValidator } from './timestamp_validator.js';
|
|
64
64
|
import { TxPermittedValidator } from './tx_permitted_validator.js';
|
|
@@ -97,6 +97,7 @@ export function createFirstStageTxValidationsForGossipedTransactions(
|
|
|
97
97
|
txsPermitted: boolean,
|
|
98
98
|
allowedInSetup: AllowedElement[] = [],
|
|
99
99
|
bindings?: LoggerBindings,
|
|
100
|
+
gasLimitOpts?: { rollupManaLimit?: number; maxBlockL2Gas?: number; maxBlockDAGas?: number },
|
|
100
101
|
): Record<string, TransactionValidator> {
|
|
101
102
|
const merkleTree = worldStateSynchronizer.getCommitted();
|
|
102
103
|
|
|
@@ -158,6 +159,7 @@ export function createFirstStageTxValidationsForGossipedTransactions(
|
|
|
158
159
|
ProtocolContractAddress.FeeJuice,
|
|
159
160
|
gasFees,
|
|
160
161
|
bindings,
|
|
162
|
+
gasLimitOpts,
|
|
161
163
|
),
|
|
162
164
|
severity: PeerErrorSeverity.MidToleranceError,
|
|
163
165
|
},
|
|
@@ -278,6 +280,9 @@ export function createTxValidatorForAcceptingTxsOverRPC(
|
|
|
278
280
|
timestamp,
|
|
279
281
|
blockNumber,
|
|
280
282
|
txsPermitted,
|
|
283
|
+
rollupManaLimit,
|
|
284
|
+
maxBlockL2Gas,
|
|
285
|
+
maxBlockDAGas,
|
|
281
286
|
}: {
|
|
282
287
|
l1ChainId: number;
|
|
283
288
|
rollupVersion: number;
|
|
@@ -287,6 +292,9 @@ export function createTxValidatorForAcceptingTxsOverRPC(
|
|
|
287
292
|
timestamp: UInt64;
|
|
288
293
|
blockNumber: BlockNumber;
|
|
289
294
|
txsPermitted: boolean;
|
|
295
|
+
rollupManaLimit: number;
|
|
296
|
+
maxBlockL2Gas?: number;
|
|
297
|
+
maxBlockDAGas?: number;
|
|
290
298
|
},
|
|
291
299
|
bindings?: LoggerBindings,
|
|
292
300
|
): TxValidator<Tx> {
|
|
@@ -317,7 +325,11 @@ export function createTxValidatorForAcceptingTxsOverRPC(
|
|
|
317
325
|
|
|
318
326
|
if (!skipFeeEnforcement) {
|
|
319
327
|
validators.push(
|
|
320
|
-
new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees, bindings
|
|
328
|
+
new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees, bindings, {
|
|
329
|
+
rollupManaLimit,
|
|
330
|
+
maxBlockL2Gas,
|
|
331
|
+
maxBlockDAGas,
|
|
332
|
+
}),
|
|
321
333
|
);
|
|
322
334
|
}
|
|
323
335
|
|
|
@@ -403,6 +415,7 @@ export async function createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
|
403
415
|
worldStateSynchronizer: WorldStateSynchronizer,
|
|
404
416
|
timestamp: bigint,
|
|
405
417
|
blockNumber: BlockNumber,
|
|
418
|
+
gasLimitOpts: { rollupManaLimit?: number; maxBlockL2Gas?: number; maxBlockDAGas?: number },
|
|
406
419
|
bindings?: LoggerBindings,
|
|
407
420
|
): Promise<TxValidator<TxMetaData>> {
|
|
408
421
|
await worldStateSynchronizer.syncImmediate();
|
|
@@ -419,9 +432,29 @@ export async function createTxValidatorForTransactionsEnteringPendingTxPool(
|
|
|
419
432
|
},
|
|
420
433
|
};
|
|
421
434
|
return new AggregateTxValidator<TxMetaData>(
|
|
422
|
-
new GasLimitsValidator<TxMetaData>(bindings),
|
|
435
|
+
new GasLimitsValidator<TxMetaData>({ ...gasLimitOpts, bindings }),
|
|
423
436
|
new TimestampTxValidator<TxMetaData>({ timestamp, blockNumber }, bindings),
|
|
424
437
|
new DoubleSpendTxValidator<TxMetaData>(nullifierSource, bindings),
|
|
425
438
|
new BlockHeaderTxValidator<TxMetaData>(archiveSource, bindings),
|
|
439
|
+
new AllowedSetupCallsMetaValidator<TxMetaData>(bindings),
|
|
426
440
|
);
|
|
427
441
|
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Creates a function that checks whether a tx's setup-phase calls are on the allow list.
|
|
445
|
+
*
|
|
446
|
+
* Uses the `PhasesTxValidator` on the full Tx. The result is stored as a boolean
|
|
447
|
+
* flag in `TxMetaData.allowedSetupCalls` at receipt time, so the pending pool
|
|
448
|
+
* migration validator can check it without needing the full Tx or its dependencies.
|
|
449
|
+
*/
|
|
450
|
+
export function createCheckAllowedSetupCalls(
|
|
451
|
+
contractDataSource: ContractDataSource,
|
|
452
|
+
setupAllowList: AllowedElement[],
|
|
453
|
+
getTimestamp: () => UInt64,
|
|
454
|
+
): (tx: Tx) => Promise<boolean> {
|
|
455
|
+
return async (tx: Tx) => {
|
|
456
|
+
const validator = new PhasesTxValidator(contractDataSource, setupAllowList, getTimestamp());
|
|
457
|
+
const result = await validator.validateTx(tx);
|
|
458
|
+
return result.result === 'valid';
|
|
459
|
+
};
|
|
460
|
+
}
|