@aztec/p2p 0.0.1-commit.358457c → 0.0.1-commit.381b1a9
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 +4 -5
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +12 -7
- package/dest/client/interface.d.ts +4 -4
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +4 -4
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +9 -27
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
- package/dest/config.d.ts +32 -11
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +85 -31
- package/dest/mem_pools/tx_pool/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_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +2 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +7 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +8 -6
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +30 -7
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +62 -16
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +9 -10
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_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 +38 -30
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +10 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +16 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -8
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +48 -36
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +24 -20
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
- package/dest/msg_validators/tx_validator/factory.d.ts +15 -4
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +21 -8
- 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/phases_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +44 -23
- package/dest/services/encoding.d.ts +2 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +7 -7
- package/dest/services/libp2p/libp2p_service.d.ts +6 -7
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +31 -23
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
- 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 +37 -14
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +11 -17
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +15 -49
- package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +1 -2
- package/dest/test-helpers/mock-pubsub.d.ts +2 -3
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +2 -2
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +2 -2
- package/dest/test-helpers/testbench-utils.d.ts +1 -1
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +2 -1
- package/dest/testbench/p2p_client_testbench_worker.js +7 -6
- package/dest/testbench/worker_client_manager.d.ts +3 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +4 -1
- package/package.json +14 -14
- package/src/client/factory.ts +15 -14
- package/src/client/interface.ts +3 -9
- package/src/client/p2p_client.ts +7 -38
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -2
- package/src/config.ts +124 -34
- package/src/mem_pools/tx_pool/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_pre_add_rule.ts +3 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +11 -1
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +15 -6
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +2 -1
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +3 -3
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +89 -17
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +43 -27
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +14 -4
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +20 -7
- package/src/msg_validators/proposal_validator/proposal_validator.ts +63 -40
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
- package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/factory.ts +22 -2
- 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/phases_validator.ts +51 -26
- package/src/services/encoding.ts +5 -6
- package/src/services/libp2p/libp2p_service.ts +30 -21
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +42 -14
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +24 -63
- package/src/test-helpers/make-test-p2p-clients.ts +0 -2
- package/src/test-helpers/mock-pubsub.ts +3 -6
- package/src/test-helpers/reqresp-nodes.ts +2 -5
- package/src/test-helpers/testbench-utils.ts +1 -0
- package/src/testbench/p2p_client_testbench_worker.ts +3 -6
- package/src/testbench/worker_client_manager.ts +11 -4
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
|
@@ -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
|
|
@@ -35,6 +35,7 @@ export class FeePayerBalancePreAddRule implements PreAddRule {
|
|
|
35
35
|
// Create combined list with incoming tx
|
|
36
36
|
const allTxs: Array<{
|
|
37
37
|
txHash: string;
|
|
38
|
+
txHashBigInt: bigint;
|
|
38
39
|
priorityFee: bigint;
|
|
39
40
|
feeLimit: bigint;
|
|
40
41
|
claimAmount: bigint;
|
|
@@ -42,6 +43,7 @@ export class FeePayerBalancePreAddRule implements PreAddRule {
|
|
|
42
43
|
}> = [
|
|
43
44
|
...existingTxs.map(t => ({
|
|
44
45
|
txHash: t.txHash,
|
|
46
|
+
txHashBigInt: t.txHashBigInt,
|
|
45
47
|
priorityFee: t.priorityFee,
|
|
46
48
|
feeLimit: t.feeLimit,
|
|
47
49
|
claimAmount: t.claimAmount,
|
|
@@ -49,6 +51,7 @@ export class FeePayerBalancePreAddRule implements PreAddRule {
|
|
|
49
51
|
})),
|
|
50
52
|
{
|
|
51
53
|
txHash: incomingMeta.txHash,
|
|
54
|
+
txHashBigInt: incomingMeta.txHashBigInt,
|
|
52
55
|
priorityFee: incomingMeta.priorityFee,
|
|
53
56
|
feeLimit: incomingMeta.feeLimit,
|
|
54
57
|
claimAmount: incomingMeta.claimAmount,
|
|
@@ -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) {
|
|
@@ -7,6 +7,6 @@ export {
|
|
|
7
7
|
type PoolReadAccess,
|
|
8
8
|
DEFAULT_TX_POOL_V2_CONFIG,
|
|
9
9
|
} from './interfaces.js';
|
|
10
|
-
export { type TxMetaData, type TxState, buildTxMetaData, comparePriority } from './tx_metadata.js';
|
|
10
|
+
export { type TxMetaData, type TxState, buildTxMetaData, comparePriority, stubTxMetaData } from './tx_metadata.js';
|
|
11
11
|
export { TxArchive } from './archive/index.js';
|
|
12
12
|
export { DeletedPool } from './deleted_pool.js';
|
|
@@ -44,8 +44,8 @@ export type TxPoolV2Config = {
|
|
|
44
44
|
minTxPoolAgeMs: number;
|
|
45
45
|
/** Maximum number of evicted tx hashes to remember for metrics tracking */
|
|
46
46
|
evictedTxCacheSize: number;
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/** Minimum percentage fee increase required to replace an existing tx via RPC (0 = no bump). */
|
|
48
|
+
priceBumpPercentage: bigint;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -56,7 +56,7 @@ export const DEFAULT_TX_POOL_V2_CONFIG: TxPoolV2Config = {
|
|
|
56
56
|
archivedTxLimit: 0, // 0 = disabled
|
|
57
57
|
minTxPoolAgeMs: 2_000,
|
|
58
58
|
evictedTxCacheSize: 10_000,
|
|
59
|
-
|
|
59
|
+
priceBumpPercentage: 10n,
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
3
3
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
4
4
|
import { BlockHash, type L2BlockId } from '@aztec/stdlib/block';
|
|
5
5
|
import { Gas } from '@aztec/stdlib/gas';
|
|
6
|
-
import type
|
|
6
|
+
import { type Tx, TxHash } from '@aztec/stdlib/tx';
|
|
7
7
|
|
|
8
8
|
import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
|
|
9
9
|
import { getTxPriorityFee } from '../tx_pool/priority.js';
|
|
@@ -40,6 +40,9 @@ export type TxMetaData = {
|
|
|
40
40
|
/** The transaction hash as hex string */
|
|
41
41
|
readonly txHash: string;
|
|
42
42
|
|
|
43
|
+
/** The transaction hash as bigint (for efficient Fr conversion in comparisons) */
|
|
44
|
+
readonly txHashBigInt: bigint;
|
|
45
|
+
|
|
43
46
|
/** Block ID (number and hash) in which the transaction was mined (undefined if not mined) */
|
|
44
47
|
minedL2BlockId?: L2BlockId;
|
|
45
48
|
|
|
@@ -83,7 +86,9 @@ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
|
|
|
83
86
|
* Fr values are captured in closures for zero-cost re-validation.
|
|
84
87
|
*/
|
|
85
88
|
export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
86
|
-
const
|
|
89
|
+
const txHashObj = tx.getTxHash();
|
|
90
|
+
const txHash = txHashObj.toString();
|
|
91
|
+
const txHashBigInt = txHashObj.toBigInt();
|
|
87
92
|
const nullifierFrs = tx.data.getNonEmptyNullifiers();
|
|
88
93
|
const nullifiers = nullifierFrs.map(n => n.toString());
|
|
89
94
|
const anchorBlockHeaderHashFr = await tx.data.constants.anchorBlockHeader.hash();
|
|
@@ -99,6 +104,7 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
99
104
|
|
|
100
105
|
return {
|
|
101
106
|
txHash,
|
|
107
|
+
txHashBigInt,
|
|
102
108
|
anchorBlockHeaderHash,
|
|
103
109
|
priorityFee,
|
|
104
110
|
feePayer,
|
|
@@ -134,11 +140,11 @@ const HEX_STRING_BYTES = 98;
|
|
|
134
140
|
const BIGINT_BYTES = 32;
|
|
135
141
|
const FR_BYTES = 80;
|
|
136
142
|
// Fixed cost: object shell + txHash + anchorBlockHeaderHash + feePayer (3 hex strings)
|
|
137
|
-
// + priorityFee + claimAmount + feeLimit + includeByTimestamp (
|
|
143
|
+
// + txHashBigInt + priorityFee + claimAmount + feeLimit + includeByTimestamp (5 bigints)
|
|
138
144
|
// + receivedAt (number, 8 bytes) + estimatedSizeBytes (number, 8 bytes)
|
|
139
145
|
// + data closure object (~OBJECT_OVERHEAD + anchorBlockHeaderHashFr Fr + anchorBlockNumber number)
|
|
140
146
|
const FIXED_METADATA_BYTES =
|
|
141
|
-
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES +
|
|
147
|
+
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES + 5 * BIGINT_BYTES + 8 + 8 + OBJECT_OVERHEAD + FR_BYTES + 8;
|
|
142
148
|
|
|
143
149
|
/** Estimates the in-memory size of a TxMetaData object based on the number of nullifiers. */
|
|
144
150
|
function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
@@ -146,8 +152,13 @@ function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
|
146
152
|
return FIXED_METADATA_BYTES + nullifierCount * (HEX_STRING_BYTES + FR_BYTES);
|
|
147
153
|
}
|
|
148
154
|
|
|
155
|
+
/** Converts a txHash bigint back to the canonical 0x-prefixed 64-char hex string. */
|
|
156
|
+
export function txHashFromBigInt(value: bigint): string {
|
|
157
|
+
return TxHash.fromBigInt(value).toString();
|
|
158
|
+
}
|
|
159
|
+
|
|
149
160
|
/** Minimal fields required for priority comparison. */
|
|
150
|
-
type PriorityComparable = Pick<TxMetaData, '
|
|
161
|
+
type PriorityComparable = Pick<TxMetaData, 'txHashBigInt' | 'priorityFee'>;
|
|
151
162
|
|
|
152
163
|
/**
|
|
153
164
|
* Compares two priority fees in ascending order.
|
|
@@ -162,10 +173,8 @@ export function compareFee(a: bigint, b: bigint): number {
|
|
|
162
173
|
* Uses field element comparison for deterministic ordering.
|
|
163
174
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
164
175
|
*/
|
|
165
|
-
export function compareTxHash(a:
|
|
166
|
-
|
|
167
|
-
const fieldB = Fr.fromHexString(b);
|
|
168
|
-
return fieldA.cmp(fieldB);
|
|
176
|
+
export function compareTxHash(a: bigint, b: bigint): number {
|
|
177
|
+
return Fr.cmpAsBigInt(a, b);
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
/**
|
|
@@ -178,24 +187,41 @@ export function comparePriority(a: PriorityComparable, b: PriorityComparable): n
|
|
|
178
187
|
if (feeComparison !== 0) {
|
|
179
188
|
return feeComparison;
|
|
180
189
|
}
|
|
181
|
-
return compareTxHash(a.
|
|
190
|
+
return compareTxHash(a.txHashBigInt, b.txHashBigInt);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Returns the minimum fee required to replace an existing tx with the given price bump percentage.
|
|
195
|
+
* Uses integer arithmetic: `existingFee + existingFee * priceBumpPercentage / 100`.
|
|
196
|
+
*/
|
|
197
|
+
export function getMinimumPriceBumpFee(existingFee: bigint, priceBumpPercentage: bigint): bigint {
|
|
198
|
+
const bump = (existingFee * priceBumpPercentage) / 100n;
|
|
199
|
+
// Ensure the minimum bump is at least 1, so that replacement always requires
|
|
200
|
+
// paying strictly more — even with 0% bump or zero existing fee.
|
|
201
|
+
const effectiveBump = bump > 0n ? bump : 1n;
|
|
202
|
+
return existingFee + effectiveBump;
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
/**
|
|
185
206
|
* Checks for nullifier conflicts between an incoming transaction and existing pool state.
|
|
186
207
|
*
|
|
187
208
|
* When the incoming tx shares nullifiers with existing pending txs:
|
|
188
|
-
* - If the incoming tx
|
|
189
|
-
* -
|
|
209
|
+
* - If the incoming tx meets or exceeds the required priority, mark conflicting txs for eviction
|
|
210
|
+
* - Otherwise, ignore the incoming tx
|
|
211
|
+
*
|
|
212
|
+
* When `priceBumpPercentage` is provided (RPC path), uses fee-only comparison with the
|
|
213
|
+
* percentage bump instead of `comparePriority`.
|
|
190
214
|
*
|
|
191
215
|
* @param incomingMeta - Metadata for the incoming transaction
|
|
192
216
|
* @param getTxHashByNullifier - Accessor to find which tx uses a nullifier
|
|
193
217
|
* @param getMetadata - Accessor to get metadata for a tx hash
|
|
218
|
+
* @param priceBumpPercentage - Optional percentage bump required for fee-based replacement
|
|
194
219
|
*/
|
|
195
220
|
export function checkNullifierConflict(
|
|
196
221
|
incomingMeta: TxMetaData,
|
|
197
222
|
getTxHashByNullifier: (nullifier: string) => string | undefined,
|
|
198
223
|
getMetadata: (txHash: string) => TxMetaData | undefined,
|
|
224
|
+
priceBumpPercentage?: bigint,
|
|
199
225
|
): PreAddResult {
|
|
200
226
|
const txHashesToEvict: string[] = [];
|
|
201
227
|
|
|
@@ -216,19 +242,32 @@ export function checkNullifierConflict(
|
|
|
216
242
|
continue;
|
|
217
243
|
}
|
|
218
244
|
|
|
219
|
-
//
|
|
220
|
-
// Otherwise,
|
|
221
|
-
|
|
222
|
-
|
|
245
|
+
// When price bump is set (RPC path), require the incoming fee to meet the bumped threshold.
|
|
246
|
+
// Otherwise (P2P path), use full comparePriority with tx hash tiebreaker.
|
|
247
|
+
const isHigherPriority =
|
|
248
|
+
priceBumpPercentage !== undefined
|
|
249
|
+
? incomingMeta.priorityFee >= getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
250
|
+
: comparePriority(incomingMeta, conflictingMeta) > 0;
|
|
251
|
+
|
|
252
|
+
if (isHigherPriority) {
|
|
223
253
|
txHashesToEvict.push(conflictingHashStr);
|
|
224
254
|
} else {
|
|
255
|
+
const minimumFee =
|
|
256
|
+
priceBumpPercentage !== undefined
|
|
257
|
+
? getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
258
|
+
: undefined;
|
|
225
259
|
return {
|
|
226
260
|
shouldIgnore: true,
|
|
227
261
|
txHashesToEvict: [],
|
|
228
262
|
reason: {
|
|
229
263
|
code: TxPoolRejectionCode.NULLIFIER_CONFLICT,
|
|
230
|
-
message:
|
|
264
|
+
message:
|
|
265
|
+
minimumFee !== undefined
|
|
266
|
+
? `Nullifier conflict with existing tx ${conflictingHashStr}. Minimum required fee: ${minimumFee}, got: ${incomingMeta.priorityFee}`
|
|
267
|
+
: `Nullifier conflict with existing tx ${conflictingHashStr}`,
|
|
231
268
|
conflictingTxHash: conflictingHashStr,
|
|
269
|
+
minimumPriceBumpFee: minimumFee,
|
|
270
|
+
txPriorityFee: minimumFee !== undefined ? incomingMeta.priorityFee : undefined,
|
|
232
271
|
},
|
|
233
272
|
};
|
|
234
273
|
}
|
|
@@ -253,3 +292,36 @@ export function stubTxMetaValidationData(overrides: { expirationTimestamp?: bigi
|
|
|
253
292
|
},
|
|
254
293
|
};
|
|
255
294
|
}
|
|
295
|
+
|
|
296
|
+
/** Creates a stub TxMetaData for tests. All fields have sensible defaults and can be overridden. */
|
|
297
|
+
export function stubTxMetaData(
|
|
298
|
+
txHash: string,
|
|
299
|
+
overrides: {
|
|
300
|
+
priorityFee?: bigint;
|
|
301
|
+
feePayer?: string;
|
|
302
|
+
claimAmount?: bigint;
|
|
303
|
+
feeLimit?: bigint;
|
|
304
|
+
nullifiers?: string[];
|
|
305
|
+
expirationTimestamp?: bigint;
|
|
306
|
+
anchorBlockHeaderHash?: string;
|
|
307
|
+
} = {},
|
|
308
|
+
): TxMetaData {
|
|
309
|
+
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
310
|
+
// Normalize to canonical zero-padded hex so txHashFromBigInt(txHashBigInt) === normalizedTxHash
|
|
311
|
+
const normalizedTxHash = txHashFromBigInt(txHashBigInt);
|
|
312
|
+
const expirationTimestamp = overrides.expirationTimestamp ?? 0n;
|
|
313
|
+
return {
|
|
314
|
+
txHash: normalizedTxHash,
|
|
315
|
+
txHashBigInt,
|
|
316
|
+
anchorBlockHeaderHash: overrides.anchorBlockHeaderHash ?? '0x1234',
|
|
317
|
+
priorityFee: overrides.priorityFee ?? 100n,
|
|
318
|
+
feePayer: overrides.feePayer ?? '0xfeepayer',
|
|
319
|
+
claimAmount: overrides.claimAmount ?? 0n,
|
|
320
|
+
feeLimit: overrides.feeLimit ?? 100n,
|
|
321
|
+
nullifiers: overrides.nullifiers ?? [`0x${normalizedTxHash.slice(2)}null1`],
|
|
322
|
+
expirationTimestamp,
|
|
323
|
+
receivedAt: 0,
|
|
324
|
+
estimatedSizeBytes: 0,
|
|
325
|
+
data: stubTxMetaValidationData({ expirationTimestamp }),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { L2BlockId } from '@aztec/stdlib/block';
|
|
3
3
|
|
|
4
|
-
import { type TxMetaData, type TxState, compareFee, compareTxHash } from './tx_metadata.js';
|
|
4
|
+
import { type TxMetaData, type TxState, compareFee, compareTxHash, txHashFromBigInt } from './tx_metadata.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Manages in-memory indices for the transaction pool.
|
|
@@ -22,8 +22,8 @@ export class TxPoolIndices {
|
|
|
22
22
|
#nullifierToTxHash: Map<string, string> = new Map();
|
|
23
23
|
/** Fee payer to txHashes index (pending txs only) */
|
|
24
24
|
#feePayerToTxHashes: Map<string, Set<string>> = new Map();
|
|
25
|
-
/** Pending
|
|
26
|
-
#pendingByPriority: Map<bigint, Set<
|
|
25
|
+
/** Pending txHash bigints grouped by priority fee */
|
|
26
|
+
#pendingByPriority: Map<bigint, Set<bigint>> = new Map();
|
|
27
27
|
/** Protected transactions: txHash -> slotNumber */
|
|
28
28
|
#protectedTransactions: Map<string, SlotNumber> = new Map();
|
|
29
29
|
|
|
@@ -73,17 +73,17 @@ export class TxPoolIndices {
|
|
|
73
73
|
* @param order - 'desc' for highest priority first, 'asc' for lowest priority first
|
|
74
74
|
*/
|
|
75
75
|
*iteratePendingByPriority(order: 'asc' | 'desc', filter?: (hash: string) => boolean): Generator<string> {
|
|
76
|
-
// Use compareFee from tx_metadata, swap args for descending order
|
|
77
76
|
const feeCompareFn = order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : compareFee;
|
|
78
|
-
const hashCompareFn =
|
|
77
|
+
const hashCompareFn =
|
|
78
|
+
order === 'desc' ? (a: bigint, b: bigint) => compareTxHash(b, a) : (a: bigint, b: bigint) => compareTxHash(a, b);
|
|
79
79
|
|
|
80
80
|
const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
|
|
81
81
|
|
|
82
82
|
for (const fee of sortedFees) {
|
|
83
83
|
const hashesAtFee = this.#pendingByPriority.get(fee)!;
|
|
84
|
-
// Use compareTxHash from tx_metadata, swap args for descending order
|
|
85
84
|
const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
|
|
86
|
-
for (const
|
|
85
|
+
for (const hashBigInt of sortedHashes) {
|
|
86
|
+
const hash = txHashFromBigInt(hashBigInt);
|
|
87
87
|
if (filter === undefined || filter(hash)) {
|
|
88
88
|
yield hash;
|
|
89
89
|
}
|
|
@@ -265,8 +265,8 @@ export class TxPoolIndices {
|
|
|
265
265
|
getPendingTxs(): TxMetaData[] {
|
|
266
266
|
const result: TxMetaData[] = [];
|
|
267
267
|
for (const hashSet of this.#pendingByPriority.values()) {
|
|
268
|
-
for (const
|
|
269
|
-
const meta = this.#metadata.get(
|
|
268
|
+
for (const txHashBigInt of hashSet) {
|
|
269
|
+
const meta = this.#metadata.get(txHashFromBigInt(txHashBigInt));
|
|
270
270
|
if (meta) {
|
|
271
271
|
result.push(meta);
|
|
272
272
|
}
|
|
@@ -414,7 +414,7 @@ export class TxPoolIndices {
|
|
|
414
414
|
prioritySet = new Set();
|
|
415
415
|
this.#pendingByPriority.set(meta.priorityFee, prioritySet);
|
|
416
416
|
}
|
|
417
|
-
prioritySet.add(meta.
|
|
417
|
+
prioritySet.add(meta.txHashBigInt);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
#removeFromPendingIndices(meta: TxMetaData): void {
|
|
@@ -435,7 +435,7 @@ export class TxPoolIndices {
|
|
|
435
435
|
// Remove from priority map
|
|
436
436
|
const hashSet = this.#pendingByPriority.get(meta.priorityFee);
|
|
437
437
|
if (hashSet) {
|
|
438
|
-
hashSet.delete(meta.
|
|
438
|
+
hashSet.delete(meta.txHashBigInt);
|
|
439
439
|
if (hashSet.size === 0) {
|
|
440
440
|
this.#pendingByPriority.delete(meta.priorityFee);
|
|
441
441
|
}
|
|
@@ -187,9 +187,35 @@ export class TxPoolV2Impl {
|
|
|
187
187
|
const errors = new Map<string, TxPoolRejectionError>();
|
|
188
188
|
const acceptedPending = new Set<string>();
|
|
189
189
|
|
|
190
|
+
// Phase 1: Pre-compute all throwable I/O outside the transaction.
|
|
191
|
+
// If any pre-computation throws, the entire call fails before mutations happen.
|
|
192
|
+
const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
|
|
193
|
+
|
|
194
|
+
const validator = await this.#createTxValidator();
|
|
195
|
+
|
|
196
|
+
for (const tx of txs) {
|
|
197
|
+
const txHash = tx.getTxHash();
|
|
198
|
+
const txHashStr = txHash.toString();
|
|
199
|
+
|
|
200
|
+
const meta = await buildTxMetaData(tx);
|
|
201
|
+
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
202
|
+
|
|
203
|
+
// Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
|
|
204
|
+
let isValid = true;
|
|
205
|
+
if (!minedBlockId) {
|
|
206
|
+
isValid = await this.#validateMeta(meta, validator);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
precomputed.set(txHashStr, { meta, minedBlockId, isValid });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Phase 2: Apply mutations inside the transaction using only pre-computed results,
|
|
213
|
+
// in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
|
|
190
214
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
191
215
|
const preAddContext: PreAddContext | undefined =
|
|
192
|
-
opts.feeComparisonOnly !== undefined
|
|
216
|
+
opts.feeComparisonOnly !== undefined
|
|
217
|
+
? { feeComparisonOnly: opts.feeComparisonOnly, priceBumpPercentage: this.#config.priceBumpPercentage }
|
|
218
|
+
: undefined;
|
|
193
219
|
|
|
194
220
|
await this.#store.transactionAsync(async () => {
|
|
195
221
|
for (const tx of txs) {
|
|
@@ -202,22 +228,25 @@ export class TxPoolV2Impl {
|
|
|
202
228
|
continue;
|
|
203
229
|
}
|
|
204
230
|
|
|
205
|
-
|
|
206
|
-
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
231
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
207
232
|
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
208
233
|
|
|
209
234
|
if (minedBlockId) {
|
|
210
235
|
// Already mined - add directly (protection already set if pre-protected)
|
|
211
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
236
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
212
237
|
accepted.push(txHash);
|
|
213
238
|
} else if (preProtectedSlot !== undefined) {
|
|
214
239
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
215
|
-
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
240
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
216
241
|
accepted.push(txHash);
|
|
242
|
+
} else if (!isValid) {
|
|
243
|
+
// Failed pre-computed validation
|
|
244
|
+
rejected.push(txHash);
|
|
217
245
|
} else {
|
|
218
|
-
// Regular pending tx -
|
|
246
|
+
// Regular pending tx - run pre-add rules using pre-computed metadata
|
|
219
247
|
const result = await this.#tryAddRegularPendingTx(
|
|
220
248
|
tx,
|
|
249
|
+
meta,
|
|
221
250
|
opts,
|
|
222
251
|
poolAccess,
|
|
223
252
|
acceptedPending,
|
|
@@ -227,8 +256,6 @@ export class TxPoolV2Impl {
|
|
|
227
256
|
);
|
|
228
257
|
if (result.status === 'accepted') {
|
|
229
258
|
acceptedPending.add(txHashStr);
|
|
230
|
-
} else if (result.status === 'rejected') {
|
|
231
|
-
rejected.push(txHash);
|
|
232
259
|
} else {
|
|
233
260
|
ignored.push(txHash);
|
|
234
261
|
}
|
|
@@ -259,27 +286,21 @@ export class TxPoolV2Impl {
|
|
|
259
286
|
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
260
287
|
}
|
|
261
288
|
|
|
262
|
-
/**
|
|
289
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
263
290
|
async #tryAddRegularPendingTx(
|
|
264
291
|
tx: Tx,
|
|
292
|
+
precomputedMeta: TxMetaData,
|
|
265
293
|
opts: { source?: string },
|
|
266
294
|
poolAccess: PreAddPoolAccess,
|
|
267
295
|
acceptedPending: Set<string>,
|
|
268
296
|
ignored: TxHash[],
|
|
269
297
|
errors: Map<string, TxPoolRejectionError>,
|
|
270
298
|
preAddContext?: PreAddContext,
|
|
271
|
-
): Promise<{ status: 'accepted' | 'ignored'
|
|
272
|
-
const
|
|
273
|
-
const txHashStr = txHash.toString();
|
|
274
|
-
|
|
275
|
-
// Build metadata and validate using metadata
|
|
276
|
-
const meta = await buildTxMetaData(tx);
|
|
277
|
-
if (!(await this.#validateMeta(meta))) {
|
|
278
|
-
return { status: 'rejected' };
|
|
279
|
-
}
|
|
299
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
300
|
+
const txHashStr = tx.getTxHash().toString();
|
|
280
301
|
|
|
281
302
|
// Run pre-add rules
|
|
282
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(
|
|
303
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
283
304
|
|
|
284
305
|
if (preAddResult.shouldIgnore) {
|
|
285
306
|
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
|
|
@@ -316,14 +337,8 @@ export class TxPoolV2Impl {
|
|
|
316
337
|
}
|
|
317
338
|
}
|
|
318
339
|
|
|
319
|
-
// Randomly drop the transaction for testing purposes (report as accepted so it propagates)
|
|
320
|
-
if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
|
|
321
|
-
this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
|
|
322
|
-
return { status: 'accepted' };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
340
|
// Add the transaction
|
|
326
|
-
await this.#addTx(tx, 'pending', opts);
|
|
341
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
327
342
|
return { status: 'accepted' };
|
|
328
343
|
}
|
|
329
344
|
|
|
@@ -765,9 +780,10 @@ export class TxPoolV2Impl {
|
|
|
765
780
|
tx: Tx,
|
|
766
781
|
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
767
782
|
opts: { source?: string } = {},
|
|
783
|
+
precomputedMeta?: TxMetaData,
|
|
768
784
|
): Promise<TxMetaData> {
|
|
769
785
|
const txHashStr = tx.getTxHash().toString();
|
|
770
|
-
const meta = await buildTxMetaData(tx);
|
|
786
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
771
787
|
meta.receivedAt = this.#dateProvider.now();
|
|
772
788
|
|
|
773
789
|
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import type { BlockProposal, P2PValidator } from '@aztec/stdlib/p2p';
|
|
2
|
+
import type { BlockProposal, P2PValidator, ValidationResult } from '@aztec/stdlib/p2p';
|
|
3
3
|
|
|
4
4
|
import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
5
5
|
|
|
6
|
-
export class BlockProposalValidator
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
export class BlockProposalValidator implements P2PValidator<BlockProposal> {
|
|
7
|
+
private proposalValidator: ProposalValidator;
|
|
8
|
+
|
|
9
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
|
|
10
|
+
this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:block_proposal_validator');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async validate(proposal: BlockProposal): Promise<ValidationResult> {
|
|
14
|
+
const headerResult = await this.proposalValidator.validate(proposal);
|
|
15
|
+
if (headerResult.result !== 'accept') {
|
|
16
|
+
return headerResult;
|
|
17
|
+
}
|
|
18
|
+
return this.proposalValidator.validateTxs(proposal);
|
|
9
19
|
}
|
|
10
20
|
}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import type { CheckpointProposal, P2PValidator } from '@aztec/stdlib/p2p';
|
|
2
|
+
import type { CheckpointProposal, P2PValidator, ValidationResult } from '@aztec/stdlib/p2p';
|
|
3
3
|
|
|
4
4
|
import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
5
5
|
|
|
6
|
-
export class CheckpointProposalValidator
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export class CheckpointProposalValidator implements P2PValidator<CheckpointProposal> {
|
|
7
|
+
private proposalValidator: ProposalValidator;
|
|
8
|
+
|
|
9
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
|
|
10
|
+
this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:checkpoint_proposal_validator');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async validate(proposal: CheckpointProposal): Promise<ValidationResult> {
|
|
14
|
+
const headerResult = await this.proposalValidator.validate(proposal);
|
|
15
|
+
if (headerResult.result !== 'accept') {
|
|
16
|
+
return headerResult;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const blockProposal = proposal.getBlockProposal();
|
|
20
|
+
if (blockProposal) {
|
|
21
|
+
return this.proposalValidator.validateTxs(blockProposal);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { result: 'accept' };
|
|
12
25
|
}
|
|
13
26
|
}
|