@aztec/p2p 0.74.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2

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 (217) hide show
  1. package/dest/bootstrap/bootstrap.js +41 -29
  2. package/dest/client/factory.js +8 -10
  3. package/dest/client/index.js +0 -1
  4. package/dest/client/p2p_client.js +513 -507
  5. package/dest/config.js +44 -39
  6. package/dest/errors/reqresp.error.js +6 -10
  7. package/dest/index.js +0 -1
  8. package/dest/mem_pools/attestation_pool/attestation_pool.js +6 -2
  9. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +64 -32
  10. package/dest/mem_pools/attestation_pool/index.js +0 -1
  11. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +22 -19
  12. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +22 -26
  13. package/dest/mem_pools/attestation_pool/mocks.js +10 -6
  14. package/dest/mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.js +1 -2
  15. package/dest/mem_pools/epoch_proof_quote_pool/index.js +0 -1
  16. package/dest/mem_pools/epoch_proof_quote_pool/memory_epoch_proof_quote_pool.js +5 -4
  17. package/dest/mem_pools/epoch_proof_quote_pool/test_utils.js +2 -3
  18. package/dest/mem_pools/index.js +1 -2
  19. package/dest/mem_pools/instrumentation.js +37 -42
  20. package/dest/mem_pools/interface.js +3 -2
  21. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +127 -134
  22. package/dest/mem_pools/tx_pool/index.js +0 -1
  23. package/dest/mem_pools/tx_pool/memory_tx_pool.js +45 -43
  24. package/dest/mem_pools/tx_pool/priority.js +1 -3
  25. package/dest/mem_pools/tx_pool/tx_pool.js +3 -2
  26. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +107 -37
  27. package/dest/mocks/index.js +47 -38
  28. package/dest/msg_validators/attestation_validator/attestation_validator.js +3 -3
  29. package/dest/msg_validators/attestation_validator/index.js +0 -1
  30. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +2 -2
  31. package/dest/msg_validators/block_proposal_validator/index.js +0 -1
  32. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.js +2 -2
  33. package/dest/msg_validators/epoch_proof_quote_validator/index.js +0 -1
  34. package/dest/msg_validators/index.js +0 -1
  35. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +9 -11
  36. package/dest/msg_validators/tx_validator/block_header_validator.js +17 -12
  37. package/dest/msg_validators/tx_validator/data_validator.js +41 -32
  38. package/dest/msg_validators/tx_validator/double_spend_validator.js +22 -14
  39. package/dest/msg_validators/tx_validator/index.js +0 -1
  40. package/dest/msg_validators/tx_validator/metadata_validator.js +29 -26
  41. package/dest/msg_validators/tx_validator/tx_proof_validator.js +17 -12
  42. package/dest/services/data_store.js +57 -57
  43. package/dest/services/discv5/discV5_service.js +31 -23
  44. package/dest/services/dummy_service.js +40 -58
  45. package/dest/services/encoding.js +10 -9
  46. package/dest/services/index.js +0 -1
  47. package/dest/services/libp2p/libp2p_service.js +709 -695
  48. package/dest/services/peer-manager/metrics.js +14 -7
  49. package/dest/services/peer-manager/peer_manager.js +340 -342
  50. package/dest/services/peer-manager/peer_scoring.js +20 -18
  51. package/dest/services/reqresp/config.js +4 -5
  52. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +35 -28
  53. package/dest/services/reqresp/connection-sampler/connection_sampler.js +60 -59
  54. package/dest/services/reqresp/index.js +1 -3
  55. package/dest/services/reqresp/interface.js +25 -30
  56. package/dest/services/reqresp/metrics.js +23 -10
  57. package/dest/services/reqresp/protocols/block.js +1 -2
  58. package/dest/services/reqresp/protocols/goodbye.js +35 -40
  59. package/dest/services/reqresp/protocols/index.js +1 -3
  60. package/dest/services/reqresp/protocols/ping.js +1 -3
  61. package/dest/services/reqresp/protocols/status.js +1 -3
  62. package/dest/services/reqresp/protocols/tx.js +5 -8
  63. package/dest/services/reqresp/rate-limiter/index.js +0 -1
  64. package/dest/services/reqresp/rate-limiter/rate_limiter.js +42 -36
  65. package/dest/services/reqresp/rate-limiter/rate_limits.js +16 -17
  66. package/dest/services/reqresp/reqresp.js +461 -395
  67. package/dest/services/reqresp/status.js +51 -0
  68. package/dest/services/service.js +3 -4
  69. package/dest/services/types.js +16 -23
  70. package/dest/util.js +23 -34
  71. package/package.json +8 -8
  72. package/src/client/p2p_client.ts +159 -125
  73. package/src/mem_pools/index.ts +3 -3
  74. package/src/mem_pools/instrumentation.ts +3 -2
  75. package/src/services/reqresp/interface.ts +11 -0
  76. package/src/services/reqresp/rate-limiter/rate_limiter.ts +3 -1
  77. package/src/services/reqresp/rate-limiter/rate_limits.ts +2 -2
  78. package/src/services/reqresp/reqresp.ts +111 -24
  79. package/src/services/reqresp/status.ts +59 -0
  80. package/dest/bootstrap/bootstrap.d.ts +0 -38
  81. package/dest/bootstrap/bootstrap.d.ts.map +0 -1
  82. package/dest/client/factory.d.ts +0 -19
  83. package/dest/client/factory.d.ts.map +0 -1
  84. package/dest/client/index.d.ts +0 -3
  85. package/dest/client/index.d.ts.map +0 -1
  86. package/dest/client/p2p_client.d.ts +0 -321
  87. package/dest/client/p2p_client.d.ts.map +0 -1
  88. package/dest/config.d.ts +0 -171
  89. package/dest/config.d.ts.map +0 -1
  90. package/dest/errors/reqresp.error.d.ts +0 -28
  91. package/dest/errors/reqresp.error.d.ts.map +0 -1
  92. package/dest/index.d.ts +0 -9
  93. package/dest/index.d.ts.map +0 -1
  94. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +0 -57
  95. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +0 -1
  96. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +0 -3
  97. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +0 -1
  98. package/dest/mem_pools/attestation_pool/index.d.ts +0 -3
  99. package/dest/mem_pools/attestation_pool/index.d.ts.map +0 -1
  100. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -22
  101. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  102. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -17
  103. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  104. package/dest/mem_pools/attestation_pool/mocks.d.ts +0 -18
  105. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +0 -1
  106. package/dest/mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.d.ts +0 -7
  107. package/dest/mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.d.ts.map +0 -1
  108. package/dest/mem_pools/epoch_proof_quote_pool/index.d.ts +0 -4
  109. package/dest/mem_pools/epoch_proof_quote_pool/index.d.ts.map +0 -1
  110. package/dest/mem_pools/epoch_proof_quote_pool/memory_epoch_proof_quote_pool.d.ts +0 -12
  111. package/dest/mem_pools/epoch_proof_quote_pool/memory_epoch_proof_quote_pool.d.ts.map +0 -1
  112. package/dest/mem_pools/epoch_proof_quote_pool/test_utils.d.ts +0 -8
  113. package/dest/mem_pools/epoch_proof_quote_pool/test_utils.d.ts.map +0 -1
  114. package/dest/mem_pools/index.d.ts +0 -5
  115. package/dest/mem_pools/index.d.ts.map +0 -1
  116. package/dest/mem_pools/instrumentation.d.ts +0 -31
  117. package/dest/mem_pools/instrumentation.d.ts.map +0 -1
  118. package/dest/mem_pools/interface.d.ts +0 -13
  119. package/dest/mem_pools/interface.d.ts.map +0 -1
  120. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -66
  121. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  122. package/dest/mem_pools/tx_pool/index.d.ts +0 -4
  123. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  124. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -56
  125. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
  126. package/dest/mem_pools/tx_pool/priority.d.ts +0 -8
  127. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  128. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -66
  129. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  130. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  131. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  132. package/dest/mocks/index.d.ts +0 -54
  133. package/dest/mocks/index.d.ts.map +0 -1
  134. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +0 -8
  135. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +0 -1
  136. package/dest/msg_validators/attestation_validator/index.d.ts +0 -2
  137. package/dest/msg_validators/attestation_validator/index.d.ts.map +0 -1
  138. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -8
  139. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
  140. package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
  141. package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
  142. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts +0 -8
  143. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts.map +0 -1
  144. package/dest/msg_validators/epoch_proof_quote_validator/index.d.ts +0 -2
  145. package/dest/msg_validators/epoch_proof_quote_validator/index.d.ts.map +0 -1
  146. package/dest/msg_validators/index.d.ts +0 -4
  147. package/dest/msg_validators/index.d.ts.map +0 -1
  148. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +0 -7
  149. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +0 -1
  150. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +0 -11
  151. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +0 -1
  152. package/dest/msg_validators/tx_validator/data_validator.d.ts +0 -6
  153. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +0 -1
  154. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +0 -12
  155. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +0 -1
  156. package/dest/msg_validators/tx_validator/index.d.ts +0 -7
  157. package/dest/msg_validators/tx_validator/index.d.ts.map +0 -1
  158. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +0 -10
  159. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +0 -1
  160. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +0 -8
  161. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +0 -1
  162. package/dest/services/data_store.d.ts +0 -27
  163. package/dest/services/data_store.d.ts.map +0 -1
  164. package/dest/services/discv5/discV5_service.d.ts +0 -36
  165. package/dest/services/discv5/discV5_service.d.ts.map +0 -1
  166. package/dest/services/dummy_service.d.ts +0 -82
  167. package/dest/services/dummy_service.d.ts.map +0 -1
  168. package/dest/services/encoding.d.ts +0 -31
  169. package/dest/services/encoding.d.ts.map +0 -1
  170. package/dest/services/index.d.ts +0 -3
  171. package/dest/services/index.d.ts.map +0 -1
  172. package/dest/services/libp2p/libp2p_service.d.ts +0 -225
  173. package/dest/services/libp2p/libp2p_service.d.ts.map +0 -1
  174. package/dest/services/peer-manager/metrics.d.ts +0 -12
  175. package/dest/services/peer-manager/metrics.d.ts.map +0 -1
  176. package/dest/services/peer-manager/peer_manager.d.ts +0 -76
  177. package/dest/services/peer-manager/peer_manager.d.ts.map +0 -1
  178. package/dest/services/peer-manager/peer_scoring.d.ts +0 -28
  179. package/dest/services/peer-manager/peer_scoring.d.ts.map +0 -1
  180. package/dest/services/reqresp/config.d.ts +0 -16
  181. package/dest/services/reqresp/config.d.ts.map +0 -1
  182. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -45
  183. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  184. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +0 -61
  185. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +0 -1
  186. package/dest/services/reqresp/index.d.ts +0 -6
  187. package/dest/services/reqresp/index.d.ts.map +0 -1
  188. package/dest/services/reqresp/interface.d.ts +0 -107
  189. package/dest/services/reqresp/interface.d.ts.map +0 -1
  190. package/dest/services/reqresp/metrics.d.ts +0 -15
  191. package/dest/services/reqresp/metrics.d.ts.map +0 -1
  192. package/dest/services/reqresp/protocols/block.d.ts +0 -4
  193. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  194. package/dest/services/reqresp/protocols/goodbye.d.ts +0 -51
  195. package/dest/services/reqresp/protocols/goodbye.d.ts.map +0 -1
  196. package/dest/services/reqresp/protocols/index.d.ts +0 -9
  197. package/dest/services/reqresp/protocols/index.d.ts.map +0 -1
  198. package/dest/services/reqresp/protocols/ping.d.ts +0 -9
  199. package/dest/services/reqresp/protocols/ping.d.ts.map +0 -1
  200. package/dest/services/reqresp/protocols/status.d.ts +0 -9
  201. package/dest/services/reqresp/protocols/status.d.ts.map +0 -1
  202. package/dest/services/reqresp/protocols/tx.d.ts +0 -13
  203. package/dest/services/reqresp/protocols/tx.d.ts.map +0 -1
  204. package/dest/services/reqresp/rate-limiter/index.d.ts +0 -2
  205. package/dest/services/reqresp/rate-limiter/index.d.ts.map +0 -1
  206. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +0 -102
  207. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +0 -1
  208. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +0 -3
  209. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +0 -1
  210. package/dest/services/reqresp/reqresp.d.ts +0 -161
  211. package/dest/services/reqresp/reqresp.d.ts.map +0 -1
  212. package/dest/services/service.d.ts +0 -85
  213. package/dest/services/service.d.ts.map +0 -1
  214. package/dest/services/types.d.ts +0 -38
  215. package/dest/services/types.d.ts.map +0 -1
  216. package/dest/util.d.ts +0 -53
  217. package/dest/util.d.ts.map +0 -1
