@aztec/p2p 0.0.1-commit.ff7989d6c → 0.0.1-commit.ffe5b04ea

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.
Files changed (191) hide show
  1. package/dest/client/factory.d.ts +5 -6
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +15 -26
  4. package/dest/client/interface.d.ts +6 -13
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +5 -13
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +3 -82
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
  10. package/dest/config.d.ts +20 -11
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +66 -32
  13. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +1 -1
  14. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  15. package/dest/mem_pools/attestation_pool/attestation_pool.js +5 -1
  16. package/dest/mem_pools/instrumentation.d.ts +4 -2
  17. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  18. package/dest/mem_pools/instrumentation.js +16 -14
  19. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
  20. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
  21. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
  22. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +2 -0
  23. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +7 -1
  24. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
  25. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
  26. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +1 -1
  27. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
  28. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +8 -6
  29. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +2 -2
  30. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
  31. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +2 -2
  32. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
  33. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  34. package/dest/mem_pools/tx_pool_v2/index.js +1 -1
  35. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +7 -5
  36. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  37. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  38. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +40 -10
  39. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  40. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +74 -16
  41. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
  42. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +26 -44
  44. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +2 -2
  45. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  46. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +3 -0
  47. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +3 -2
  48. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  49. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +42 -29
  50. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +6 -4
  51. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  52. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +10 -2
  53. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +6 -4
  54. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  55. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +16 -2
  56. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -8
  57. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  58. package/dest/msg_validators/proposal_validator/proposal_validator.js +48 -36
  59. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +2 -2
  60. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  61. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
  62. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  63. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  64. package/dest/msg_validators/tx_validator/allowed_public_setup.js +24 -20
  65. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
  66. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
  67. package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
  68. package/dest/msg_validators/tx_validator/factory.d.ts +114 -6
  69. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  70. package/dest/msg_validators/tx_validator/factory.js +219 -58
  71. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
  72. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
  73. package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
  74. package/dest/msg_validators/tx_validator/gas_validator.d.ts +58 -3
  75. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  76. package/dest/msg_validators/tx_validator/gas_validator.js +73 -36
  77. package/dest/msg_validators/tx_validator/index.d.ts +3 -1
  78. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  79. package/dest/msg_validators/tx_validator/index.js +2 -0
  80. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +1 -1
  81. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  82. package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
  83. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  84. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  85. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  86. package/dest/msg_validators/tx_validator/phases_validator.d.ts +2 -2
  87. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  88. package/dest/msg_validators/tx_validator/phases_validator.js +44 -23
  89. package/dest/services/dummy_service.d.ts +2 -3
  90. package/dest/services/dummy_service.d.ts.map +1 -1
  91. package/dest/services/dummy_service.js +1 -4
  92. package/dest/services/encoding.d.ts +2 -2
  93. package/dest/services/encoding.d.ts.map +1 -1
  94. package/dest/services/encoding.js +7 -7
  95. package/dest/services/libp2p/libp2p_service.d.ts +15 -13
  96. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  97. package/dest/services/libp2p/libp2p_service.js +79 -89
  98. package/dest/services/peer-manager/metrics.d.ts +3 -1
  99. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  100. package/dest/services/peer-manager/metrics.js +6 -0
  101. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  102. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  103. package/dest/services/peer-manager/peer_manager.js +2 -1
  104. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
  105. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  106. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +14 -37
  107. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +17 -11
  108. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
  109. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +49 -15
  110. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
  111. package/dest/services/reqresp/reqresp.d.ts +1 -1
  112. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  113. package/dest/services/reqresp/reqresp.js +2 -1
  114. package/dest/services/service.d.ts +2 -2
  115. package/dest/services/service.d.ts.map +1 -1
  116. package/dest/services/tx_provider.d.ts +3 -3
  117. package/dest/services/tx_provider.d.ts.map +1 -1
  118. package/dest/services/tx_provider.js +4 -4
  119. package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
  120. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  121. package/dest/test-helpers/make-test-p2p-clients.js +1 -2
  122. package/dest/test-helpers/mock-pubsub.d.ts +2 -3
  123. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  124. package/dest/test-helpers/mock-pubsub.js +2 -2
  125. package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
  126. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  127. package/dest/test-helpers/reqresp-nodes.js +2 -2
  128. package/dest/test-helpers/testbench-utils.d.ts +2 -2
  129. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  130. package/dest/testbench/p2p_client_testbench_worker.js +7 -6
  131. package/dest/testbench/worker_client_manager.d.ts +3 -1
  132. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  133. package/dest/testbench/worker_client_manager.js +6 -2
  134. package/dest/util.d.ts +1 -1
  135. package/package.json +14 -14
  136. package/src/client/factory.ts +23 -47
  137. package/src/client/interface.ts +5 -19
  138. package/src/client/p2p_client.ts +4 -110
  139. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +2 -3
  140. package/src/config.ts +91 -34
  141. package/src/mem_pools/attestation_pool/attestation_pool.ts +5 -4
  142. package/src/mem_pools/instrumentation.ts +17 -13
  143. package/src/mem_pools/tx_pool_v2/README.md +9 -1
  144. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
  145. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
  146. package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +11 -1
  147. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +2 -2
  148. package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +15 -6
  149. package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +2 -1
  150. package/src/mem_pools/tx_pool_v2/index.ts +1 -1
  151. package/src/mem_pools/tx_pool_v2/interfaces.ts +7 -4
  152. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +104 -19
  153. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +29 -43
  154. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +4 -1
  155. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +50 -29
  156. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +14 -4
  157. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +20 -7
  158. package/src/msg_validators/proposal_validator/proposal_validator.ts +63 -40
  159. package/src/msg_validators/tx_validator/README.md +115 -0
  160. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -3
  161. package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
  162. package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
  163. package/src/msg_validators/tx_validator/factory.ts +353 -77
  164. package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
  165. package/src/msg_validators/tx_validator/gas_validator.ts +90 -27
  166. package/src/msg_validators/tx_validator/index.ts +2 -0
  167. package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
  168. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  169. package/src/msg_validators/tx_validator/phases_validator.ts +51 -26
  170. package/src/services/dummy_service.ts +1 -5
  171. package/src/services/encoding.ts +5 -6
  172. package/src/services/libp2p/libp2p_service.ts +90 -96
  173. package/src/services/peer-manager/metrics.ts +7 -0
  174. package/src/services/peer-manager/peer_manager.ts +2 -1
  175. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +14 -42
  176. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +63 -24
  177. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
  178. package/src/services/reqresp/reqresp.ts +3 -1
  179. package/src/services/service.ts +1 -1
  180. package/src/services/tx_provider.ts +2 -2
  181. package/src/test-helpers/make-test-p2p-clients.ts +1 -3
  182. package/src/test-helpers/mock-pubsub.ts +3 -6
  183. package/src/test-helpers/reqresp-nodes.ts +3 -6
  184. package/src/test-helpers/testbench-utils.ts +1 -1
  185. package/src/testbench/p2p_client_testbench_worker.ts +4 -7
  186. package/src/testbench/worker_client_manager.ts +13 -5
  187. package/src/util.ts +1 -1
  188. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
  189. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
  190. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
  191. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
