@aztec/p2p 0.0.1-commit.4eabbdb → 0.0.1-commit.5358163d3

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 (163) hide show
  1. package/dest/client/factory.d.ts +4 -5
  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 -58
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
  10. package/dest/config.d.ts +10 -14
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +25 -35
  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/invalid_txs_after_reorg_rule.js +2 -2
  24. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +2 -2
  25. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  26. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +10 -6
  27. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
  28. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  29. package/dest/mem_pools/tx_pool_v2/index.js +1 -1
  30. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +7 -5
  31. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  32. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  33. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +26 -4
  34. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  35. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +48 -7
  36. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
  37. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  38. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +9 -10
  39. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +2 -2
  40. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  41. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +3 -0
  42. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +3 -2
  43. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  44. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +175 -145
  45. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
  46. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  47. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
  48. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  49. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
  50. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  51. package/dest/msg_validators/proposal_validator/proposal_validator.js +10 -0
  52. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +2 -1
  53. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +1 -1
  54. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +166 -0
  55. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +2 -2
  56. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  57. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
  58. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  59. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  60. package/dest/msg_validators/tx_validator/allowed_public_setup.js +25 -10
  61. package/dest/msg_validators/tx_validator/factory.d.ts +114 -6
  62. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  63. package/dest/msg_validators/tx_validator/factory.js +219 -58
  64. package/dest/msg_validators/tx_validator/gas_validator.d.ts +58 -3
  65. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  66. package/dest/msg_validators/tx_validator/gas_validator.js +73 -36
  67. package/dest/msg_validators/tx_validator/index.d.ts +2 -1
  68. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  69. package/dest/msg_validators/tx_validator/index.js +1 -0
  70. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  71. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  72. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  73. package/dest/msg_validators/tx_validator/phases_validator.d.ts +2 -2
  74. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  75. package/dest/msg_validators/tx_validator/phases_validator.js +25 -24
  76. package/dest/services/dummy_service.d.ts +2 -3
  77. package/dest/services/dummy_service.d.ts.map +1 -1
  78. package/dest/services/dummy_service.js +1 -4
  79. package/dest/services/encoding.d.ts +2 -2
  80. package/dest/services/encoding.d.ts.map +1 -1
  81. package/dest/services/encoding.js +7 -7
  82. package/dest/services/libp2p/libp2p_service.d.ts +15 -13
  83. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  84. package/dest/services/libp2p/libp2p_service.js +72 -83
  85. package/dest/services/peer-manager/metrics.d.ts +3 -1
  86. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  87. package/dest/services/peer-manager/metrics.js +6 -0
  88. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  89. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  90. package/dest/services/peer-manager/peer_manager.js +2 -1
  91. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
  92. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  93. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +14 -37
  94. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +17 -11
  95. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
  96. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +49 -15
  97. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
  98. package/dest/services/reqresp/reqresp.d.ts +1 -1
  99. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  100. package/dest/services/reqresp/reqresp.js +2 -1
  101. package/dest/services/service.d.ts +2 -2
  102. package/dest/services/service.d.ts.map +1 -1
  103. package/dest/services/tx_provider.d.ts +3 -3
  104. package/dest/services/tx_provider.d.ts.map +1 -1
  105. package/dest/services/tx_provider.js +4 -4
  106. package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
  107. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  108. package/dest/test-helpers/make-test-p2p-clients.js +1 -2
  109. package/dest/test-helpers/mock-pubsub.d.ts +2 -3
  110. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  111. package/dest/test-helpers/mock-pubsub.js +2 -2
  112. package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
  113. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  114. package/dest/test-helpers/reqresp-nodes.js +2 -2
  115. package/dest/test-helpers/testbench-utils.d.ts +2 -2
  116. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  117. package/dest/testbench/p2p_client_testbench_worker.js +5 -5
  118. package/package.json +14 -14
  119. package/src/client/factory.ts +22 -46
  120. package/src/client/interface.ts +5 -19
  121. package/src/client/p2p_client.ts +4 -88
  122. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -2
  123. package/src/config.ts +36 -42
  124. package/src/mem_pools/attestation_pool/attestation_pool.ts +5 -4
  125. package/src/mem_pools/instrumentation.ts +17 -13
  126. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
  127. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
  128. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +2 -2
  129. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +10 -6
  130. package/src/mem_pools/tx_pool_v2/index.ts +1 -1
  131. package/src/mem_pools/tx_pool_v2/interfaces.ts +7 -4
  132. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +65 -10
  133. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
  134. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +4 -1
  135. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +184 -148
  136. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +1 -1
  137. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +1 -1
  138. package/src/msg_validators/proposal_validator/proposal_validator.ts +15 -1
  139. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +144 -1
  140. package/src/msg_validators/tx_validator/README.md +115 -0
  141. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -3
  142. package/src/msg_validators/tx_validator/allowed_public_setup.ts +27 -13
  143. package/src/msg_validators/tx_validator/factory.ts +353 -77
  144. package/src/msg_validators/tx_validator/gas_validator.ts +90 -27
  145. package/src/msg_validators/tx_validator/index.ts +1 -0
  146. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  147. package/src/msg_validators/tx_validator/phases_validator.ts +25 -29
  148. package/src/services/dummy_service.ts +1 -5
  149. package/src/services/encoding.ts +5 -6
  150. package/src/services/libp2p/libp2p_service.ts +84 -92
  151. package/src/services/peer-manager/metrics.ts +7 -0
  152. package/src/services/peer-manager/peer_manager.ts +2 -1
  153. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +14 -42
  154. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +63 -24
  155. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
  156. package/src/services/reqresp/reqresp.ts +3 -1
  157. package/src/services/service.ts +1 -1
  158. package/src/services/tx_provider.ts +2 -2
  159. package/src/test-helpers/make-test-p2p-clients.ts +0 -2
  160. package/src/test-helpers/mock-pubsub.ts +3 -6
  161. package/src/test-helpers/reqresp-nodes.ts +2 -5
  162. package/src/test-helpers/testbench-utils.ts +1 -1
  163. package/src/testbench/p2p_client_testbench_worker.ts +2 -6