@@ -1,11 +1,16 @@
1
- import { __esDecorate, __runInitializers } from "tslib";
2
- import { BlockAttestation, BlockProposal, EpochProofQuote, MerkleTreeId, P2PClientType, PeerErrorSeverity, TopicTypeMap, Tx, getTopicTypeForClientType, metricsTopicStrToLabels, } from '@aztec/circuit-types';
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { BlockAttestation, BlockProposal, EpochProofQuote, MerkleTreeId, P2PClientType, PeerErrorSeverity, TopicTypeMap, Tx, getTopicTypeForClientType, metricsTopicStrToLabels } from '@aztec/circuit-types';
3
8
  import { Fr } from '@aztec/circuits.js';
4
9
  import { createLogger } from '@aztec/foundation/log';
5
10
  import { SerialQueue } from '@aztec/foundation/queue';
6
11
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
12
  import { Attributes, OtelMetricsAdapter, WithTracer, trackSpan } from '@aztec/telemetry-client';
8
- import { gossipsub, } from '@chainsafe/libp2p-gossipsub';
13
+ import { gossipsub } from '@chainsafe/libp2p-gossipsub';
9
14
  import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
10
15
  import { noise } from '@chainsafe/libp2p-noise';
11
16
  import { yamux } from '@chainsafe/libp2p-yamux';
@@ -17,7 +22,7 @@ import { tcp } from '@libp2p/tcp';
17
22
  import { createLibp2p } from 'libp2p';
18
23
  import { EpochProofQuoteValidator } from '../../msg_validators/epoch_proof_quote_validator/index.js';
19
24
  import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
20
- import { DataTxValidator, DoubleSpendTxValidator, MetadataTxValidator, TxProofValidator, } from '../../msg_validators/tx_validator/index.js';
25
+ import { DataTxValidator, DoubleSpendTxValidator, MetadataTxValidator, TxProofValidator } from '../../msg_validators/tx_validator/index.js';
21
26
  import { convertToMultiaddr } from '../../util.js';
22
27
  import { AztecDatastore } from '../data_store.js';
23
28
  import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
@@ -30,698 +35,707 @@ import { ReqResp } from '../reqresp/reqresp.js';
30
35
  import { GossipSubEvent } from '../types.js';