@@ -46,6 +46,8 @@ export type TxPoolV2Config = {
46
46
  evictedTxCacheSize: number;
47
47
  /** The probability (0-1) that a transaction is discarded. 0 disables dropping. For testing purposes only. */
48
48
  dropTransactionsProbability: number;
49
+ /** Minimum percentage fee increase required to replace an existing tx via RPC (0 = no bump). */
50
+ priceBumpPercentage: bigint;
49
51
  };
50
52
 
51
53
  /**
@@ -57,6 +59,7 @@ export const DEFAULT_TX_POOL_V2_CONFIG: TxPoolV2Config = {
57
59
  minTxPoolAgeMs: 2_000,
58
60
  evictedTxCacheSize: 10_000,
59
61
  dropTransactionsProbability: 0,
62
+ priceBumpPercentage: 10n,
60
63
  };
61
64
 
62
65
  /**
@@ -110,12 +113,12 @@ export interface TxPoolV2 extends TypedEventEmitter<TxPoolV2Events> {
110
113
  addPendingTxs(txs: Tx[], opts?: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult>;
111
114
 
112
115
  /**
113
- * Checks if a transaction can be added without modifying the pool.
114
- * Performs the same validation as addPendingTxs but doesn't persist changes.
116
+ * Checks if the pool would accept a transaction without modifying state.
117
+ * Used as a pre-check before expensive proof verification.
115
118
  * @param tx - Transaction to check
116
- * @returns Result: 'accepted', 'ignored' (if already in pool or undesirable), or 'rejected' (if validation fails)
119
+ * @returns 'accepted' if the pool would accept, 'ignored' if already in pool or undesirable
117
120
  */