@@ -6,6 +6,7 @@ import {
6
6
  type PublicCallRequestWithCalldata,
7
7
  TX_ERROR_DURING_VALIDATION,
8
8
  TX_ERROR_SETUP_FUNCTION_NOT_ALLOWED,
9
+ TX_ERROR_SETUP_FUNCTION_UNKNOWN_CONTRACT,
9
10
  Tx,
10
11
  TxExecutionPhase,
11
12
  type TxValidationResult,
@@ -45,7 +46,8 @@ export class PhasesTxValidator implements TxValidator<Tx> {
45
46
 
46
47
  const setupFns = getCallRequestsWithCalldataByPhase(tx, TxExecutionPhase.SETUP);
47
48
  for (const setupFn of setupFns) {
48
- if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
49
+ const rejectionReason = await this.checkAllowList(setupFn, this.setupAllowList);
50
+ if (rejectionReason) {
49
51
  this.#log.verbose(
50
52
  `Rejecting tx ${tx.getTxHash().toString()} because it calls setup function not on allow list: ${
51
53
  setupFn.request.contractAddress
@@ -53,7 +55,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
53
55
  { allowList: this.setupAllowList },
54
56
  );
55
57
 
56
- return { result: 'invalid', reason: [TX_ERROR_SETUP_FUNCTION_NOT_ALLOWED] };
58
+ return { result: 'invalid', reason: [rejectionReason] };
57
59
  }
58
60
  }
59
61
 
@@ -66,53 +68,47 @@ export class PhasesTxValidator implements TxValidator<Tx> {
66
68
  }
67
69
  }
68
70
 
69
- private async isOnAllowList(
71
+ /** Returns a rejection reason if the call is not on the allow list, or undefined if it is allowed. */
72
+ private async checkAllowList(
70
73
  publicCall: PublicCallRequestWithCalldata,
71
74
  allowList: AllowedElement[],
72
- ): Promise<boolean> {
75
+ ): Promise<string | undefined> {
73
76
  if (publicCall.isEmpty()) {
74
- return true;
77
+ return undefined;
75
78
  }
76
79
 
77
80
  const contractAddress = publicCall.request.contractAddress;
78
81
  const functionSelector = publicCall.functionSelector;
79
82
 
80
- // do these checks first since they don't require the contract class
83
+ // Check address-based entries first since they don't require the contract class.
81
84
  for (const entry of allowList) {
82
- if ('address' in entry && !('selector' in entry)) {
83
- if (contractAddress.equals(entry.address)) {
84
- return true;
85
- }
86
- }
87
-
88
- if ('address' in entry && 'selector' in entry) {
85
+ if ('address' in entry) {
89
86
  if (contractAddress.equals(entry.address) && entry.selector.equals(functionSelector)) {
90
- return true;
87
+ return undefined;
91
88
  }
92
89
  }
90
+ }
93
91
 
94
- const contractClass = await this.contractsDB.getContractInstance(contractAddress, this.timestamp);
95
-
96
- if (!contractClass) {
97
- throw new Error(`Contract not found: ${contractAddress}`);
92
+ // Check class-based entries. Fetch the contract instance lazily (only once).
93
+ let contractClassId: undefined | { value: string | undefined };
94
+ for (const entry of allowList) {
95
+ if (!('classId' in entry)) {
96
+ continue;
98
97
  }
99
98
 
100
- if ('classId' in entry && !('selector' in entry)) {
101
- if (contractClass.currentContractClassId.equals(entry.classId)) {
102
- return true;
99
+ if (contractClassId === undefined) {
100
+ const instance = await this.contractsDB.getContractInstance(contractAddress, this.timestamp);
101
+ contractClassId = { value: instance?.currentContractClassId.toString() };
102
+ if (!contractClassId.value) {
103
+ return TX_ERROR_SETUP_FUNCTION_UNKNOWN_CONTRACT;
103
104
  }
104
105
  }
105
106
 
106
- if ('classId' in entry && 'selector' in entry) {
107
- if (
108
- contractClass.currentContractClassId.equals(entry.classId) &&
109
- (entry.selector === undefined || entry.selector.equals(functionSelector))
110
- ) {
111
- return true;
112
- }
107
+ if (contractClassId.value === entry.classId.toString() && entry.selector.equals(functionSelector)) {
108
+ return undefined;
113
109
  }
114
110
  }
115
111
 
116
- return false;
112
+ return TX_ERROR_SETUP_FUNCTION_NOT_ALLOWED;
117
113
  }
118
114
  }
@@ -141,14 +141,10 @@ export class DummyP2PService implements P2PService {
141
141
  return undefined;
142
142
  }
143
143
 
144
- validate(_txs: Tx[]): Promise<void> {
144
+ validateTxsReceivedInBlockProposal(_txs: Tx[]): Promise<void> {
145
145
  return Promise.resolve();
146
146
  }
147
147
 
148
- validatePropagatedTx(_tx: Tx, _peerId: PeerId): Promise<boolean> {
149
- return Promise.resolve(true);
150
- }
151
-
152
148
  addReqRespSubProtocol(
153
149
  _subProtocol: ReqRespSubProtocol,
154
150
  _handler: ReqRespSubProtocolHandler,
@@ -1,11 +1,11 @@
1
1
  // Taken from lodestar: https://github.com/ChainSafe/lodestar
2
- import { sha256 } from '@aztec/foundation/crypto/sha256';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { MAX_TX_SIZE_KB, TopicType, getTopicFromString } from '@aztec/stdlib/p2p';
5
4
 
6
5
  import type { RPC } from '@chainsafe/libp2p-gossipsub/message';
7
6
  import type { DataTransform } from '@chainsafe/libp2p-gossipsub/types';
8
7
  import type { Message } from '@libp2p/interface';
8
+ import { webcrypto } from 'node:crypto';
9
9
  import { compressSync, uncompressSync } from 'snappy';
10
10
  import xxhashFactory from 'xxhash-wasm';
11
11
 
@@ -44,11 +44,10 @@ export function msgIdToStrFn(msgId: Uint8Array): string {
44
44
  * @param message - The libp2p message
45
45
  * @returns The message identifier
46
46
  */
47
- export function getMsgIdFn(message: Message) {
48
- const { topic } = message;
49
-
50
- const vec = [Buffer.from(topic), message.data];
51
- return sha256(Buffer.concat(vec)).subarray(0, 20);
47
+ export async function getMsgIdFn({ topic, data }: Message): Promise<Uint8Array> {
48
+ const buffer = Buffer.concat([Buffer.from(topic), data]);
49
+ const hash = await webcrypto.subtle.digest('SHA-256', buffer);
50
+ return Buffer.from(hash.slice(0, 20));
52
51
  }
53
52
 
54
53
  const DefaultMaxSizesKb: Record<TopicType, number> = {
@@ -16,13 +16,12 @@ import {
16
16
  CheckpointProposal,
17
17
  type CheckpointProposalCore,
18
18
  type Gossipable,
19
- P2PClientType,
20
19
  P2PMessage,
21
20
  type ValidationResult as P2PValidationResult,
22
21
  PeerErrorSeverity,
23
22
  TopicType,
24
23
  createTopicString,
25
- getTopicsForClientAndConfig,
24
+ getTopicsForConfig,
26
25
  metricsTopicStrToLabels,
27
26
  } from '@aztec/stdlib/p2p';
28
27
  import { MerkleTreeId } from '@aztec/stdlib/trees';
@@ -69,9 +68,11 @@ import {
69
68
  } from '../../msg_validators/index.js';
70
69
  import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
71
70
  import {
72
- type MessageValidator,
73
- createTxMessageValidators,
74
- createTxReqRespValidator,
71
+ type TransactionValidator,
72
+ createFirstStageTxValidationsForGossipedTransactions,
73
+ createSecondStageTxValidationsForGossipedTransactions,
74
+ createTxValidatorForBlockProposalReceivedTxs,
75
+ createTxValidatorForReqResponseReceivedTxs,
75
76
  } from '../../msg_validators/tx_validator/factory.js';
76
77
  import { GossipSubEvent } from '../../types/index.js';
77
78
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
@@ -87,6 +88,9 @@ import { PeerScoring } from '../peer-manager/peer_scoring.js';
87
88
  import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
88
89
  import type { P2PReqRespConfig } from '../reqresp/config.js';
89
90
  import {
91
+ AuthRequest,
92
+ BlockTxsRequest,
93
+ BlockTxsResponse,
90
94
  DEFAULT_SUB_PROTOCOL_VALIDATORS,
91
95
  type ReqRespInterface,
92
96
  type ReqRespResponse,
@@ -94,14 +98,9 @@ import {
94
98
  type ReqRespSubProtocolHandler,
95
99
  type ReqRespSubProtocolHandlers,
96
100
  type ReqRespSubProtocolValidators,
101
+ StatusMessage,
97
102
  type SubProtocolMap,
98
103
  ValidationError,
99
- } from '../reqresp/index.js';
100
- import {
101
- AuthRequest,
102
- BlockTxsRequest,
103
- BlockTxsResponse,
104
- StatusMessage,
105
104
  pingHandler,
106
105
  reqGoodbyeHandler,
107
106
  reqRespBlockHandler,
@@ -135,7 +134,7 @@ type ReceivedMessageValidationResult<T, M = undefined> =
135
134
  /**
136
135
  * Lib P2P implementation of the P2PService interface.
137
136
  */
138
- export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
137
+ export class LibP2PService extends WithTracer implements P2PService {
139
138
  private discoveryRunningPromise?: RunningPromise;
140
139
  private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
141
140
 
@@ -182,7 +181,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
182
181
  protected logger: Logger;
183
182
 
184
183
  constructor(
185
- private clientType: T,
186
184
  private config: P2PConfig,
187
185
  protected node: PubSubLibp2p,
188
186
  private peerDiscoveryService: PeerDiscoveryService,
@@ -224,9 +222,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
224
222
  this.protocolVersion,
225
223
  );
226
224
 
227
- this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
225
+ this.blockProposalValidator = new BlockProposalValidator(epochCache, {
226
+ txsPermitted: !config.disableTransactions,
227
+ maxTxsPerBlock: config.maxTxsPerBlock,
228
+ });
228
229
  this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, {
229
230
  txsPermitted: !config.disableTransactions,
231
+ maxTxsPerBlock: config.maxTxsPerBlock,
230
232
  });
231
233
  this.checkpointAttestationValidator = config.fishermanMode
232
234
  ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry)
@@ -262,8 +264,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
262
264
  * @param txPool - The transaction pool to be accessed by the service.
263
265
  * @returns The new service.
264
266
  */
265
- public static async new<T extends P2PClientType>(
266
- clientType: T,
267
+ public static async new(
267
268
  config: P2PConfig,
268
269
  peerId: PeerId,
269
270
  deps: {
@@ -475,7 +476,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
475
476
  peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
476
477
 
477
478
  return new LibP2PService(
478
- clientType,
479
479
  config,
480
480
  node,
481
481
  peerDiscoveryService,
@@ -549,7 +549,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
549
549
  await this.node.start();
550
550
 
551
551
  // Subscribe to standard GossipSub topics by default
552
- for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
552
+ for (const topic of getTopicsForConfig(this.config.disableTransactions)) {
553
553
  this.subscribeToTopic(this.topicStrings[topic]);
554
554
  }
555
555
 
@@ -818,9 +818,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
818
818
  if (msg.topic === this.topicStrings[TopicType.tx]) {
819
819
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
820
820
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
821
- if (this.clientType === P2PClientType.Full) {
822
- await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
823
- }
821
+ await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
824
822
  } else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
825
823
  await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
826
824
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
@@ -906,15 +904,45 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
906
904
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
907
905
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
908
906
  const tx = Tx.fromBuffer(payloadData);
909
- const isValid = await this.validatePropagatedTx(tx, source);
910
- if (!isValid) {
911
- this.logger.trace(`Rejecting invalid propagated tx`, {
912
- [Attributes.P2P_ID]: source.toString(),
913
- });
907
+
908
+ const currentBlockNumber = await this.archiver.getBlockNumber();
909
+ const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
910
+
911
+ // Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
912
+ const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
913
+ const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
914
+ if (!firstStageOutcome.allPassed) {
915
+ const { name } = firstStageOutcome.failure;
916
+ let { severity } = firstStageOutcome.failure;
917
+
918
+ // Double spend validator has a special case handler. We perform more detailed examination
919
+ // as to how recently the nullifier was entered into the tree and if the transaction should
920
+ // have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
921
+ if (name === 'doubleSpendValidator') {
922
+ const txBlockNumber = BlockNumber(currentBlockNumber + 1);
923
+ severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
924
+ }
925
+
926
+ this.peerManager.penalizePeer(source, severity);
927
+ return { result: TopicValidatorResult.Reject };
928
+ }
929
+
930
+ // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
931
+ const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
932
+ if (canAdd === 'ignored') {
933
+ return { result: TopicValidatorResult.Ignore, obj: tx };
934
+ }
935
+
936
+ // Stage 2: expensive proof verification
937
+ const secondStageValidators = this.createSecondStageMessageValidators();
938
+ const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
939
+ if (!secondStageOutcome.allPassed) {
940
+ const { severity } = secondStageOutcome.failure;
941
+ this.peerManager.penalizePeer(source, severity);
914
942
  return { result: TopicValidatorResult.Reject };
915
943
  }
916
944
 
917
- // Propagate only on pool acceptance
945
+ // Pool add: persist the tx
918
946
  const txHash = tx.getTxHash();
919
947
  const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
920
948
 
@@ -922,7 +950,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
922
950
  const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
923
951
 
924
952
  this.logger.trace(`Validate propagated tx`, {
925
- isValid,
926
953
  wasAccepted,
927
954
  wasIgnored,
928
955
  [Attributes.P2P_ID]: source.toString(),
@@ -1537,43 +1564,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1537
1564
  }
1538
1565
 
1539
1566
  protected createRequestedTxValidator(): TxValidator {
1540
- return createTxReqRespValidator(this.proofVerifier, {
1567
+ return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
1541
1568
  l1ChainId: this.config.l1ChainId,
1542
1569
  rollupVersion: this.config.rollupVersion,
1543
1570
  });
1544
1571
  }
1545
1572
 
1546
- @trackSpan('Libp2pService.validatePropagatedTx', tx => ({
1547
- [Attributes.TX_HASH]: tx.getTxHash().toString(),
1548
- }))
1549
- protected async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
1550
- const currentBlockNumber = await this.archiver.getBlockNumber();
1551
-
1552
- // We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
1553
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1554
- const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1555
-
1556
- for (const validator of messageValidators) {
1557
- const outcome = await this.runValidations(tx, validator);
1558
-
1559
- if (outcome.allPassed) {
1560
- continue;
1561
- }
1562
- const { name } = outcome.failure;
1563
- let { severity } = outcome.failure;
1564
-
1565
- // Double spend validator has a special case handler
1566
- if (name === 'doubleSpendValidator') {
1567
- const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
1568
- severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
1569
- }
1570
-
1571
- this.peerManager.penalizePeer(peerId, severity);
1572
- return false;
1573
- }
1574
- return true;
1575
- }
1576
-
1577
1573
  private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
1578
1574
  if (blockNumber === this.feesCache?.blockNumber) {
1579
1575
  return this.feesCache.gasFees;
@@ -1601,60 +1597,56 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1601
1597
  };
1602
1598
  }
1603
1599
 
1604
- public async validate(txs: Tx[]): Promise<void> {
1605
- const currentBlockNumber = await this.archiver.getBlockNumber();
1606
-
1607
- // We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
1608
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1609
- const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1600
+ public async validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
1601
+ const validator = createTxValidatorForBlockProposalReceivedTxs(
1602
+ this.proofVerifier,
1603
+ { l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
1604
+ this.logger.getBindings(),
1605
+ );
1610
1606
 
1611
- await Promise.all(
1607
+ const results = await Promise.all(
1612
1608
  txs.map(async tx => {
1613
- for (const validator of messageValidators) {
1614
- const outcome = await this.runValidations(tx, validator);
1615
- if (!outcome.allPassed) {
1616
- throw new Error('Invalid tx detected', { cause: { outcome } });
1617
- }
1618
- }
1609
+ const result = await validator.validateTx(tx);
1610
+ return result.result !== 'invalid';
1619
1611
  }),
1620
1612
  );
1613
+ if (results.some(value => value === false)) {
1614
+ throw new Error('Invalid tx detected');
1615
+ }
1621
1616
  }
1622
1617
 
1623
- /**
1624
- * Create message validators for the given block number and timestamp.
1625
- *
1626
- * Each validator is a pair of a validator and a severity.
1627
- * If a validator fails, the peer is penalized with the severity of the validator.
1628
- *
1629
- * @param currentBlockNumber - The current synced block number.
1630
- * @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
1631
- * @returns The message validators.
1632
- */
1633
- private async createMessageValidators(
1618
+ /** Creates the first stage (fast) validators for gossiped transactions. */
1619
+ protected async createFirstStageMessageValidators(
1634
1620
  currentBlockNumber: BlockNumber,
1635
1621
  nextSlotTimestamp: UInt64,
1636
- ): Promise<Record<string, MessageValidator>[]> {
1622
+ ): Promise<Record<string, TransactionValidator>> {
1637
1623
  const gasFees = await this.getGasFees(currentBlockNumber);
1638
- const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
1639
-
1640
- const blockNumberInWhichTheTxIsConsideredToBeIncluded = BlockNumber(currentBlockNumber + 1);
1624
+ const allowedInSetup = [
1625
+ ...(await getDefaultAllowedSetupFunctions()),
1626
+ ...(this.config.txPublicSetupAllowListExtend ?? []),
1627
+ ];
1628
+ const blockNumber = BlockNumber(currentBlockNumber + 1);
1641
1629
 
1642
- return createTxMessageValidators(
1630
+ return createFirstStageTxValidationsForGossipedTransactions(
1643
1631
  nextSlotTimestamp,
1644
- blockNumberInWhichTheTxIsConsideredToBeIncluded,
1632
+ blockNumber,
1645
1633
  this.worldStateSynchronizer,
1646
1634
  gasFees,
1647
1635
  this.config.l1ChainId,
1648
1636
  this.config.rollupVersion,
1649
1637
  protocolContractsHash,
1650
1638
  this.archiver,
1651
- this.proofVerifier,
1652
1639
  !this.config.disableTransactions,
1653
1640
  allowedInSetup,
1654
1641
  this.logger.getBindings(),
1655
1642
  );
1656
1643
  }
1657
1644
 
1645
+ /** Creates the second stage (expensive proof verification) validators for gossiped transactions. */
1646
+ protected createSecondStageMessageValidators(): Record<string, TransactionValidator> {
1647
+ return createSecondStageTxValidationsForGossipedTransactions(this.proofVerifier, this.logger.getBindings());
1648
+ }
1649
+
1658
1650
  /**
1659
1651
  * Run validations on a tx.
1660
1652
  * @param tx - The tx to validate.
@@ -1663,7 +1655,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1663
1655
  */
1664
1656
  private async runValidations(
1665
1657
  tx: Tx,
1666
- messageValidators: Record<string, MessageValidator>,
1658
+ messageValidators: Record<string, TransactionValidator>,
1667
1659
  ): Promise<ValidationOutcome> {
1668
1660
  const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
1669
1661
  const { result } = await validator.validateTx(tx);
@@ -18,6 +18,7 @@ export class PeerManagerMetrics {
18
18
  private sentGoodbyes: UpDownCounter;
19
19
  private receivedGoodbyes: UpDownCounter;
20
20
  private peerCount: Gauge;
21
+ private healthyPeerCount: Gauge;
21
22
  private lowScoreDisconnects: UpDownCounter;
22
23
  private peerConnectionDuration: Histogram;
23
24
 
@@ -49,6 +50,7 @@ export class PeerManagerMetrics {
49
50
  goodbyeReasonAttrs,
50
51
  );
51
52
  this.peerCount = meter.createGauge(Metrics.PEER_MANAGER_PEER_COUNT);
53
+ this.healthyPeerCount = meter.createGauge(Metrics.PEER_MANAGER_HEALTHY_PEER_COUNT);
52
54
  this.lowScoreDisconnects = createUpDownCounterWithDefault(meter, Metrics.PEER_MANAGER_LOW_SCORE_DISCONNECTS, {
53
55
  [Attributes.P2P_PEER_SCORE_STATE]: ['Banned', 'Disconnect'],
54
56
  });
@@ -67,6 +69,10 @@ export class PeerManagerMetrics {
67
69
  this.peerCount.record(count);
68
70
  }
69
71
 
72
+ public recordHealthyPeerCount(count: number) {
73
+ this.healthyPeerCount.record(count);
74
+ }
75
+
70
76
  public recordLowScoreDisconnect(scoreState: 'Banned' | 'Disconnect') {
71
77
  this.lowScoreDisconnects.add(1, { [Attributes.P2P_PEER_SCORE_STATE]: scoreState });
72
78
  }
@@ -79,6 +85,7 @@ export class PeerManagerMetrics {
79
85
  const connectedAt = this.peerConnectedAt.get(id.toString());
80
86
  if (connectedAt) {
81
87
  this.peerConnectionDuration.record(Date.now() - connectedAt);
88
+ this.peerConnectedAt.delete(id.toString());
82
89
  }
83
90
  }
84
91
  }
@@ -515,7 +515,8 @@ export class PeerManager implements PeerManagerInterface {
515
515
  ...this.peerScoring.getStats(),
516
516
  });
517
517
 
518
- this.metrics.recordPeerCount(healthyConnections.length);
518
+ this.metrics.recordPeerCount(connections.length);
519
+ this.metrics.recordHealthyPeerCount(healthyConnections.length);
519
520
 
520
521
  // Exit if no peers to connect
521
522
  if (peersToConnect <= 0) {
@@ -8,7 +8,6 @@ import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
8
8
  import { Tx, TxArray, TxHash } from '@aztec/stdlib/tx';
9
9
 
10
10
  import type { PeerId } from '@libp2p/interface';
11
- import { peerIdFromString } from '@libp2p/peer-id';
12
11
 
13
12
  import type { IMissingTxsTracker } from '../../tx_collection/missing_txs_tracker.js';
14
13
  import { ReqRespSubProtocol } from '.././interface.js';
@@ -90,10 +89,9 @@ export class BatchTxRequester {
90
89
  if (this.opts.peerCollection) {
91
90
  this.peers = this.opts.peerCollection;
92
91
  } else {
93
- const initialPeers = this.p2pService.connectionSampler.getPeerListSortedByConnectionCountAsc();
94
92
  const badPeerThreshold = this.opts.badPeerThreshold ?? DEFAULT_BATCH_TX_REQUESTER_BAD_PEER_THRESHOLD;
95
93
  this.peers = new PeerCollection(
96
- initialPeers,
94
+ this.p2pService.connectionSampler,
97
95
  this.pinnedPeer,
98
96
  this.dateProvider,
99
97
  badPeerThreshold,
@@ -227,7 +225,6 @@ export class BatchTxRequester {
227
225
  * Starts dumb worker loops
228
226
  * */
229
227
  private async dumbRequester() {
230
- const nextPeerIndex = this.makeRoundRobinIndexer();
231
228
  const nextBatchIndex = this.makeRoundRobinIndexer();
232
229
 
233
230
  // Chunk missing tx hashes into batches of txBatchSize, wrapping around to ensure no peer gets less than txBatchSize
@@ -263,15 +260,9 @@ export class BatchTxRequester {
263
260
  return { blockRequest, txs };
264
261
  };
265
262
 
266
- const nextPeer = () => {
267
- const peers = this.peers.getDumbPeersToQuery();
268
- const idx = nextPeerIndex(() => peers.length);
269
- return idx === undefined ? undefined : peerIdFromString(peers[idx]);
270
- };
271
-
272
- const workerCount = Math.min(this.dumbParallelWorkerCount, this.peers.getAllPeers().size);
263
+ const workerCount = this.dumbParallelWorkerCount;
273
264
  const workers = Array.from({ length: workerCount }, (_, index) =>
274
- this.dumbWorkerLoop(nextPeer, makeRequest, index + 1),
265
+ this.dumbWorkerLoop(this.peers.nextDumbPeerToQuery.bind(this.peers), makeRequest, index + 1),
275
266
  );
276
267
 
277
268
  await Promise.allSettled(workers);
@@ -332,14 +323,6 @@ export class BatchTxRequester {
332
323
  * Starts smart worker loops
333
324
  * */
334
325
  private async smartRequester() {
335
- const nextPeerIndex = this.makeRoundRobinIndexer();
336
-
337
- const nextPeer = () => {
338
- const peers = this.peers.getSmartPeersToQuery();
339
- const idx = nextPeerIndex(() => peers.length);
340
- return idx === undefined ? undefined : peerIdFromString(peers[idx]);
341
- };
342
-
343
326
  const makeRequest = (pid: PeerId) => {
344
327
  const txs = this.txsMetadata.getTxsToRequestFromThePeer(pid);
345
328
  const blockRequest = BlockTxsRequest.fromTxsSourceAndMissingTxs(this.blockTxsSource, txs);
@@ -350,9 +333,8 @@ export class BatchTxRequester {
350
333
  return { blockRequest, txs };
351
334
  };
352
335
 
353
- const workers = Array.from(
354
- { length: Math.min(this.smartParallelWorkerCount, this.peers.getAllPeers().size) },
355
- (_, index) => this.smartWorkerLoop(nextPeer, makeRequest, index + 1),
336
+ const workers = Array.from({ length: this.smartParallelWorkerCount }, (_, index) =>
337
+ this.smartWorkerLoop(this.peers.nextSmartPeerToQuery.bind(this.peers), makeRequest, index + 1),
356
338
  );
357
339
 
358
340
  await Promise.allSettled(workers);
@@ -387,26 +369,18 @@ export class BatchTxRequester {
387
369
  if (weRanOutOfPeersToQuery) {
388
370
  this.logger.debug(`Worker loop smart: No more peers to query`);
389
371
 
390
- // If there are no more dumb peers to query then none of our peers can become smart,
391
- // thus we can simply exit this worker
392
- const noMoreDumbPeersToQuery = this.peers.getDumbPeersToQuery().length === 0;
393
- if (noMoreDumbPeersToQuery) {
394
- // These might be either smart peers that will get unblocked after _some time_
395
- const nextSmartPeerDelay = this.peers.getNextSmartPeerAvailabilityDelayMs();
396
- const thereAreSomeRateLimitedSmartPeers = nextSmartPeerDelay !== undefined;
397
- if (thereAreSomeRateLimitedSmartPeers) {
398
- await this.sleepClampedToDeadline(nextSmartPeerDelay);
399
- continue;
400
- }
401
-
402
- this.logger.debug(`Worker loop smart: No more smart peers to query killing ${workerIndex}`);
403
- break;
372
+ // If we have rate limited peers wait for them.
373
+ const nextSmartPeerDelay = this.peers.getNextSmartPeerAvailabilityDelayMs();
374
+ const thereAreSomeRateLimitedSmartPeers = nextSmartPeerDelay !== undefined;
375
+ if (thereAreSomeRateLimitedSmartPeers) {
376
+ await this.sleepClampedToDeadline(nextSmartPeerDelay);
377
+ continue;
404
378
  }
405
379
 
406
- // Otherwise there are still some dumb peers that could become smart.
407
380
  // We end up here when all known smart peers became temporarily unavailable via combination of
408
381
  // (bad, in-flight, or rate-limited) or in some weird scenario all current smart peers turn bad which is permanent
409
- // but dumb peers still exist that could become smart.
382
+ // but there are dumb peers that could be promoted
383
+ // or new peer can join as dumb and be promoted later
410
384
  //
411
385
  // When a dumb peer responds with valid txIndices, it gets
412
386
  // promoted to smart and releases the semaphore, waking this worker.
@@ -599,9 +573,7 @@ export class BatchTxRequester {
599
573
  this.markTxsPeerHas(peerId, response);
600
574
 
601
575
  // Unblock smart workers
602
- if (this.peers.getSmartPeersToQuery().length <= this.smartParallelWorkerCount) {
603
- this.smartRequesterSemaphore.release();
604
- }
576
+ this.smartRequesterSemaphore.release();
605
577
  }
606
578
 
607
579
  private isBlockResponseValid(response: BlockTxsResponse): boolean {