31
36
  /**
32
37
  * Lib P2P implementation of the P2PService interface.
33
- */
34
- let LibP2PService = (() => {
35
- var _a;
36
- let _classSuper = WithTracer;
37
- let _instanceExtraInitializers = [];
38
- let _processAttestationFromPeer_decorators;
39
- let _processBlockFromPeer_decorators;
40
- let _broadcastAttestation_decorators;
41
- let _validateRequestedTx_decorators;
42
- let _validatePropagatedTx_decorators;
43
- let _validateAttestation_decorators;
44
- let _validateBlockProposal_decorators;
45
- let _validateEpochProofQuote_decorators;
46
- return _a = class LibP2PService extends _classSuper {
47
- constructor(clientType, config, node, peerDiscoveryService, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, telemetry, logger = createLogger('p2p:libp2p_service')) {
48
- super(telemetry, 'LibP2PService');
49
- this.clientType = (__runInitializers(this, _instanceExtraInitializers), clientType);
50
- this.config = config;
51
- this.node = node;
52
- this.peerDiscoveryService = peerDiscoveryService;
53
- this.mempools = mempools;
54
- this.l2BlockSource = l2BlockSource;
55
- this.proofVerifier = proofVerifier;
56
- this.worldStateSynchronizer = worldStateSynchronizer;
57
- this.logger = logger;
58
- this.jobQueue = new SerialQueue();
59
- const peerScoring = new PeerScoring(config);
60
- this.reqresp = new ReqResp(config, node, peerScoring);
61
- this.peerManager = new PeerManager(node, peerDiscoveryService, config, telemetry, logger, peerScoring, this.reqresp);
62
- // Update gossipsub score params
63
- this.node.services.pubsub.score.params.appSpecificScore = (peerId) => {
64
- return this.peerManager.getPeerScore(peerId);
65
- };
66
- this.node.services.pubsub.score.params.appSpecificWeight = 10;
67
- this.attestationValidator = new AttestationValidator(epochCache);
68
- this.blockProposalValidator = new BlockProposalValidator(epochCache);
69
- this.epochProofQuoteValidator = new EpochProofQuoteValidator(epochCache);
70
- this.blockReceivedCallback = async (block) => {
71
- this.logger.warn(`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`, { p2pMessageIdentifier: await block.p2pMessageIdentifier() });
72
- return undefined;
73
- };
74
- }
75
- /**
76
- * Creates an instance of the LibP2P service.
77
- * @param config - The configuration to use when creating the service.
78
- * @param txPool - The transaction pool to be accessed by the service.
79
- * @returns The new service.
80
- */
81
- static async new(clientType, config, peerDiscoveryService, peerId, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, store, telemetry) {
82
- const { tcpListenAddress, tcpAnnounceAddress, minPeerCount, maxPeerCount } = config;
83
- const bindAddrTcp = convertToMultiaddr(tcpListenAddress, 'tcp');
84
- // We know tcpAnnounceAddress cannot be null here because we set it or throw when setting up the service.
85
- const announceAddrTcp = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
86
- const datastore = new AztecDatastore(store);
87
- const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
88
- const node = await createLibp2p({
89
- start: false,
90
- peerId,
91
- addresses: {
92
- listen: [bindAddrTcp],
93
- announce: [announceAddrTcp],
94
- },
95
- transports: [
96
- tcp({
97
- maxConnections: config.maxPeerCount,
98
- // socket option: the maximum length of the queue of pending connections
99
- // https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
100
- // it's not safe if we increase this number
101
- backlog: 5,
102
- closeServerOnMaxConnections: {
103
- closeAbove: maxPeerCount ?? Infinity,
104
- listenBelow: maxPeerCount ?? Infinity,
105
- },
106
- }),
107
- ],
108
- datastore,
109
- streamMuxers: [yamux(), mplex()],
110
- connectionEncryption: [noise()],
111
- connectionManager: {
112
- minConnections: minPeerCount,
113
- maxConnections: maxPeerCount,
114
- },
115
- services: {
116
- identify: identify({
117
- protocolPrefix: 'aztec',
118
- }),
119
- pubsub: gossipsub({
120
- allowPublishToZeroTopicPeers: true,
121
- D: config.gossipsubD,
122
- Dlo: config.gossipsubDlo,
123
- Dhi: config.gossipsubDhi,
124
- heartbeatInterval: config.gossipsubInterval,
125
- mcacheLength: config.gossipsubMcacheLength,
126
- mcacheGossip: config.gossipsubMcacheGossip,
127
- msgIdFn: getMsgIdFn,
128
- msgIdToStrFn: msgIdToStrFn,
129
- fastMsgIdFn: fastMsgIdFn,
130
- dataTransform: new SnappyTransform(),
131
- metricsRegister: otelMetricsAdapter,
132
- metricsTopicStrToLabel: metricsTopicStrToLabels(),
133
- asyncValidation: true,
134
- scoreParams: createPeerScoreParams({
135
- topics: {
136
- [Tx.p2pTopic]: createTopicScoreParams({
137
- topicWeight: 1,
138
- invalidMessageDeliveriesWeight: -20,
139
- invalidMessageDeliveriesDecay: 0.5,
140
- }),
141
- [BlockAttestation.p2pTopic]: createTopicScoreParams({
142
- topicWeight: 1,
143
- invalidMessageDeliveriesWeight: -20,
144
- invalidMessageDeliveriesDecay: 0.5,
145
- }),
146
- [BlockAttestation.p2pTopic]: createTopicScoreParams({
147
- topicWeight: 1,
148
- invalidMessageDeliveriesWeight: -20,
149
- invalidMessageDeliveriesDecay: 0.5,
150
- }),
151
- [EpochProofQuote.p2pTopic]: createTopicScoreParams({
152
- topicWeight: 1,
153
- invalidMessageDeliveriesWeight: -20,
154
- invalidMessageDeliveriesDecay: 0.5,
155
- }),
156
- },
38
+ */ export class LibP2PService extends WithTracer {
39
+ clientType;
40
+ config;
41
+ node;
42
+ peerDiscoveryService;
43
+ mempools;
44
+ l2BlockSource;
45
+ proofVerifier;
46
+ worldStateSynchronizer;
47
+ logger;
48
+ jobQueue;
49
+ peerManager;
50
+ discoveryRunningPromise;
51
+ // Message validators
52
+ attestationValidator;
53
+ blockProposalValidator;
54
+ epochProofQuoteValidator;
55
+ // Request and response sub service
56
+ reqresp;
57
+ /**
58
+ * Callback for when a block is received from a peer.
59
+ * @param block - The block received from the peer.
60
+ * @returns The attestation for the block, if any.
61
+ */ blockReceivedCallback;
62
+ constructor(clientType, config, node, peerDiscoveryService, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, telemetry, logger = createLogger('p2p:libp2p_service')){
63
+ super(telemetry, 'LibP2PService'), this.clientType = clientType, this.config = config, this.node = node, this.peerDiscoveryService = peerDiscoveryService, this.mempools = mempools, this.l2BlockSource = l2BlockSource, this.proofVerifier = proofVerifier, this.worldStateSynchronizer = worldStateSynchronizer, this.logger = logger, this.jobQueue = new SerialQueue();
64
+ const peerScoring = new PeerScoring(config);
65
+ this.reqresp = new ReqResp(config, node, peerScoring);
66
+ this.peerManager = new PeerManager(node, peerDiscoveryService, config, telemetry, logger, peerScoring, this.reqresp);
67
+ // Update gossipsub score params
68
+ this.node.services.pubsub.score.params.appSpecificScore = (peerId)=>{
69
+ return this.peerManager.getPeerScore(peerId);
70
+ };
71
+ this.node.services.pubsub.score.params.appSpecificWeight = 10;
72
+ this.attestationValidator = new AttestationValidator(epochCache);
73
+ this.blockProposalValidator = new BlockProposalValidator(epochCache);
74
+ this.epochProofQuoteValidator = new EpochProofQuoteValidator(epochCache);
75
+ this.blockReceivedCallback = async (block)=>{
76
+ this.logger.warn(`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`, {
77
+ p2pMessageIdentifier: await block.p2pMessageIdentifier()
78
+ });
79
+ return undefined;
80
+ };
81
+ }
82
+ /**
83
+ * Creates an instance of the LibP2P service.
84
+ * @param config - The configuration to use when creating the service.
85
+ * @param txPool - The transaction pool to be accessed by the service.
86
+ * @returns The new service.
87
+ */ static async new(clientType, config, peerDiscoveryService, peerId, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, store, telemetry) {
88
+ const { tcpListenAddress, tcpAnnounceAddress, minPeerCount, maxPeerCount } = config;
89
+ const bindAddrTcp = convertToMultiaddr(tcpListenAddress, 'tcp');
90
+ // We know tcpAnnounceAddress cannot be null here because we set it or throw when setting up the service.
91
+ const announceAddrTcp = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
92
+ const datastore = new AztecDatastore(store);
93
+ const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
94
+ const node = await createLibp2p({
95
+ start: false,
96
+ peerId,
97
+ addresses: {
98
+ listen: [
99
+ bindAddrTcp
100
+ ],
101
+ announce: [
102
+ announceAddrTcp
103
+ ]
104
+ },
105
+ transports: [
106
+ tcp({
107
+ maxConnections: config.maxPeerCount,
108
+ // socket option: the maximum length of the queue of pending connections
109
+ // https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
110
+ // it's not safe if we increase this number
111
+ backlog: 5,
112
+ closeServerOnMaxConnections: {
113
+ closeAbove: maxPeerCount ?? Infinity,
114
+ listenBelow: maxPeerCount ?? Infinity
115
+ }
116
+ })
117
+ ],
118
+ datastore,
119
+ streamMuxers: [
120
+ yamux(),
121
+ mplex()
122
+ ],
123
+ connectionEncryption: [
124
+ noise()
125
+ ],
126
+ connectionManager: {
127
+ minConnections: minPeerCount,
128
+ maxConnections: maxPeerCount
129
+ },
130
+ services: {
131
+ identify: identify({
132
+ protocolPrefix: 'aztec'
133
+ }),
134
+ pubsub: gossipsub({
135
+ allowPublishToZeroTopicPeers: true,
136
+ D: config.gossipsubD,
137
+ Dlo: config.gossipsubDlo,
138
+ Dhi: config.gossipsubDhi,
139
+ heartbeatInterval: config.gossipsubInterval,
140
+ mcacheLength: config.gossipsubMcacheLength,
141
+ mcacheGossip: config.gossipsubMcacheGossip,
142
+ msgIdFn: getMsgIdFn,
143
+ msgIdToStrFn: msgIdToStrFn,
144
+ fastMsgIdFn: fastMsgIdFn,
145
+ dataTransform: new SnappyTransform(),
146
+ metricsRegister: otelMetricsAdapter,
147
+ metricsTopicStrToLabel: metricsTopicStrToLabels(),
148
+ asyncValidation: true,
149
+ scoreParams: createPeerScoreParams({
150
+ topics: {
151
+ [Tx.p2pTopic]: createTopicScoreParams({
152
+ topicWeight: 1,
153
+ invalidMessageDeliveriesWeight: -20,
154
+ invalidMessageDeliveriesDecay: 0.5
157
155
  }),
158
- }),
159
- components: (components) => ({
160
- connectionManager: components.connectionManager,
161
- }),
162
- },
163
- });
164
- return new _a(clientType, config, node, peerDiscoveryService, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, telemetry);
165
- }
166
- /**
167
- * Starts the LibP2P service.
168
- * @returns An empty promise.
169
- */
170
- async start() {
171
- // Check if service is already started
172
- if (this.node.status === 'started') {
173
- throw new Error('P2P service already started');
174
- }
175
- // Get listen & announce addresses for logging
176
- const { tcpListenAddress, tcpAnnounceAddress } = this.config;
177
- if (!tcpAnnounceAddress) {
178
- throw new Error('Announce address not provided.');
179
- }
180
- const announceTcpMultiaddr = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
181
- // Start job queue, peer discovery service and libp2p node
182
- this.jobQueue.start();
183
- await this.peerDiscoveryService.start();
184
- await this.node.start();
185
- // Subscribe to standard GossipSub topics by default
186
- for (const topic of getTopicTypeForClientType(this.clientType)) {
187
- this.subscribeToTopic(TopicTypeMap[topic].p2pTopic);
188
- }
189
- // Create request response protocol handlers
190
- const txHandler = reqRespTxHandler(this.mempools);
191
- const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
192
- const blockHandler = reqRespBlockHandler(this.l2BlockSource);
193
- const requestResponseHandlers = {
194
- [ReqRespSubProtocol.PING]: pingHandler,
195
- [ReqRespSubProtocol.STATUS]: statusHandler,
196
- [ReqRespSubProtocol.TX]: txHandler.bind(this),
197
- [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
198
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
199
- };
200
- // Add p2p topic validators
201
- // As they are stored within a kv pair, there is no need to register them conditionally
202
- // based on the client type
203
- const topicValidators = {
204
- [Tx.p2pTopic]: this.validatePropagatedTxFromMessage.bind(this),
205
- [BlockAttestation.p2pTopic]: this.validatePropagatedAttestationFromMessage.bind(this),
206
- [BlockProposal.p2pTopic]: this.validatePropagatedBlockFromMessage.bind(this),
207
- [EpochProofQuote.p2pTopic]: this.validatePropagatedEpochProofQuoteFromMessage.bind(this),
208
- };
209
- for (const [topic, validator] of Object.entries(topicValidators)) {
210
- this.node.services.pubsub.topicValidators.set(topic, validator);
211
- }
212
- // add GossipSub listener
213
- this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
214
- // Start running promise for peer discovery
215
- this.discoveryRunningPromise = new RunningPromise(() => this.peerManager.heartbeat(), this.logger, this.config.peerCheckIntervalMS);
216
- this.discoveryRunningPromise.start();
217
- // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
218
- const reqrespSubProtocolValidators = {
219
- ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
220
- // TODO(#11336): A request validator for blocks
221
- [ReqRespSubProtocol.TX]: this.validateRequestedTx.bind(this),
222
- };
223
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
224
- this.logger.info(`Started P2P service`, {
225
- listen: tcpListenAddress,
226
- announce: announceTcpMultiaddr,
227
- peerId: this.node.peerId.toString(),
228
- });
229
- }
230
- /**
231
- * Stops the LibP2P service.
232
- * @returns An empty promise.
233
- */
234
- async stop() {
235
- // Remove gossip sub listener
236
- this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
237
- // Stop peer manager
238
- this.logger.debug('Stopping peer manager...');
239
- await this.peerManager.stop();
240
- this.logger.debug('Stopping job queue...');
241
- await this.jobQueue.end();
242
- this.logger.debug('Stopping running promise...');
243
- await this.discoveryRunningPromise?.stop();
244
- this.logger.debug('Stopping peer discovery service...');
245
- await this.peerDiscoveryService.stop();
246
- this.logger.debug('Request response service stopped...');
247
- await this.reqresp.stop();
248
- this.logger.debug('Stopping LibP2P...');
249
- await this.stopLibP2P();
250
- this.logger.info('LibP2P service stopped');
251
- }
252
- getPeers(includePending) {
253
- return this.peerManager.getPeers(includePending);
254
- }
255
- handleGossipSubEvent(e) {
256
- const { msg } = e.detail;
257
- this.logger.trace(`Received PUBSUB message.`);
258
- void this.jobQueue
259
- .put(() => this.handleNewGossipMessage(msg))
260
- .catch(err => this.logger.error(`Error processing gossip message`, err));
261
- }
262
- /**
263
- * Send Request via the ReqResp service
264
- * The subprotocol defined will determine the request and response types
265
- *
266
- * See the subProtocolMap for the mapping of subprotocols to request/response types in `interface.ts`
267
- *
268
- * @param protocol The request response protocol to use
269
- * @param request The request type to send
270
- * @returns
271
- */
272
- sendRequest(protocol, request) {
273
- return this.reqresp.sendRequest(protocol, request);
274
- }
275
- /**
276
- * Send a batch of requests to peers, and return the responses
277
- * @param protocol - The request response protocol to use
278
- * @param requests - The requests to send to the peers
279
- * @returns The responses to the requests
280
- */
281
- sendBatchRequest(protocol, requests) {
282
- return this.reqresp.sendBatchRequest(protocol, requests);
283
- }
284
- /**
285
- * Get the ENR of the node
286
- * @returns The ENR of the node
287
- */
288
- getEnr() {
289
- return this.peerDiscoveryService.getEnr();
290
- }
291
- registerBlockReceivedCallback(callback) {
292
- this.blockReceivedCallback = callback;
293
- this.logger.verbose('Block received callback registered');
294
- }
295
- /**
296
- * Subscribes to a topic.
297
- * @param topic - The topic to subscribe to.
298
- */
299
- subscribeToTopic(topic) {
300
- if (!this.node.services.pubsub) {
301
- throw new Error('Pubsub service not available.');
302
- }
303
- void this.node.services.pubsub.subscribe(topic);
304
- }
305
- /**
306
- * Publishes data to a topic.
307
- * @param topic - The topic to publish to.
308
- * @param data - The data to publish.
309
- * @returns The number of recipients the data was sent to.
310
- */
311
- async publishToTopic(topic, data) {
312
- if (!this.node.services.pubsub) {
313
- throw new Error('Pubsub service not available.');
314
- }
315
- const result = await this.node.services.pubsub.publish(topic, data);
316
- return result.recipients.length;
317
- }
318
- /**
319
- * Handles a new gossip message that was received by the client.
320
- * @param topic - The message's topic.
321
- * @param data - The message data
322
- */
323
- async handleNewGossipMessage(message) {
324
- if (message.topic === Tx.p2pTopic) {
325
- const tx = Tx.fromBuffer(Buffer.from(message.data));
326
- await this.processTxFromPeer(tx);
327
- }
328
- if (message.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) {
329
- const attestation = BlockAttestation.fromBuffer(Buffer.from(message.data));
330
- await this.processAttestationFromPeer(attestation);
331
- }
332
- if (message.topic == BlockProposal.p2pTopic) {
333
- const block = BlockProposal.fromBuffer(Buffer.from(message.data));
334
- await this.processBlockFromPeer(block);
335
- }
336
- if (message.topic == EpochProofQuote.p2pTopic) {
337
- const epochProofQuote = EpochProofQuote.fromBuffer(Buffer.from(message.data));
338
- await this.processEpochProofQuoteFromPeer(epochProofQuote);
339
- }
340
- return;
341
- }
342
- /**Process Attestation From Peer
343
- * When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
344
- *
345
- * @param attestation - The attestation to process.
346
- */
347
- async processAttestationFromPeer(attestation) {
348
- this.logger.debug(`Received attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()} from external peer.`, {
349
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
350
- slot: attestation.slotNumber.toNumber(),
351
- archive: attestation.archive.toString(),
352
- block: attestation.blockNumber.toNumber(),
353
- });
354
- await this.mempools.attestationPool.addAttestations([attestation]);
355
- }
356
- /**Process block from peer
357
- *
358
- * Pass the received block to the validator client
359
- *
360
- * @param block - The block to process.
361
- */
362
- // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
363
- async processBlockFromPeer(block) {
364
- this.logger.verbose(`Received block ${block.blockNumber.toNumber()} for slot ${block.slotNumber.toNumber()} from external peer.`, {
365
- p2pMessageIdentifier: await block.p2pMessageIdentifier(),
366
- slot: block.slotNumber.toNumber(),
367
- archive: block.archive.toString(),
368
- block: block.blockNumber.toNumber(),
369
- });
370
- const attestation = await this.blockReceivedCallback(block);
371
- // TODO: fix up this pattern - the abstraction is not nice
372
- // The attestation can be undefined if no handler is registered / the validator deems the block invalid
373
- if (attestation != undefined) {
374
- this.logger.verbose(`Broadcasting attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()}`, {
375
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
376
- slot: attestation.slotNumber.toNumber(),
377
- archive: attestation.archive.toString(),
378
- block: attestation.blockNumber.toNumber(),
379
- });
380
- await this.broadcastAttestation(attestation);
381
- }
382
- }
383
- /**
384
- * Broadcast an attestation to all peers.
385
- * @param attestation - The attestation to broadcast.
386
- */
387
- async broadcastAttestation(attestation) {
388
- await this.propagate(attestation);
389
- }
390
- async processEpochProofQuoteFromPeer(epochProofQuote) {
391
- const epoch = epochProofQuote.payload.epochToProve;
392
- const prover = epochProofQuote.payload.prover.toString();
393
- const p2pMessageIdentifier = await epochProofQuote.p2pMessageIdentifier();
394
- this.logger.verbose(`Received epoch proof quote ${p2pMessageIdentifier} by prover ${prover} for epoch ${epoch} from external peer.`, { quote: epochProofQuote.toInspect(), p2pMessageIdentifier });
395
- this.mempools.epochProofQuotePool.addQuote(epochProofQuote);
396
- }
397
- /**
398
- * Propagates provided message to peers.
399
- * @param message - The message to propagate.
400
- */
401
- async propagate(message) {
402
- const p2pMessageIdentifier = await message.p2pMessageIdentifier();
403
- this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
404
- void this.jobQueue.put(async () => {
405
- await this.sendToPeers(message);
406
- });
407
- }
408
- async processTxFromPeer(tx) {
409
- const txHash = await tx.getTxHash();
410
- const txHashString = txHash.toString();
411
- this.logger.verbose(`Received tx ${txHashString} from external peer.`);
412
- await this.mempools.txPool.addTxs([tx]);
413
- }
414
- /**
415
- * Validate a tx that has been requested from a peer.
416
- *
417
- * The core component of this validator is that the tx hash MUST match the requested tx hash,
418
- * In order to perform this check, the tx proof must be verified.
419
- *
420
- * Note: This function is called from within `ReqResp.sendRequest` as part of the
421
- * ReqRespSubProtocol.TX subprotocol validation.
422
- *
423
- * @param requestedTxHash - The hash of the tx that was requested.
424
- * @param responseTx - The tx that was received as a response to the request.
425
- * @param peerId - The peer ID of the peer that sent the tx.
426
- * @returns True if the tx is valid, false otherwise.
427
- */
428
- async validateRequestedTx(requestedTxHash, responseTx, peerId) {
429
- const proofValidator = new TxProofValidator(this.proofVerifier);
430
- const validProof = await proofValidator.validateTx(responseTx);
431
- // If the node returns the wrong data, we penalize it
432
- if (!requestedTxHash.equals(await responseTx.getTxHash())) {
433
- // Returning the wrong data is a low tolerance error
434
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
435
- return false;
436
- }
437
- if (validProof.result === 'invalid') {
438
- // If the proof is invalid, but the txHash is correct, then this is an active attack and we severly punish
439
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
440
- return false;
441
- }
156
+ [BlockAttestation.p2pTopic]: createTopicScoreParams({
157
+ topicWeight: 1,
158
+ invalidMessageDeliveriesWeight: -20,
159
+ invalidMessageDeliveriesDecay: 0.5
160
+ }),
161
+ [BlockAttestation.p2pTopic]: createTopicScoreParams({
162
+ topicWeight: 1,
163
+ invalidMessageDeliveriesWeight: -20,
164
+ invalidMessageDeliveriesDecay: 0.5
165
+ }),
166
+ [EpochProofQuote.p2pTopic]: createTopicScoreParams({
167
+ topicWeight: 1,
168
+ invalidMessageDeliveriesWeight: -20,
169
+ invalidMessageDeliveriesDecay: 0.5
170
+ })
171
+ }
172
+ })
173
+ }),
174
+ components: (components)=>({
175
+ connectionManager: components.connectionManager
176
+ })
177
+ }
178
+ });
179
+ return new LibP2PService(clientType, config, node, peerDiscoveryService, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, telemetry);
180
+ }
181
+ /**
182
+ * Starts the LibP2P service.
183
+ * @returns An empty promise.
184
+ */ async start() {
185
+ // Check if service is already started
186
+ if (this.node.status === 'started') {
187
+ throw new Error('P2P service already started');
188
+ }
189
+ // Get listen & announce addresses for logging
190
+ const { tcpListenAddress, tcpAnnounceAddress } = this.config;
191
+ if (!tcpAnnounceAddress) {
192
+ throw new Error('Announce address not provided.');
193
+ }
194
+ const announceTcpMultiaddr = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
195
+ // Start job queue, peer discovery service and libp2p node
196
+ this.jobQueue.start();
197
+ await this.peerDiscoveryService.start();
198
+ await this.node.start();
199
+ // Subscribe to standard GossipSub topics by default
200
+ for (const topic of getTopicTypeForClientType(this.clientType)){
201
+ this.subscribeToTopic(TopicTypeMap[topic].p2pTopic);
202
+ }
203
+ // Create request response protocol handlers
204
+ const txHandler = reqRespTxHandler(this.mempools);
205
+ const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
206
+ const blockHandler = reqRespBlockHandler(this.l2BlockSource);
207
+ const requestResponseHandlers = {
208
+ [ReqRespSubProtocol.PING]: pingHandler,
209
+ [ReqRespSubProtocol.STATUS]: statusHandler,
210
+ [ReqRespSubProtocol.TX]: txHandler.bind(this),
211
+ [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
212
+ [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this)
213
+ };
214
+ // Add p2p topic validators
215
+ // As they are stored within a kv pair, there is no need to register them conditionally
216
+ // based on the client type
217
+ const topicValidators = {
218
+ [Tx.p2pTopic]: this.validatePropagatedTxFromMessage.bind(this),
219
+ [BlockAttestation.p2pTopic]: this.validatePropagatedAttestationFromMessage.bind(this),
220
+ [BlockProposal.p2pTopic]: this.validatePropagatedBlockFromMessage.bind(this),
221
+ [EpochProofQuote.p2pTopic]: this.validatePropagatedEpochProofQuoteFromMessage.bind(this)
222
+ };
223
+ for (const [topic, validator] of Object.entries(topicValidators)){
224
+ this.node.services.pubsub.topicValidators.set(topic, validator);
225
+ }
226
+ // add GossipSub listener
227
+ this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
228
+ // Start running promise for peer discovery
229
+ this.discoveryRunningPromise = new RunningPromise(()=>this.peerManager.heartbeat(), this.logger, this.config.peerCheckIntervalMS);
230
+ this.discoveryRunningPromise.start();
231
+ // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
232
+ const reqrespSubProtocolValidators = {
233
+ ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
234
+ // TODO(#11336): A request validator for blocks
235
+ [ReqRespSubProtocol.TX]: this.validateRequestedTx.bind(this)
236
+ };
237
+ await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
238
+ this.logger.info(`Started P2P service`, {
239
+ listen: tcpListenAddress,
240
+ announce: announceTcpMultiaddr,
241
+ peerId: this.node.peerId.toString()
242
+ });
243
+ }
244
+ /**
245
+ * Stops the LibP2P service.
246
+ * @returns An empty promise.
247
+ */ async stop() {
248
+ // Remove gossip sub listener
249
+ this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
250
+ // Stop peer manager
251
+ this.logger.debug('Stopping peer manager...');
252
+ await this.peerManager.stop();
253
+ this.logger.debug('Stopping job queue...');
254
+ await this.jobQueue.end();
255
+ this.logger.debug('Stopping running promise...');
256
+ await this.discoveryRunningPromise?.stop();
257
+ this.logger.debug('Stopping peer discovery service...');
258
+ await this.peerDiscoveryService.stop();
259
+ this.logger.debug('Request response service stopped...');
260
+ await this.reqresp.stop();
261
+ this.logger.debug('Stopping LibP2P...');
262
+ await this.stopLibP2P();
263
+ this.logger.info('LibP2P service stopped');
264
+ }
265
+ getPeers(includePending) {
266
+ return this.peerManager.getPeers(includePending);
267
+ }
268
+ handleGossipSubEvent(e) {
269
+ const { msg } = e.detail;
270
+ this.logger.trace(`Received PUBSUB message.`);
271
+ void this.jobQueue.put(()=>this.handleNewGossipMessage(msg)).catch((err)=>this.logger.error(`Error processing gossip message`, err));
272
+ }
273
+ /**
274
+ * Send Request via the ReqResp service
275
+ * The subprotocol defined will determine the request and response types
276
+ *
277
+ * See the subProtocolMap for the mapping of subprotocols to request/response types in `interface.ts`
278
+ *
279
+ * @param protocol The request response protocol to use
280
+ * @param request The request type to send
281
+ * @returns
282
+ */ sendRequest(protocol, request) {
283
+ return this.reqresp.sendRequest(protocol, request);
284
+ }
285
+ /**
286
+ * Send a batch of requests to peers, and return the responses
287
+ * @param protocol - The request response protocol to use
288
+ * @param requests - The requests to send to the peers
289
+ * @returns The responses to the requests
290
+ */ sendBatchRequest(protocol, requests) {
291
+ return this.reqresp.sendBatchRequest(protocol, requests);
292
+ }
293
+ /**
294
+ * Get the ENR of the node
295
+ * @returns The ENR of the node
296
+ */ getEnr() {
297
+ return this.peerDiscoveryService.getEnr();
298
+ }
299
+ registerBlockReceivedCallback(callback) {
300
+ this.blockReceivedCallback = callback;
301
+ this.logger.verbose('Block received callback registered');
302
+ }
303
+ /**
304
+ * Subscribes to a topic.
305
+ * @param topic - The topic to subscribe to.
306
+ */ subscribeToTopic(topic) {
307
+ if (!this.node.services.pubsub) {
308
+ throw new Error('Pubsub service not available.');
309
+ }
310
+ void this.node.services.pubsub.subscribe(topic);
311
+ }
312
+ /**
313
+ * Publishes data to a topic.
314
+ * @param topic - The topic to publish to.
315
+ * @param data - The data to publish.
316
+ * @returns The number of recipients the data was sent to.
317
+ */ async publishToTopic(topic, data) {
318
+ if (!this.node.services.pubsub) {
319
+ throw new Error('Pubsub service not available.');
320
+ }
321
+ const result = await this.node.services.pubsub.publish(topic, data);
322
+ return result.recipients.length;
323
+ }
324
+ /**
325
+ * Handles a new gossip message that was received by the client.
326
+ * @param topic - The message's topic.
327
+ * @param data - The message data
328
+ */ async handleNewGossipMessage(message) {
329
+ if (message.topic === Tx.p2pTopic) {
330
+ const tx = Tx.fromBuffer(Buffer.from(message.data));
331
+ await this.processTxFromPeer(tx);
332
+ }
333
+ if (message.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) {
334
+ const attestation = BlockAttestation.fromBuffer(Buffer.from(message.data));
335
+ await this.processAttestationFromPeer(attestation);
336
+ }
337
+ if (message.topic == BlockProposal.p2pTopic) {
338
+ const block = BlockProposal.fromBuffer(Buffer.from(message.data));
339
+ await this.processBlockFromPeer(block);
340
+ }
341
+ if (message.topic == EpochProofQuote.p2pTopic) {
342
+ const epochProofQuote = EpochProofQuote.fromBuffer(Buffer.from(message.data));
343
+ await this.processEpochProofQuoteFromPeer(epochProofQuote);
344
+ }
345
+ return;
346
+ }
347
+ /**Process Attestation From Peer
348
+ * When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
349
+ *
350
+ * @param attestation - The attestation to process.
351
+ */ async processAttestationFromPeer(attestation) {
352
+ this.logger.debug(`Received attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()} from external peer.`, {
353
+ p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
354
+ slot: attestation.slotNumber.toNumber(),
355
+ archive: attestation.archive.toString(),
356
+ block: attestation.blockNumber.toNumber()
357
+ });
358
+ await this.mempools.attestationPool.addAttestations([
359
+ attestation
360
+ ]);
361
+ }
362
+ /**Process block from peer
363
+ *
364
+ * Pass the received block to the validator client
365
+ *
366
+ * @param block - The block to process.
367
+ */ // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
368
+ async processBlockFromPeer(block) {
369
+ this.logger.verbose(`Received block ${block.blockNumber.toNumber()} for slot ${block.slotNumber.toNumber()} from external peer.`, {
370
+ p2pMessageIdentifier: await block.p2pMessageIdentifier(),
371
+ slot: block.slotNumber.toNumber(),
372
+ archive: block.archive.toString(),
373
+ block: block.blockNumber.toNumber()
374
+ });
375
+ const attestation = await this.blockReceivedCallback(block);
376
+ // TODO: fix up this pattern - the abstraction is not nice
377
+ // The attestation can be undefined if no handler is registered / the validator deems the block invalid
378
+ if (attestation != undefined) {
379
+ this.logger.verbose(`Broadcasting attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()}`, {
380
+ p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
381
+ slot: attestation.slotNumber.toNumber(),
382
+ archive: attestation.archive.toString(),
383
+ block: attestation.blockNumber.toNumber()
384
+ });
385
+ await this.broadcastAttestation(attestation);
386
+ }
387
+ }
388
+ /**
389
+ * Broadcast an attestation to all peers.
390
+ * @param attestation - The attestation to broadcast.
391
+ */ async broadcastAttestation(attestation) {
392
+ await this.propagate(attestation);
393
+ }
394
+ async processEpochProofQuoteFromPeer(epochProofQuote) {
395
+ const epoch = epochProofQuote.payload.epochToProve;
396
+ const prover = epochProofQuote.payload.prover.toString();
397
+ const p2pMessageIdentifier = await epochProofQuote.p2pMessageIdentifier();
398
+ this.logger.verbose(`Received epoch proof quote ${p2pMessageIdentifier} by prover ${prover} for epoch ${epoch} from external peer.`, {
399
+ quote: epochProofQuote.toInspect(),
400
+ p2pMessageIdentifier
401
+ });
402
+ this.mempools.epochProofQuotePool.addQuote(epochProofQuote);
403
+ }
404
+ /**
405
+ * Propagates provided message to peers.
406
+ * @param message - The message to propagate.
407
+ */ async propagate(message) {
408
+ const p2pMessageIdentifier = await message.p2pMessageIdentifier();
409
+ this.logger.trace(`Message ${p2pMessageIdentifier} queued`, {
410
+ p2pMessageIdentifier
411
+ });
412
+ void this.jobQueue.put(async ()=>{
413
+ await this.sendToPeers(message);
414
+ });
415
+ }
416
+ async processTxFromPeer(tx) {
417
+ const txHash = await tx.getTxHash();
418
+ const txHashString = txHash.toString();
419
+ this.logger.verbose(`Received tx ${txHashString} from external peer.`);
420
+ await this.mempools.txPool.addTxs([
421
+ tx
422
+ ]);
423
+ }
424
+ /**
425
+ * Validate a tx that has been requested from a peer.
426
+ *
427
+ * The core component of this validator is that the tx hash MUST match the requested tx hash,
428
+ * In order to perform this check, the tx proof must be verified.
429
+ *
430
+ * Note: This function is called from within `ReqResp.sendRequest` as part of the
431
+ * ReqRespSubProtocol.TX subprotocol validation.
432
+ *
433
+ * @param requestedTxHash - The hash of the tx that was requested.
434
+ * @param responseTx - The tx that was received as a response to the request.
435
+ * @param peerId - The peer ID of the peer that sent the tx.
436
+ * @returns True if the tx is valid, false otherwise.
437
+ */ async validateRequestedTx(requestedTxHash, responseTx, peerId) {
438
+ const proofValidator = new TxProofValidator(this.proofVerifier);
439
+ const validProof = await proofValidator.validateTx(responseTx);
440
+ // If the node returns the wrong data, we penalize it
441
+ if (!requestedTxHash.equals(await responseTx.getTxHash())) {
442
+ // Returning the wrong data is a low tolerance error
443
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
444
+ return false;
445
+ }
446
+ if (validProof.result === 'invalid') {
447
+ // If the proof is invalid, but the txHash is correct, then this is an active attack and we severly punish
448
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
449
+ return false;
450
+ }
451
+ return true;
452
+ }
453
+ /**
454
+ * Validate a tx from a peer.
455
+ * @param propagationSource - The peer ID of the peer that sent the tx.
456
+ * @param msg - The tx message.
457
+ * @returns True if the tx is valid, false otherwise.
458
+ */ async validatePropagatedTxFromMessage(propagationSource, msg) {
459
+ const tx = Tx.fromBuffer(Buffer.from(msg.data));
460
+ const isValid = await this.validatePropagatedTx(tx, propagationSource);
461
+ this.logger.trace(`validatePropagatedTx: ${isValid}`, {
462
+ [Attributes.TX_HASH]: (await tx.getTxHash()).toString(),
463
+ [Attributes.P2P_ID]: propagationSource.toString()
464
+ });
465
+ return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
466
+ }
467
+ /**
468
+ * Validate an attestation from a peer.
469
+ * @param propagationSource - The peer ID of the peer that sent the attestation.
470
+ * @param msg - The attestation message.
471
+ * @returns True if the attestation is valid, false otherwise.
472
+ */ async validatePropagatedAttestationFromMessage(propagationSource, msg) {
473
+ const attestation = BlockAttestation.fromBuffer(Buffer.from(msg.data));
474
+ const isValid = await this.validateAttestation(propagationSource, attestation);
475
+ this.logger.trace(`validatePropagatedAttestation: ${isValid}`, {
476
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString(),
477
+ [Attributes.P2P_ID]: propagationSource.toString()
478
+ });
479
+ return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
480
+ }
481
+ /**
482
+ * Validate a block proposal from a peer.
483
+ * @param propagationSource - The peer ID of the peer that sent the block.
484
+ * @param msg - The block proposal message.
485
+ * @returns True if the block proposal is valid, false otherwise.
486
+ */ async validatePropagatedBlockFromMessage(propagationSource, msg) {
487
+ const block = BlockProposal.fromBuffer(Buffer.from(msg.data));
488
+ const isValid = await this.validateBlockProposal(propagationSource, block);
489
+ this.logger.trace(`validatePropagatedBlock: ${isValid}`, {
490
+ [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
491
+ [Attributes.P2P_ID]: propagationSource.toString()
492
+ });
493
+ return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
494
+ }
495
+ /**
496
+ * Validate an epoch proof quote from a peer.
497
+ * @param propagationSource - The peer ID of the peer that sent the epoch proof quote.
498
+ * @param msg - The epoch proof quote message.
499
+ * @returns True if the epoch proof quote is valid, false otherwise.
500
+ */ async validatePropagatedEpochProofQuoteFromMessage(propagationSource, msg) {
501
+ const epochProofQuote = EpochProofQuote.fromBuffer(Buffer.from(msg.data));
502
+ const isValid = await this.validateEpochProofQuote(propagationSource, epochProofQuote);
503
+ this.logger.trace(`validatePropagatedEpochProofQuote: ${isValid}`, {
504
+ [Attributes.EPOCH_NUMBER]: epochProofQuote.payload.epochToProve.toString(),
505
+ [Attributes.P2P_ID]: propagationSource.toString()
506
+ });
507
+ return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
508
+ }
509
+ async validatePropagatedTx(tx, peerId) {
510
+ const blockNumber = await this.l2BlockSource.getBlockNumber() + 1;
511
+ const messageValidators = this.createMessageValidators(blockNumber);
512
+ const outcome = await this.runValidations(tx, messageValidators);
513
+ if (outcome.allPassed) {
514
+ return true;
515
+ }
516
+ const { name, severity } = outcome.failure;
517
+ // Double spend validator has a special case handler
518
+ if (name === 'doubleSpendValidator') {
519
+ const isValid = await this.handleDoubleSpendFailure(tx, blockNumber, peerId);
520
+ if (isValid) {
442
521
  return true;
443
522
  }
444
- /**
445
- * Validate a tx from a peer.
446
- * @param propagationSource - The peer ID of the peer that sent the tx.
447
- * @param msg - The tx message.
448
- * @returns True if the tx is valid, false otherwise.
449
- */
450
- async validatePropagatedTxFromMessage(propagationSource, msg) {
451
- const tx = Tx.fromBuffer(Buffer.from(msg.data));
452
- const isValid = await this.validatePropagatedTx(tx, propagationSource);
453
- this.logger.trace(`validatePropagatedTx: ${isValid}`, {
454
- [Attributes.TX_HASH]: (await tx.getTxHash()).toString(),
455
- [Attributes.P2P_ID]: propagationSource.toString(),
456
- });
457
- return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
458
- }
459
- /**
460
- * Validate an attestation from a peer.
461
- * @param propagationSource - The peer ID of the peer that sent the attestation.
462
- * @param msg - The attestation message.
463
- * @returns True if the attestation is valid, false otherwise.
464
- */
465
- async validatePropagatedAttestationFromMessage(propagationSource, msg) {
466
- const attestation = BlockAttestation.fromBuffer(Buffer.from(msg.data));
467
- const isValid = await this.validateAttestation(propagationSource, attestation);
468
- this.logger.trace(`validatePropagatedAttestation: ${isValid}`, {
469
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString(),
470
- [Attributes.P2P_ID]: propagationSource.toString(),
471
- });
472
- return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
473
- }
474
- /**
475
- * Validate a block proposal from a peer.
476
- * @param propagationSource - The peer ID of the peer that sent the block.
477
- * @param msg - The block proposal message.
478
- * @returns True if the block proposal is valid, false otherwise.
479
- */
480
- async validatePropagatedBlockFromMessage(propagationSource, msg) {
481
- const block = BlockProposal.fromBuffer(Buffer.from(msg.data));
482
- const isValid = await this.validateBlockProposal(propagationSource, block);
483
- this.logger.trace(`validatePropagatedBlock: ${isValid}`, {
484
- [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
485
- [Attributes.P2P_ID]: propagationSource.toString(),
486
- });
487
- return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
488
- }
489
- /**
490
- * Validate an epoch proof quote from a peer.
491
- * @param propagationSource - The peer ID of the peer that sent the epoch proof quote.
492
- * @param msg - The epoch proof quote message.
493
- * @returns True if the epoch proof quote is valid, false otherwise.
494
- */
495
- async validatePropagatedEpochProofQuoteFromMessage(propagationSource, msg) {
496
- const epochProofQuote = EpochProofQuote.fromBuffer(Buffer.from(msg.data));
497
- const isValid = await this.validateEpochProofQuote(propagationSource, epochProofQuote);
498
- this.logger.trace(`validatePropagatedEpochProofQuote: ${isValid}`, {
499
- [Attributes.EPOCH_NUMBER]: epochProofQuote.payload.epochToProve.toString(),
500
- [Attributes.P2P_ID]: propagationSource.toString(),
501
- });
502
- return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
503
- }
504
- async validatePropagatedTx(tx, peerId) {
505
- const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1;
506
- const messageValidators = this.createMessageValidators(blockNumber);
507
- const outcome = await this.runValidations(tx, messageValidators);
508
- if (outcome.allPassed) {
509
- return true;
510
- }
511
- const { name, severity } = outcome.failure;
512
- // Double spend validator has a special case handler
513
- if (name === 'doubleSpendValidator') {
514
- const isValid = await this.handleDoubleSpendFailure(tx, blockNumber, peerId);
515
- if (isValid) {
516
- return true;
517
- }
518
- }
519
- this.peerManager.penalizePeer(peerId, severity);
520
- return false;
521
- }
522
- /**
523
- * Create message validators for the given block number.
524
- *
525
- * Each validator is a pair of a validator and a severity.
526
- * If a validator fails, the peer is penalized with the severity of the validator.
527
- *
528
- * @param blockNumber - The block number to create validators for.
529
- * @returns The message validators.
530
- */
531
- createMessageValidators(blockNumber) {
532
- return {
533
- dataValidator: {
534
- validator: new DataTxValidator(),
535
- severity: PeerErrorSeverity.HighToleranceError,
536
- },
537
- metadataValidator: {
538
- validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)),
539
- severity: PeerErrorSeverity.HighToleranceError,
540
- },
541
- proofValidator: {
542
- validator: new TxProofValidator(this.proofVerifier),
543
- severity: PeerErrorSeverity.MidToleranceError,
544
- },
545
- doubleSpendValidator: {
546
- validator: new DoubleSpendTxValidator({
547
- nullifiersExist: async (nullifiers) => {
548
- const merkleTree = this.worldStateSynchronizer.getCommitted();
549
- const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
550
- return indices.map(index => index !== undefined);
551
- },
552
- }),
553
- severity: PeerErrorSeverity.HighToleranceError,
554
- },
555
- };
556
- }
557
- /**
558
- * Run validations on a tx.
559
- * @param tx - The tx to validate.
560
- * @param messageValidators - The message validators to run.
561
- * @returns The validation outcome.
562
- */
563
- async runValidations(tx, messageValidators) {
564
- const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
565
- const { result } = await validator.validateTx(tx);
566
- return { name, isValid: result === 'valid', severity };
567
- });
568
- // A promise that resolves when all validations have been run
569
- const allValidations = Promise.all(validationPromises);
570
- // A promise that resolves when the first validation fails
571
- const firstFailure = Promise.race(validationPromises.map(async (promise) => {
572
- const result = await promise;
573
- return result.isValid ? new Promise(() => { }) : result;
574
- }));
575
- // Wait for the first validation to fail or all validations to pass
576
- const result = await Promise.race([
577
- allValidations.then(() => ({ allPassed: true })),
578
- firstFailure.then(failure => ({ allPassed: false, failure: failure })),
579
- ]);
580
- // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
581
- return result;
582
- }
583
- /**
584
- * Handle a double spend failure.
585
- *
586
- * Double spend failures are managed on their own because they are a special case.
587
- * We must check if the double spend is recent or old, if it is past a threshold, then we heavily penalize the peer.
588
- *
589
- * @param tx - The tx that failed the double spend validator.
590
- * @param blockNumber - The block number of the tx.
591
- * @param peerId - The peer ID of the peer that sent the tx.
592
- * @returns True if the tx is valid, false otherwise.
593
- */
594
- async handleDoubleSpendFailure(tx, blockNumber, peerId) {
595
- if (blockNumber <= this.config.severePeerPenaltyBlockLength) {
596
- return false;
597
- }
598
- const snapshotValidator = new DoubleSpendTxValidator({
599
- nullifiersExist: async (nullifiers) => {
600
- const merkleTree = this.worldStateSynchronizer.getSnapshot(blockNumber - this.config.severePeerPenaltyBlockLength);
523
+ }
524
+ this.peerManager.penalizePeer(peerId, severity);
525
+ return false;
526
+ }
527
+ /**
528
+ * Create message validators for the given block number.
529
+ *
530
+ * Each validator is a pair of a validator and a severity.
531
+ * If a validator fails, the peer is penalized with the severity of the validator.
532
+ *
533
+ * @param blockNumber - The block number to create validators for.
534
+ * @returns The message validators.
535
+ */ createMessageValidators(blockNumber) {
536
+ return {
537
+ dataValidator: {
538
+ validator: new DataTxValidator(),
539
+ severity: PeerErrorSeverity.HighToleranceError
540
+ },
541
+ metadataValidator: {
542
+ validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)),
543
+ severity: PeerErrorSeverity.HighToleranceError
544
+ },
545
+ proofValidator: {
546
+ validator: new TxProofValidator(this.proofVerifier),
547
+ severity: PeerErrorSeverity.MidToleranceError
548
+ },
549
+ doubleSpendValidator: {
550
+ validator: new DoubleSpendTxValidator({
551
+ nullifiersExist: async (nullifiers)=>{
552
+ const merkleTree = this.worldStateSynchronizer.getCommitted();
601
553
  const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
602
- return indices.map(index => index !== undefined);
603
- },
604
- });
605
- const validSnapshot = await snapshotValidator.validateTx(tx);
606
- if (validSnapshot.result !== 'valid') {
607
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
608
- return false;
609
- }
610
- return true;
611
- }
612
- /**
613
- * Validate an attestation.
614
- *
615
- * @param attestation - The attestation to validate.
616
- * @returns True if the attestation is valid, false otherwise.
617
- */
618
- async validateAttestation(peerId, attestation) {
619
- const severity = await this.attestationValidator.validate(attestation);
620
- if (severity) {
621
- this.peerManager.penalizePeer(peerId, severity);
622
- return false;
623
- }
624
- return true;
625
- }
626
- /**
627
- * Validate a block proposal.
628
- *
629
- * @param block - The block proposal to validate.
630
- * @returns True if the block proposal is valid, false otherwise.
631
- */
632
- async validateBlockProposal(peerId, block) {
633
- const severity = await this.blockProposalValidator.validate(block);
634
- if (severity) {
635
- this.peerManager.penalizePeer(peerId, severity);
636
- return false;
637
- }
638
- return true;
639
- }
640
- /**
641
- * Validate an epoch proof quote.
642
- *
643
- * @param epochProofQuote - The epoch proof quote to validate.
644
- * @returns True if the epoch proof quote is valid, false otherwise.
645
- */
646
- async validateEpochProofQuote(peerId, epochProofQuote) {
647
- const severity = await this.epochProofQuoteValidator.validate(epochProofQuote);
648
- if (severity) {
649
- this.peerManager.penalizePeer(peerId, severity);
650
- return false;
651
- }
652
- return true;
653
- }
654
- getPeerScore(peerId) {
655
- return this.node.services.pubsub.score.score(peerId.toString());
656
- }
657
- async sendToPeers(message) {
658
- const parent = message.constructor;
659
- const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
660
- this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
661
- const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
662
- this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, { p2pMessageIdentifier: identifier });
663
- }
664
- // Libp2p seems to hang sometimes if new peers are initiating connections.
665
- async stopLibP2P() {
666
- const TIMEOUT_MS = 5000; // 5 seconds timeout
667
- const timeout = new Promise((_resolve, reject) => {
668
- setTimeout(() => reject(new Error('Timeout during libp2p.stop()')), TIMEOUT_MS);
669
- });
670
- try {
671
- await Promise.race([this.node.stop(), timeout]);
672
- this.logger.debug('LibP2P stopped');
673
- }
674
- catch (error) {
675
- this.logger.error('Error during stop or timeout:', error);
676
- }
677
- }
678
- },
679
- (() => {
680
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
681
- _processAttestationFromPeer_decorators = [trackSpan('Libp2pService.processAttestationFromPeer', async (attestation) => ({
682
- [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
683
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
684
- [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
685
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
686
- }))];
687
- _processBlockFromPeer_decorators = [trackSpan('Libp2pService.processBlockFromPeer', async (block) => ({
688
- [Attributes.BLOCK_NUMBER]: block.blockNumber.toNumber(),
689
- [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
690
- [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
691
- [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then(i => i.toString()),
692
- }))];
693
- _broadcastAttestation_decorators = [trackSpan('Libp2pService.broadcastAttestation', async (attestation) => ({
694
- [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
695
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
696
- [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
697
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
698
- }))];
699
- _validateRequestedTx_decorators = [trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
700
- [Attributes.TX_HASH]: requestedTxHash.toString(),
701
- }))];
702
- _validatePropagatedTx_decorators = [trackSpan('Libp2pService.validatePropagatedTx', async (tx) => ({
703
- [Attributes.TX_HASH]: (await tx.getTxHash()).toString(),
704
- }))];
705
- _validateAttestation_decorators = [trackSpan('Libp2pService.validateAttestation', (_peerId, attestation) => ({
706
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString(),
707
- }))];
708
- _validateBlockProposal_decorators = [trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
709
- [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
710
- }))];
711
- _validateEpochProofQuote_decorators = [trackSpan('Libp2pService.validateEpochProofQuote', (_peerId, epochProofQuote) => ({
712
- [Attributes.EPOCH_NUMBER]: epochProofQuote.payload.epochToProve.toString(),
713
- }))];
714
- __esDecorate(_a, null, _processAttestationFromPeer_decorators, { kind: "method", name: "processAttestationFromPeer", static: false, private: false, access: { has: obj => "processAttestationFromPeer" in obj, get: obj => obj.processAttestationFromPeer }, metadata: _metadata }, null, _instanceExtraInitializers);
715
- __esDecorate(_a, null, _processBlockFromPeer_decorators, { kind: "method", name: "processBlockFromPeer", static: false, private: false, access: { has: obj => "processBlockFromPeer" in obj, get: obj => obj.processBlockFromPeer }, metadata: _metadata }, null, _instanceExtraInitializers);
716
- __esDecorate(_a, null, _broadcastAttestation_decorators, { kind: "method", name: "broadcastAttestation", static: false, private: false, access: { has: obj => "broadcastAttestation" in obj, get: obj => obj.broadcastAttestation }, metadata: _metadata }, null, _instanceExtraInitializers);
717
- __esDecorate(_a, null, _validateRequestedTx_decorators, { kind: "method", name: "validateRequestedTx", static: false, private: false, access: { has: obj => "validateRequestedTx" in obj, get: obj => obj.validateRequestedTx }, metadata: _metadata }, null, _instanceExtraInitializers);
718
- __esDecorate(_a, null, _validatePropagatedTx_decorators, { kind: "method", name: "validatePropagatedTx", static: false, private: false, access: { has: obj => "validatePropagatedTx" in obj, get: obj => obj.validatePropagatedTx }, metadata: _metadata }, null, _instanceExtraInitializers);
719
- __esDecorate(_a, null, _validateAttestation_decorators, { kind: "method", name: "validateAttestation", static: false, private: false, access: { has: obj => "validateAttestation" in obj, get: obj => obj.validateAttestation }, metadata: _metadata }, null, _instanceExtraInitializers);
720
- __esDecorate(_a, null, _validateBlockProposal_decorators, { kind: "method", name: "validateBlockProposal", static: false, private: false, access: { has: obj => "validateBlockProposal" in obj, get: obj => obj.validateBlockProposal }, metadata: _metadata }, null, _instanceExtraInitializers);
721
- __esDecorate(_a, null, _validateEpochProofQuote_decorators, { kind: "method", name: "validateEpochProofQuote", static: false, private: false, access: { has: obj => "validateEpochProofQuote" in obj, get: obj => obj.validateEpochProofQuote }, metadata: _metadata }, null, _instanceExtraInitializers);
722
- if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
723
- })(),
724
- _a;
725
- })();
726
- export { LibP2PService };
727
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlicDJwX3NlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmljZXMvbGlicDJwL2xpYnAycF9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLGFBQWEsRUFFYixlQUFlLEVBR2YsWUFBWSxFQUNaLGFBQWEsRUFDYixpQkFBaUIsRUFHakIsWUFBWSxFQUNaLEVBQUUsRUFJRix5QkFBeUIsRUFDekIsdUJBQXVCLEdBQ3hCLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRXhDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBRW5FLE9BQU8sRUFBRSxVQUFVLEVBQUUsa0JBQWtCLEVBQXdCLFVBQVUsRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUd0SCxPQUFPLEVBSUwsU0FBUyxHQUNWLE1BQU0sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLHNCQUFzQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDbEcsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2hELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUE2QixvQkFBb0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXBGLE9BQU8saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1B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
554
+ return indices.map((index)=>index !== undefined);
555
+ }
556
+ }),
557
+ severity: PeerErrorSeverity.HighToleranceError
558
+ }
559
+ };
560
+ }
561
+ /**
562
+ * Run validations on a tx.
563
+ * @param tx - The tx to validate.
564
+ * @param messageValidators - The message validators to run.
565
+ * @returns The validation outcome.
566
+ */ async runValidations(tx, messageValidators) {
567
+ const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }])=>{
568
+ const { result } = await validator.validateTx(tx);
569
+ return {
570
+ name,
571
+ isValid: result === 'valid',
572
+ severity
573
+ };
574
+ });
575
+ // A promise that resolves when all validations have been run
576
+ const allValidations = Promise.all(validationPromises);
577
+ // A promise that resolves when the first validation fails
578
+ const firstFailure = Promise.race(validationPromises.map(async (promise)=>{
579
+ const result = await promise;
580
+ return result.isValid ? new Promise(()=>{}) : result;
581
+ }));
582
+ // Wait for the first validation to fail or all validations to pass
583
+ const result = await Promise.race([
584
+ allValidations.then(()=>({
585
+ allPassed: true
586
+ })),
587
+ firstFailure.then((failure)=>({
588
+ allPassed: false,
589
+ failure: failure
590
+ }))
591
+ ]);
592
+ // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
593
+ return result;
594
+ }
595
+ /**
596
+ * Handle a double spend failure.
597
+ *
598
+ * Double spend failures are managed on their own because they are a special case.
599
+ * We must check if the double spend is recent or old, if it is past a threshold, then we heavily penalize the peer.
600
+ *
601
+ * @param tx - The tx that failed the double spend validator.
602
+ * @param blockNumber - The block number of the tx.
603
+ * @param peerId - The peer ID of the peer that sent the tx.
604
+ * @returns True if the tx is valid, false otherwise.
605
+ */ async handleDoubleSpendFailure(tx, blockNumber, peerId) {
606
+ if (blockNumber <= this.config.severePeerPenaltyBlockLength) {
607
+ return false;
608
+ }
609
+ const snapshotValidator = new DoubleSpendTxValidator({
610
+ nullifiersExist: async (nullifiers)=>{
611
+ const merkleTree = this.worldStateSynchronizer.getSnapshot(blockNumber - this.config.severePeerPenaltyBlockLength);
612
+ const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
613
+ return indices.map((index)=>index !== undefined);
614
+ }
615
+ });
616
+ const validSnapshot = await snapshotValidator.validateTx(tx);
617
+ if (validSnapshot.result !== 'valid') {
618
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
619
+ return false;
620
+ }
621
+ return true;
622
+ }
623
+ /**
624
+ * Validate an attestation.
625
+ *
626
+ * @param attestation - The attestation to validate.
627
+ * @returns True if the attestation is valid, false otherwise.
628
+ */ async validateAttestation(peerId, attestation) {
629
+ const severity = await this.attestationValidator.validate(attestation);
630
+ if (severity) {
631
+ this.peerManager.penalizePeer(peerId, severity);
632
+ return false;
633
+ }
634
+ return true;
635
+ }
636
+ /**
637
+ * Validate a block proposal.
638
+ *
639
+ * @param block - The block proposal to validate.
640
+ * @returns True if the block proposal is valid, false otherwise.
641
+ */ async validateBlockProposal(peerId, block) {
642
+ const severity = await this.blockProposalValidator.validate(block);
643
+ if (severity) {
644
+ this.peerManager.penalizePeer(peerId, severity);
645
+ return false;
646
+ }
647
+ return true;
648
+ }
649
+ /**
650
+ * Validate an epoch proof quote.
651
+ *
652
+ * @param epochProofQuote - The epoch proof quote to validate.
653
+ * @returns True if the epoch proof quote is valid, false otherwise.
654
+ */ async validateEpochProofQuote(peerId, epochProofQuote) {
655
+ const severity = await this.epochProofQuoteValidator.validate(epochProofQuote);
656
+ if (severity) {
657
+ this.peerManager.penalizePeer(peerId, severity);
658
+ return false;
659
+ }
660
+ return true;
661
+ }
662
+ getPeerScore(peerId) {
663
+ return this.node.services.pubsub.score.score(peerId.toString());
664
+ }
665
+ async sendToPeers(message) {
666
+ const parent = message.constructor;
667
+ const identifier = await message.p2pMessageIdentifier().then((i)=>i.toString());
668
+ this.logger.trace(`Sending message ${identifier}`, {
669
+ p2pMessageIdentifier: identifier
670
+ });
671
+ const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
672
+ this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
673
+ p2pMessageIdentifier: identifier
674
+ });
675
+ }
676
+ // Libp2p seems to hang sometimes if new peers are initiating connections.
677
+ async stopLibP2P() {
678
+ const TIMEOUT_MS = 5000; // 5 seconds timeout
679
+ const timeout = new Promise((_resolve, reject)=>{
680
+ setTimeout(()=>reject(new Error('Timeout during libp2p.stop()')), TIMEOUT_MS);
681
+ });
682
+ try {
683
+ await Promise.race([
684
+ this.node.stop(),
685
+ timeout
686
+ ]);
687
+ this.logger.debug('LibP2P stopped');
688
+ } catch (error) {
689
+ this.logger.error('Error during stop or timeout:', error);
690
+ }
691
+ }
692
+ }
693
+ _ts_decorate([
694
+ trackSpan('Libp2pService.processAttestationFromPeer', async (attestation)=>({
695
+ [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
696
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
697
+ [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
698
+ [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then((i)=>i.toString())
699
+ }))
700
+ ], LibP2PService.prototype, "processAttestationFromPeer", null);
701
+ _ts_decorate([
702
+ trackSpan('Libp2pService.processBlockFromPeer', async (block)=>({
703
+ [Attributes.BLOCK_NUMBER]: block.blockNumber.toNumber(),
704
+ [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
705
+ [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
706
+ [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then((i)=>i.toString())
707
+ }))
708
+ ], LibP2PService.prototype, "processBlockFromPeer", null);
709
+ _ts_decorate([
710
+ trackSpan('Libp2pService.broadcastAttestation', async (attestation)=>({
711
+ [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
712
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
713
+ [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
714
+ [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then((i)=>i.toString())
715
+ }))
716
+ ], LibP2PService.prototype, "broadcastAttestation", null);
717
+ _ts_decorate([
718
+ trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx)=>({
719
+ [Attributes.TX_HASH]: requestedTxHash.toString()
720
+ }))
721
+ ], LibP2PService.prototype, "validateRequestedTx", null);
722
+ _ts_decorate([
723
+ trackSpan('Libp2pService.validatePropagatedTx', async (tx)=>({
724
+ [Attributes.TX_HASH]: (await tx.getTxHash()).toString()
725
+ }))
726
+ ], LibP2PService.prototype, "validatePropagatedTx", null);
727
+ _ts_decorate([
728
+ trackSpan('Libp2pService.validateAttestation', (_peerId, attestation)=>({
729
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString()
730
+ }))
731
+ ], LibP2PService.prototype, "validateAttestation", null);
732
+ _ts_decorate([
733
+ trackSpan('Libp2pService.validateBlockProposal', (_peerId, block)=>({
734
+ [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString()
735
+ }))
736
+ ], LibP2PService.prototype, "validateBlockProposal", null);
737
+ _ts_decorate([
738
+ trackSpan('Libp2pService.validateEpochProofQuote', (_peerId, epochProofQuote)=>({
739
+ [Attributes.EPOCH_NUMBER]: epochProofQuote.payload.epochToProve.toString()
740
+ }))
741
+ ], LibP2PService.prototype, "validateEpochProofQuote", null);