118
- canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored' | 'rejected'>;
121
+ canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'>;
119
122
 
120
123
  /**
121
124
  * Adds transactions as immediately protected for a given slot.
@@ -2,7 +2,8 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
4
4
  import { BlockHash, type L2BlockId } from '@aztec/stdlib/block';
5
- import type { Tx } from '@aztec/stdlib/tx';
5
+ import { Gas } from '@aztec/stdlib/gas';
6
+ import { type Tx, TxHash } from '@aztec/stdlib/tx';
6
7
 
7
8
  import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
8
9
  import { getTxPriorityFee } from '../tx_pool/priority.js';
@@ -12,6 +13,8 @@ import { type PreAddResult, TxPoolRejectionCode } from './eviction/interfaces.js
12
13
  export type TxMetaValidationData = {
13
14
  getNonEmptyNullifiers(): Fr[];
14
15
  expirationTimestamp: bigint;
16
+ /** Whether the tx has public calls. Used to select the correct L2 gas minimum. */
17
+ forPublic?: unknown;
15
18
  constants: {
16
19
  anchorBlockHeader: {
17
20
  hash(): Promise<BlockHash>;
@@ -19,6 +22,9 @@ export type TxMetaValidationData = {
19
22
  blockNumber: BlockNumber;
20
23
  };
21
24
  };
25
+ txContext: {
26
+ gasSettings: { gasLimits: Gas };
27
+ };
22
28
  };
23
29
  };
24
30
 
@@ -34,6 +40,9 @@ export type TxMetaData = {
34
40
  /** The transaction hash as hex string */
35
41
  readonly txHash: string;
36
42
 
43
+ /** The transaction hash as bigint (for efficient Fr conversion in comparisons) */
44
+ readonly txHashBigInt: bigint;
45
+
37
46
  /** Block ID (number and hash) in which the transaction was mined (undefined if not mined) */
38
47
  minedL2BlockId?: L2BlockId;
39
48
 
@@ -77,7 +86,9 @@ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
77
86
  * Fr values are captured in closures for zero-cost re-validation.
78
87
  */
79
88
  export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
80
- const txHash = tx.getTxHash().toString();
89
+ const txHashObj = tx.getTxHash();
90
+ const txHash = txHashObj.toString();
91
+ const txHashBigInt = txHashObj.toBigInt();
81
92
  const nullifierFrs = tx.data.getNonEmptyNullifiers();
82
93
  const nullifiers = nullifierFrs.map(n => n.toString());
83
94
  const anchorBlockHeaderHashFr = await tx.data.constants.anchorBlockHeader.hash();
@@ -93,6 +104,7 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
93
104
 
94
105
  return {
95
106
  txHash,
107
+ txHashBigInt,
96
108
  anchorBlockHeaderHash,
97
109
  priorityFee,
98
110
  feePayer,
@@ -105,11 +117,15 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
105
117
  data: {
106
118
  getNonEmptyNullifiers: () => nullifierFrs,
107
119
  expirationTimestamp,
120
+ forPublic: !!tx.data.forPublic,
108
121
  constants: {
109
122
  anchorBlockHeader: {
110
123
  hash: () => Promise.resolve(anchorBlockHeaderHashFr),
111
124
  globalVariables: { blockNumber: anchorBlockNumber },
112
125
  },
126
+ txContext: {
127
+ gasSettings: { gasLimits: tx.data.constants.txContext.gasSettings.gasLimits },
128
+ },
113
129
  },
114
130
  },
115
131
  };
@@ -124,11 +140,11 @@ const HEX_STRING_BYTES = 98;
124
140
  const BIGINT_BYTES = 32;
125
141
  const FR_BYTES = 80;
126
142
  // Fixed cost: object shell + txHash + anchorBlockHeaderHash + feePayer (3 hex strings)
127
- // + priorityFee + claimAmount + feeLimit + includeByTimestamp (4 bigints)
143
+ // + txHashBigInt + priorityFee + claimAmount + feeLimit + includeByTimestamp (5 bigints)
128
144
  // + receivedAt (number, 8 bytes) + estimatedSizeBytes (number, 8 bytes)
129
145
  // + data closure object (~OBJECT_OVERHEAD + anchorBlockHeaderHashFr Fr + anchorBlockNumber number)
130
146
  const FIXED_METADATA_BYTES =
131
- OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES + 4 * BIGINT_BYTES + 8 + 8 + OBJECT_OVERHEAD + FR_BYTES + 8;
147
+ OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES + 5 * BIGINT_BYTES + 8 + 8 + OBJECT_OVERHEAD + FR_BYTES + 8;
132
148
 
133
149
  /** Estimates the in-memory size of a TxMetaData object based on the number of nullifiers. */
134
150
  function estimateTxMetaDataSize(nullifierCount: number): number {
@@ -136,14 +152,19 @@ function estimateTxMetaDataSize(nullifierCount: number): number {
136
152
  return FIXED_METADATA_BYTES + nullifierCount * (HEX_STRING_BYTES + FR_BYTES);
137
153
  }
138
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
+
139
160
  /** Minimal fields required for priority comparison. */
140
- type PriorityComparable = Pick<TxMetaData, 'txHash' | 'priorityFee'>;
161
+ export type PriorityComparable = Pick<TxMetaData, 'txHash' | 'txHashBigInt' | 'priorityFee'>;
141
162
 
142
163
  /**
143
164
  * Compares two priority fees in ascending order.
144
165
  * Returns negative if a < b, positive if a > b, 0 if equal.
145
166
  */
146
- export function compareFee(a: bigint, b: bigint): number {
167
+ export function compareFee(a: bigint, b: bigint): -1 | 0 | 1 {
147
168
  return a < b ? -1 : a > b ? 1 : 0;
148
169
  }
149
170
 
@@ -152,10 +173,8 @@ export function compareFee(a: bigint, b: bigint): number {
152
173
  * Uses field element comparison for deterministic ordering.
153
174
  * Returns negative if a < b, positive if a > b, 0 if equal.
154
175
  */
155
- export function compareTxHash(a: string, b: string): number {
156
- const fieldA = Fr.fromHexString(a);
157
- const fieldB = Fr.fromHexString(b);
158
- return fieldA.cmp(fieldB);
176
+ export function compareTxHash(a: bigint, b: bigint): -1 | 0 | 1 {
177
+ return Fr.cmpAsBigInt(a, b);
159
178
  }
160
179
 
161
180
  /**
@@ -163,29 +182,46 @@ export function compareTxHash(a: string, b: string): number {
163
182
  * Returns negative if a < b, positive if a > b, 0 if equal.
164
183
  * Use with sort() for ascending order, or negate/reverse for descending.
165
184
  */
166
- export function comparePriority(a: PriorityComparable, b: PriorityComparable): number {
185
+ export function comparePriority(a: PriorityComparable, b: PriorityComparable): -1 | 0 | 1 {
167
186
  const feeComparison = compareFee(a.priorityFee, b.priorityFee);
168
187
  if (feeComparison !== 0) {
169
188
  return feeComparison;
170
189
  }
171
- return compareTxHash(a.txHash, b.txHash);
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;
172
203
  }
173
204
 
174
205
  /**
175
206
  * Checks for nullifier conflicts between an incoming transaction and existing pool state.
176
207
  *
177
208
  * When the incoming tx shares nullifiers with existing pending txs:
178
- * - If the incoming tx has strictly higher priority, mark conflicting txs for eviction
179
- * - If any conflicting tx has equal or higher priority, ignore the incoming tx
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`.
180
214
  *
181
215
  * @param incomingMeta - Metadata for the incoming transaction
182
216
  * @param getTxHashByNullifier - Accessor to find which tx uses a nullifier
183
217
  * @param getMetadata - Accessor to get metadata for a tx hash
218
+ * @param priceBumpPercentage - Optional percentage bump required for fee-based replacement
184
219
  */
185
220
  export function checkNullifierConflict(
186
221
  incomingMeta: TxMetaData,
187
222
  getTxHashByNullifier: (nullifier: string) => string | undefined,
188
223
  getMetadata: (txHash: string) => TxMetaData | undefined,
224
+ priceBumpPercentage?: bigint,
189
225
  ): PreAddResult {
190
226
  const txHashesToEvict: string[] = [];
191
227
 
@@ -206,19 +242,32 @@ export function checkNullifierConflict(
206
242
  continue;
207
243
  }
208
244
 
209
- // If incoming tx has strictly higher priority, mark for eviction
210
- // Otherwise, ignore incoming tx (ties go to existing tx)
211
- // Use comparePriority for deterministic ordering (includes txHash as tiebreaker)
212
- if (comparePriority(incomingMeta, conflictingMeta) > 0) {
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) {
213
253
  txHashesToEvict.push(conflictingHashStr);
214
254
  } else {
255
+ const minimumFee =
256
+ priceBumpPercentage !== undefined
257
+ ? getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
258
+ : undefined;
215
259
  return {
216
260
  shouldIgnore: true,
217
261
  txHashesToEvict: [],
218
262
  reason: {
219
263
  code: TxPoolRejectionCode.NULLIFIER_CONFLICT,
220
- message: `Nullifier conflict with existing tx ${conflictingHashStr}`,
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}`,
221
268
  conflictingTxHash: conflictingHashStr,
269
+ minimumPriceBumpFee: minimumFee,
270
+ txPriorityFee: minimumFee !== undefined ? incomingMeta.priorityFee : undefined,
222
271
  },
223
272
  };
224
273
  }
@@ -237,6 +286,42 @@ export function stubTxMetaValidationData(overrides: { expirationTimestamp?: bigi
237
286
  hash: () => Promise.resolve(new BlockHash(Fr.ZERO)),
238
287
  globalVariables: { blockNumber: BlockNumber(0) },
239
288
  },
289
+ txContext: {
290
+ gasSettings: { gasLimits: Gas.empty() },
291
+ },
240
292
  },
241
293
  };
242
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,8 @@
1
+ import { insertIntoSortedArray, removeFromSortedArray } from '@aztec/foundation/array';
1
2
  import { SlotNumber } from '@aztec/foundation/branded-types';
2
3
  import type { L2BlockId } from '@aztec/stdlib/block';
3
4
 
4
- import { type TxMetaData, type TxState, compareFee, compareTxHash } from './tx_metadata.js';
5
+ import { type PriorityComparable, type TxMetaData, type TxState, comparePriority } from './tx_metadata.js';
5
6
 
6
7
  /**
7
8
  * Manages in-memory indices for the transaction pool.
@@ -22,8 +23,8 @@ export class TxPoolIndices {
22
23
  #nullifierToTxHash: Map<string, string> = new Map();
23
24
  /** Fee payer to txHashes index (pending txs only) */
24
25
  #feePayerToTxHashes: Map<string, Set<string>> = new Map();
25
- /** Pending txHashes grouped by priority fee */
26
- #pendingByPriority: Map<bigint, Set<string>> = new Map();
26
+ /** Pending transactions sorted ascending by priority fee, ties broken by txHash */
27
+ #pendingByPriority: PriorityComparable[] = [];
27
28
  /** Protected transactions: txHash -> slotNumber */
28
29
  #protectedTransactions: Map<string, SlotNumber> = new Map();
29
30
 
@@ -73,20 +74,14 @@ export class TxPoolIndices {
73
74
  * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
74
75
  */
75
76
  *iteratePendingByPriority(order: 'asc' | 'desc', filter?: (hash: string) => boolean): Generator<string> {
76
- // Use compareFee from tx_metadata, swap args for descending order
77
- const feeCompareFn = order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : compareFee;
78
- const hashCompareFn = order === 'desc' ? (a: string, b: string) => compareTxHash(b, a) : compareTxHash;
79
-
80
- const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
81
-
82
- for (const fee of sortedFees) {
83
- const hashesAtFee = this.#pendingByPriority.get(fee)!;
84
- // Use compareTxHash from tx_metadata, swap args for descending order
85
- const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
86
- for (const hash of sortedHashes) {
87
- if (filter === undefined || filter(hash)) {
88
- yield hash;
89
- }
77
+ const arr = this.#pendingByPriority;
78
+ const start = order === 'asc' ? 0 : arr.length - 1;
79
+ const step = order === 'asc' ? 1 : -1;
80
+ const inBounds = order === 'asc' ? (i: number) => i < arr.length : (i: number) => i >= 0;
81
+
82
+ for (let i = start; inBounds(i); i += step) {
83
+ if (filter === undefined || filter(arr[i].txHash)) {
84
+ yield arr[i].txHash;
90
85
  }
91
86
  }
92
87
  }
@@ -227,11 +222,7 @@ export class TxPoolIndices {
227
222
 
228
223
  /** Gets the count of pending transactions */
229
224
  getPendingTxCount(): number {
230
- let count = 0;
231
- for (const hashes of this.#pendingByPriority.values()) {
232
- count += hashes.size;
233
- }
234
- return count;
225
+ return this.#pendingByPriority.length;
235
226
  }
236
227
 
237
228
  /** Gets the lowest priority pending transaction hashes (up to limit) */
@@ -264,12 +255,10 @@ export class TxPoolIndices {
264
255
  /** Gets all pending transactions */
265
256
  getPendingTxs(): TxMetaData[] {
266
257
  const result: TxMetaData[] = [];
267
- for (const hashSet of this.#pendingByPriority.values()) {
268
- for (const txHash of hashSet) {
269
- const meta = this.#metadata.get(txHash);
270
- if (meta) {
271
- result.push(meta);
272
- }
258
+ for (const entry of this.#pendingByPriority) {
259
+ const meta = this.#metadata.get(entry.txHash);
260
+ if (meta) {
261
+ result.push(meta);
273
262
  }
274
263
  }
275
264
  return result;
@@ -408,13 +397,12 @@ export class TxPoolIndices {
408
397
  }
409
398
  feePayerSet.add(meta.txHash);
410
399
 
411
- // Add to priority bucket
412
- let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
413
- if (!prioritySet) {
414
- prioritySet = new Set();
415
- this.#pendingByPriority.set(meta.priorityFee, prioritySet);
416
- }
417
- prioritySet.add(meta.txHash);
400
+ insertIntoSortedArray(
401
+ this.#pendingByPriority,
402
+ { txHash: meta.txHash, priorityFee: meta.priorityFee, txHashBigInt: meta.txHashBigInt },
403
+ comparePriority,
404
+ false,
405
+ );
418
406
  }
419
407
 
420
408
  #removeFromPendingIndices(meta: TxMetaData): void {
@@ -432,13 +420,11 @@ export class TxPoolIndices {
432
420
  }
433
421
  }
434
422
 
435
- // Remove from priority map
436
- const hashSet = this.#pendingByPriority.get(meta.priorityFee);
437
- if (hashSet) {
438
- hashSet.delete(meta.txHash);
439
- if (hashSet.size === 0) {
440
- this.#pendingByPriority.delete(meta.priorityFee);
441
- }
442
- }
423
+ // Remove from priority array
424
+ removeFromSortedArray(
425
+ this.#pendingByPriority,
426
+ { txHash: meta.txHash, priorityFee: meta.priorityFee, txHashBigInt: meta.txHashBigInt },
427
+ comparePriority,
428
+ );
443
429
  }
444
430
  }
@@ -58,6 +58,9 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
58
58
  const hashes = txHashes.map(h => (typeof h === 'string' ? TxHash.fromString(h) : TxHash.fromBigInt(h)));
59
59
  this.emit('txs-removed', { txHashes: hashes });
60
60
  },
61
+ onTxsMined: (txHashes: string[]) => {
62
+ this.#metrics?.transactionsRemoved(txHashes);
63
+ },
61
64
  };
62
65
 
63
66
  // Create the implementation
@@ -74,7 +77,7 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
74
77
  return this.#queue.put(() => this.#impl.addPendingTxs(txs, opts));
75
78
  }
76
79
 
77
- canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored' | 'rejected'> {
80
+ canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
78
81
  return this.#queue.put(() => this.#impl.canAddPendingTx(tx));
79
82
  }
80
83
 
@@ -45,6 +45,7 @@ import { TxPoolIndices } from './tx_pool_indices.js';
45
45
  export interface TxPoolV2Callbacks {
46
46
  onTxsAdded: (txs: Tx[], opts: { source?: string }) => void;
47
47
  onTxsRemoved: (txHashes: string[] | bigint[]) => void;
48
+ onTxsMined: (txHashes: string[]) => void;
48
49
  }
49
50
 
50
51
  /**
@@ -187,9 +188,35 @@ export class TxPoolV2Impl {
187
188
  const errors = new Map<string, TxPoolRejectionError>();
188
189
  const acceptedPending = new Set<string>();
189
190
 
191
+ // Phase 1: Pre-compute all throwable I/O outside the transaction.
192
+ // If any pre-computation throws, the entire call fails before mutations happen.
193
+ const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
194
+
195
+ const validator = await this.#createTxValidator();
196
+
197
+ for (const tx of txs) {
198
+ const txHash = tx.getTxHash();
199
+ const txHashStr = txHash.toString();
200
+
201
+ const meta = await buildTxMetaData(tx);
202
+ const minedBlockId = await this.#getMinedBlockId(txHash);
203
+
204
+ // Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
205
+ let isValid = true;
206
+ if (!minedBlockId) {
207
+ isValid = await this.#validateMeta(meta, validator);
208
+ }
209
+
210
+ precomputed.set(txHashStr, { meta, minedBlockId, isValid });
211
+ }
212
+
213
+ // Phase 2: Apply mutations inside the transaction using only pre-computed results,
214
+ // in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
190
215
  const poolAccess = this.#createPreAddPoolAccess();
191
216
  const preAddContext: PreAddContext | undefined =
192
- opts.feeComparisonOnly !== undefined ? { feeComparisonOnly: opts.feeComparisonOnly } : undefined;
217
+ opts.feeComparisonOnly !== undefined
218
+ ? { feeComparisonOnly: opts.feeComparisonOnly, priceBumpPercentage: this.#config.priceBumpPercentage }
219
+ : undefined;
193
220
 
194
221
  await this.#store.transactionAsync(async () => {
195
222
  for (const tx of txs) {
@@ -202,22 +229,25 @@ export class TxPoolV2Impl {
202
229
  continue;
203
230
  }
204
231
 
205
- // Check mined status first (applies to all paths)
206
- const minedBlockId = await this.#getMinedBlockId(txHash);
232
+ const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
207
233
  const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
208
234
 
209
235
  if (minedBlockId) {
210
236
  // Already mined - add directly (protection already set if pre-protected)
211
- await this.#addTx(tx, { mined: minedBlockId }, opts);
237
+ await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
212
238
  accepted.push(txHash);
213
239
  } else if (preProtectedSlot !== undefined) {
214
240
  // Pre-protected and not mined - add as protected (bypass validation)
215
- await this.#addTx(tx, { protected: preProtectedSlot }, opts);
241
+ await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
216
242
  accepted.push(txHash);
243
+ } else if (!isValid) {
244
+ // Failed pre-computed validation
245
+ rejected.push(txHash);
217
246
  } else {
218
- // Regular pending tx - validate and run pre-add rules
247
+ // Regular pending tx - run pre-add rules using pre-computed metadata
219
248
  const result = await this.#tryAddRegularPendingTx(
220
249
  tx,
250
+ meta,
221
251
  opts,
222
252
  poolAccess,
223
253
  acceptedPending,
@@ -227,8 +257,6 @@ export class TxPoolV2Impl {
227
257
  );
228
258
  if (result.status === 'accepted') {
229
259
  acceptedPending.add(txHashStr);
230
- } else if (result.status === 'rejected') {
231
- rejected.push(txHash);
232
260
  } else {
233
261
  ignored.push(txHash);
234
262
  }
@@ -259,27 +287,21 @@ export class TxPoolV2Impl {
259
287
  return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
260
288
  }
261
289
 
262
- /** Validates and adds a regular pending tx. Returns status. */
290
+ /** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
263
291
  async #tryAddRegularPendingTx(
264
292
  tx: Tx,
293
+ precomputedMeta: TxMetaData,
265
294
  opts: { source?: string },
266
295
  poolAccess: PreAddPoolAccess,
267
296
  acceptedPending: Set<string>,
268
297
  ignored: TxHash[],
269
298
  errors: Map<string, TxPoolRejectionError>,
270
299
  preAddContext?: PreAddContext,
271
- ): Promise<{ status: 'accepted' | 'ignored' | 'rejected' }> {
272
- const txHash = tx.getTxHash();
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
- }
300
+ ): Promise<{ status: 'accepted' | 'ignored' }> {
301
+ const txHashStr = tx.getTxHash().toString();
280
302
 
281
303
  // Run pre-add rules
282
- const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess, preAddContext);
304
+ const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
283
305
 
284
306
  if (preAddResult.shouldIgnore) {
285
307
  this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
@@ -323,11 +345,11 @@ export class TxPoolV2Impl {
323
345
  }
324
346
 
325
347
  // Add the transaction
326
- await this.#addTx(tx, 'pending', opts);
348
+ await this.#addTx(tx, 'pending', opts, precomputedMeta);
327
349
  return { status: 'accepted' };
328
350
  }
329
351
 
330
- async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored' | 'rejected'> {
352
+ async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
331
353
  const txHashStr = tx.getTxHash().toString();
332
354
 
333
355
  // Check if already in pool
@@ -335,14 +357,8 @@ export class TxPoolV2Impl {
335
357
  return 'ignored';
336
358
  }
337
359
 
338
- // Build metadata and validate using metadata
360
+ // Build metadata and check pre-add rules
339
361
  const meta = await buildTxMetaData(tx);
340
- const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
341
- if (validationResult !== true) {
342
- return 'rejected';
343
- }
344
-
345
- // Use pre-add rules
346
362
  const poolAccess = this.#createPreAddPoolAccess();
347
363
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
348
364
 
@@ -485,6 +501,10 @@ export class TxPoolV2Impl {
485
501
  await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
486
502
  });
487
503
 
504
+ if (found.length > 0) {
505
+ this.#callbacks.onTxsMined(found.map(m => m.txHash));
506
+ }
507
+
488
508
  this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
489
509
  }
490
510
 
@@ -771,9 +791,10 @@ export class TxPoolV2Impl {
771
791
  tx: Tx,
772
792
  state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
773
793
  opts: { source?: string } = {},
794
+ precomputedMeta?: TxMetaData,
774
795
  ): Promise<TxMetaData> {
775
796
  const txHashStr = tx.getTxHash().toString();
776
- const meta = await buildTxMetaData(tx);
797
+ const meta = precomputedMeta ?? (await buildTxMetaData(tx));
777
798
  meta.receivedAt = this.#dateProvider.now();
778
799
 
779
800
  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 extends ProposalValidator<BlockProposal> implements P2PValidator<BlockProposal> {
7
- constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
8
- super(epochCache, opts, 'p2p:block_proposal_validator');
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
- extends ProposalValidator<CheckpointProposal>
8
- implements P2PValidator<CheckpointProposal>
9
- {
10
- constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
11
- super(epochCache, opts, 'p2p:checkpoint_proposal_validator');
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
  }