@aztec/p2p 0.75.0 → 0.76.0

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 (102) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/bootstrap/bootstrap.js +9 -4
  3. package/dest/client/factory.d.ts +4 -2
  4. package/dest/client/factory.d.ts.map +1 -1
  5. package/dest/client/factory.js +4 -4
  6. package/dest/config.d.ts +30 -14
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +30 -14
  9. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +2 -2
  10. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  11. package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
  12. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +2 -2
  13. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  14. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +1 -1
  15. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts +2 -2
  16. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts.map +1 -1
  17. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.js +1 -1
  18. package/dest/services/discv5/discV5_service.d.ts +5 -1
  19. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  20. package/dest/services/discv5/discV5_service.js +65 -18
  21. package/dest/services/dummy_service.d.ts +1 -0
  22. package/dest/services/dummy_service.d.ts.map +1 -1
  23. package/dest/services/dummy_service.js +2 -1
  24. package/dest/services/libp2p/libp2p_logger.d.ts +7 -0
  25. package/dest/services/libp2p/libp2p_logger.d.ts.map +1 -0
  26. package/dest/services/libp2p/libp2p_logger.js +67 -0
  27. package/dest/services/libp2p/libp2p_service.d.ts +3 -3
  28. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  29. package/dest/services/libp2p/libp2p_service.js +42 -10
  30. package/dest/services/reqresp/interface.d.ts +9 -0
  31. package/dest/services/reqresp/interface.d.ts.map +1 -1
  32. package/dest/services/reqresp/interface.js +1 -1
  33. package/dest/services/reqresp/protocols/goodbye.js +2 -2
  34. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  35. package/dest/services/reqresp/rate-limiter/rate_limiter.js +4 -2
  36. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -3
  37. package/dest/services/reqresp/reqresp.d.ts +7 -2
  38. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  39. package/dest/services/reqresp/reqresp.js +90 -21
  40. package/dest/services/reqresp/status.d.ts +31 -0
  41. package/dest/services/reqresp/status.d.ts.map +1 -0
  42. package/dest/services/reqresp/status.js +52 -0
  43. package/dest/services/service.d.ts +1 -0
  44. package/dest/services/service.d.ts.map +1 -1
  45. package/dest/services/types.d.ts +1 -7
  46. package/dest/services/types.d.ts.map +1 -1
  47. package/dest/services/types.js +2 -10
  48. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +7 -0
  49. package/dest/test-helpers/generate-peer-id-private-keys.d.ts.map +1 -0
  50. package/dest/test-helpers/generate-peer-id-private-keys.js +15 -0
  51. package/dest/test-helpers/get-ports.d.ts +7 -0
  52. package/dest/test-helpers/get-ports.d.ts.map +1 -0
  53. package/dest/test-helpers/get-ports.js +8 -0
  54. package/dest/test-helpers/index.d.ts +6 -0
  55. package/dest/test-helpers/index.d.ts.map +1 -0
  56. package/dest/test-helpers/index.js +6 -0
  57. package/dest/test-helpers/make-enrs.d.ts +16 -0
  58. package/dest/test-helpers/make-enrs.d.ts.map +1 -0
  59. package/dest/test-helpers/make-enrs.js +35 -0
  60. package/dest/test-helpers/make-test-p2p-clients.d.ts +37 -0
  61. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -0
  62. package/dest/test-helpers/make-test-p2p-clients.js +71 -0
  63. package/dest/{mocks/index.d.ts → test-helpers/reqresp-nodes.d.ts} +6 -5
  64. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -0
  65. package/dest/test-helpers/reqresp-nodes.js +183 -0
  66. package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -0
  67. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -0
  68. package/dest/testbench/p2p_client_testbench_worker.js +125 -0
  69. package/dest/versioning.d.ts +12 -0
  70. package/dest/versioning.d.ts.map +1 -0
  71. package/dest/versioning.js +38 -0
  72. package/package.json +10 -8
  73. package/src/bootstrap/bootstrap.ts +9 -3
  74. package/src/client/factory.ts +12 -5
  75. package/src/config.ts +56 -29
  76. package/src/msg_validators/attestation_validator/attestation_validator.ts +3 -3
  77. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +3 -3
  78. package/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.ts +3 -3
  79. package/src/services/discv5/discV5_service.ts +67 -18
  80. package/src/services/dummy_service.ts +2 -0
  81. package/src/services/libp2p/libp2p_logger.ts +78 -0
  82. package/src/services/libp2p/libp2p_service.ts +47 -10
  83. package/src/services/reqresp/interface.ts +11 -0
  84. package/src/services/reqresp/protocols/goodbye.ts +1 -1
  85. package/src/services/reqresp/rate-limiter/rate_limiter.ts +3 -1
  86. package/src/services/reqresp/rate-limiter/rate_limits.ts +2 -2
  87. package/src/services/reqresp/reqresp.ts +120 -25
  88. package/src/services/reqresp/status.ts +59 -0
  89. package/src/services/service.ts +2 -0
  90. package/src/services/types.ts +2 -10
  91. package/src/test-helpers/generate-peer-id-private-keys.ts +15 -0
  92. package/src/test-helpers/get-ports.ts +8 -0
  93. package/src/test-helpers/index.ts +5 -0
  94. package/src/test-helpers/make-enrs.ts +44 -0
  95. package/src/test-helpers/make-test-p2p-clients.ts +124 -0
  96. package/src/{mocks/index.ts → test-helpers/reqresp-nodes.ts} +10 -5
  97. package/src/testbench/README.md +20 -0
  98. package/src/testbench/p2p_client_testbench_worker.ts +156 -0
  99. package/src/testbench/scripts/run_testbench.sh +7 -0
  100. package/src/versioning.ts +50 -0
  101. package/dest/mocks/index.d.ts.map +0 -1
  102. package/dest/mocks/index.js +0 -181
@@ -0,0 +1,156 @@
1
+ /**
2
+ * A testbench worker that creates a p2p client and listens for commands from the parent.
3
+ *
4
+ * Used when running testbench commands
5
+ */
6
+ import { MockL2BlockSource } from '@aztec/archiver/test';
7
+ import { P2PClientType, Tx, TxStatus, type WorldStateSynchronizer } from '@aztec/circuit-types';
8
+ import { type EpochCacheInterface } from '@aztec/epoch-cache';
9
+ import { EthAddress } from '@aztec/foundation/eth-address';
10
+ import { createLogger } from '@aztec/foundation/log';
11
+ import { sleep } from '@aztec/foundation/sleep';
12
+ import { type DataStoreConfig } from '@aztec/kv-store/config';
13
+ import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
14
+
15
+ import { type P2PConfig } from '../config.js';
16
+ import { createP2PClient } from '../index.js';
17
+ import { type AttestationPool } from '../mem_pools/attestation_pool/attestation_pool.js';
18
+ import { type EpochProofQuotePool } from '../mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.js';
19
+ import { type TxPool } from '../mem_pools/tx_pool/index.js';
20
+ import { AlwaysTrueCircuitVerifier } from '../test-helpers/reqresp-nodes.js';
21
+
22
+ // Simple mock implementation
23
+ function mockTxPool(): TxPool {
24
+ // Mock all methods
25
+ return {
26
+ addTxs: () => Promise.resolve(),
27
+ getTxByHash: () => Promise.resolve(undefined),
28
+ getArchivedTxByHash: () => Promise.resolve(undefined),
29
+ markAsMined: () => Promise.resolve(),
30
+ markMinedAsPending: () => Promise.resolve(),
31
+ deleteTxs: () => Promise.resolve(),
32
+ getAllTxs: () => Promise.resolve([]),
33
+ getAllTxHashes: () => Promise.resolve([]),
34
+ getPendingTxHashes: () => Promise.resolve([]),
35
+ getMinedTxHashes: () => Promise.resolve([]),
36
+ getTxStatus: () => Promise.resolve(TxStatus.PENDING),
37
+ };
38
+ }
39
+
40
+ function mockAttestationPool(): AttestationPool {
41
+ return {
42
+ addAttestations: () => Promise.resolve(),
43
+ deleteAttestations: () => Promise.resolve(),
44
+ deleteAttestationsOlderThan: () => Promise.resolve(),
45
+ deleteAttestationsForSlot: () => Promise.resolve(),
46
+ deleteAttestationsForSlotAndProposal: () => Promise.resolve(),
47
+ getAttestationsForSlot: () => Promise.resolve([]),
48
+ };
49
+ }
50
+
51
+ function mockEpochProofQuotePool(): EpochProofQuotePool {
52
+ return {
53
+ addQuote: () => {},
54
+ getQuotes: () => [],
55
+ deleteQuotesToEpoch: () => {},
56
+ };
57
+ }
58
+
59
+ function mockEpochCache(): EpochCacheInterface {
60
+ return {
61
+ getCommittee: () => Promise.resolve([] as EthAddress[]),
62
+ getProposerIndexEncoding: () => '0x' as `0x${string}`,
63
+ getEpochAndSlotNow: () => ({ epoch: 0n, slot: 0n, ts: 0n }),
64
+ computeProposerIndex: () => 0n,
65
+ getProposerInCurrentOrNextSlot: () =>
66
+ Promise.resolve({
67
+ currentProposer: EthAddress.ZERO,
68
+ nextProposer: EthAddress.ZERO,
69
+ currentSlot: 0n,
70
+ nextSlot: 0n,
71
+ }),
72
+ isInCommittee: () => Promise.resolve(false),
73
+ };
74
+ }
75
+
76
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
77
+ process.on('message', async msg => {
78
+ const { type, config, clientIndex } = msg as { type: string; config: P2PConfig; clientIndex: number };
79
+
80
+ try {
81
+ if (type === 'START') {
82
+ const txPool = mockTxPool();
83
+ const attestationPool = mockAttestationPool();
84
+ const epochProofQuotePool = mockEpochProofQuotePool();
85
+ const epochCache = mockEpochCache();
86
+ const worldState = {} as WorldStateSynchronizer;
87
+ const l2BlockSource = new MockL2BlockSource();
88
+ await l2BlockSource.createBlocks(100);
89
+
90
+ const proofVerifier = new AlwaysTrueCircuitVerifier();
91
+ const kvStore = await openTmpStore(`test-${clientIndex}`);
92
+ const logger = createLogger(`p2p:${clientIndex}`);
93
+
94
+ const deps = {
95
+ txPool,
96
+ attestationPool,
97
+ epochProofQuotePool,
98
+ store: kvStore,
99
+ logger,
100
+ };
101
+
102
+ const client = await createP2PClient(
103
+ P2PClientType.Full,
104
+ config as P2PConfig & DataStoreConfig,
105
+ l2BlockSource,
106
+ proofVerifier,
107
+ worldState,
108
+ epochCache,
109
+ undefined,
110
+ deps,
111
+ );
112
+
113
+ // Create spy for gossip messages
114
+ let gossipMessageCount = 0;
115
+ (client as any).p2pService.handleNewGossipMessage = (...args: any[]) => {
116
+ gossipMessageCount++;
117
+ process.send!({ type: 'GOSSIP_RECEIVED', count: gossipMessageCount });
118
+ return (client as any).p2pService.constructor.prototype.handleNewGossipMessage.apply(
119
+ (client as any).p2pService,
120
+ args,
121
+ );
122
+ };
123
+
124
+ await client.start();
125
+ // Wait until the client is ready
126
+ for (let i = 0; i < 100; i++) {
127
+ const isReady = client.isReady();
128
+ logger.debug(`Client ${clientIndex} isReady: ${isReady}`);
129
+ if (isReady) {
130
+ break;
131
+ }
132
+ await sleep(1000);
133
+ }
134
+
135
+ // Listen for commands from parent
136
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
137
+ process.on('message', async (cmd: any) => {
138
+ switch (cmd.type) {
139
+ case 'STOP':
140
+ await client.stop();
141
+ process.exit(0);
142
+ break;
143
+ case 'SEND_TX':
144
+ await client.sendTx(Tx.fromBuffer(Buffer.from(cmd.tx)));
145
+ process.send!({ type: 'TX_SENT' });
146
+ break;
147
+ }
148
+ });
149
+
150
+ process.send!({ type: 'READY' });
151
+ }
152
+ } catch (err: any) {
153
+ process.send!({ type: 'ERROR', error: err.message });
154
+ process.exit(1);
155
+ }
156
+ });
@@ -0,0 +1,7 @@
1
+ ## Test bench
2
+ # Run the testbench and pipe the output into a file
3
+ # Usage: ./run_testbench.sh <outputfile>
4
+
5
+ outputfile=$1
6
+
7
+ LOG_LEVEL="debug; trace: .*gossipsub" yarn test testbench.test.ts 2>&1 | pino-pretty > $outputfile
@@ -0,0 +1,50 @@
1
+ import {
2
+ type ComponentsVersions,
3
+ checkCompressedComponentVersion,
4
+ compressComponentVersions,
5
+ getComponentsVersionsFromConfig,
6
+ } from '@aztec/circuit-types';
7
+ import { type ChainConfig } from '@aztec/circuit-types/config';
8
+ import { toBufferBE } from '@aztec/foundation/bigint-buffer';
9
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vks';
10
+ import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
11
+
12
+ import { type SignableENR } from '@chainsafe/enr';
13
+ import xxhashFactory from 'xxhash-wasm';
14
+
15
+ import { AZTEC_ENR_KEY } from './services/types.js';
16
+
17
+ const USE_XX_HASH = false; // Enable to reduce the size of the ENR record for production
18
+ const XX_HASH_LEN = 8;
19
+ const xxhash = await xxhashFactory();
20
+
21
+ /** Returns the component versions based on config and this build. */
22
+ export function getVersions(config: ChainConfig) {
23
+ return getComponentsVersionsFromConfig(config, protocolContractTreeRoot, getVKTreeRoot());
24
+ }
25
+
26
+ /** Sets the aztec key on the ENR record with versioning info. */
27
+ export function setAztecEnrKey(enr: SignableENR, config: ChainConfig, useXxHash = USE_XX_HASH) {
28
+ const versions = getVersions(config);
29
+ const value = versionsToEnrValue(versions, useXxHash);
30
+ enr.set(AZTEC_ENR_KEY, value);
31
+ return versions;
32
+ }
33
+
34
+ /** Checks the given value from an ENR record against the expected versions. */
35
+ export function checkAztecEnrVersion(enrValue: Buffer, expectedVersions: ComponentsVersions) {
36
+ if (enrValue.length === XX_HASH_LEN) {
37
+ const expected = versionsToEnrValue(expectedVersions, true);
38
+ if (!Buffer.from(enrValue).equals(expected)) {
39
+ throw new Error(`Expected ENR version ${expected.toString('hex')} but received ${enrValue.toString('hex')}`);
40
+ }
41
+ } else {
42
+ const actual = Buffer.from(enrValue).toString();
43
+ checkCompressedComponentVersion(actual, expectedVersions);
44
+ }
45
+ }
46
+
47
+ function versionsToEnrValue(versions: ComponentsVersions, useXxHash: boolean) {
48
+ const compressed = compressComponentVersions(versions);
49
+ return useXxHash ? toBufferBE(xxhash.h64(compressed), XX_HASH_LEN) : Buffer.from(compressed);
50
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mocks/index.ts"],"names":[],"mappings":"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"}
@@ -1,181 +0,0 @@
1
- import { timesParallel } from '@aztec/foundation/collection';
2
- import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
3
- import { getTelemetryClient } from '@aztec/telemetry-client';
4
- import { gossipsub } from '@chainsafe/libp2p-gossipsub';
5
- import { noise } from '@chainsafe/libp2p-noise';
6
- import { yamux } from '@chainsafe/libp2p-yamux';
7
- import { bootstrap } from '@libp2p/bootstrap';
8
- import { identify } from '@libp2p/identify';
9
- import { createSecp256k1PeerId } from '@libp2p/peer-id-factory';
10
- import { tcp } from '@libp2p/tcp';
11
- import getPort from 'get-port';
12
- import { createLibp2p } from 'libp2p';
13
- import { BootstrapNode } from '../bootstrap/bootstrap.js';
14
- import { DiscV5Service } from '../services/discv5/discV5_service.js';
15
- import { LibP2PService } from '../services/libp2p/libp2p_service.js';
16
- import { ReqRespSubProtocol, noopValidator, } from '../services/reqresp/interface.js';
17
- import { pingHandler, statusHandler } from '../services/reqresp/protocols/index.js';
18
- import { ReqResp } from '../services/reqresp/reqresp.js';
19
- /**
20
- * Creates a libp2p node, pre configured.
21
- * @param boostrapAddrs - an optional list of bootstrap addresses
22
- * @returns Lip2p node
23
- */
24
- export async function createLibp2pNode(boostrapAddrs = [], peerId, port, enableGossipSub = false, start = true) {
25
- port = port ?? (await getPort());
26
- const options = {
27
- start,
28
- addresses: {
29
- listen: [`/ip4/0.0.0.0/tcp/${port}`],
30
- announce: [`/ip4/0.0.0.0/tcp/${port}`],
31
- },
32
- connectionEncryption: [noise()],
33
- streamMuxers: [yamux()],
34
- transports: [tcp()],
35
- services: {
36
- identify: identify({
37
- protocolPrefix: 'aztec',
38
- }),
39
- },
40
- };
41
- if (boostrapAddrs.length > 0) {
42
- options.peerDiscovery = [
43
- bootstrap({
44
- list: boostrapAddrs,
45
- }),
46
- ];
47
- }
48
- if (peerId) {
49
- options.peerId = peerId;
50
- }
51
- if (enableGossipSub) {
52
- options.services.pubsub = gossipsub({
53
- allowPublishToZeroTopicPeers: true,
54
- });
55
- }
56
- return await createLibp2p(options);
57
- }
58
- /**
59
- * Test Libp2p service
60
- * P2P functionality is operational, however everything else is default
61
- *
62
- *
63
- */
64
- export async function createTestLibP2PService(clientType, boostrapAddrs = [], l2BlockSource, worldStateSynchronizer, epochCache, mempools, telemetry, port = 0, peerId) {
65
- peerId = peerId ?? (await createSecp256k1PeerId());
66
- const config = {
67
- tcpAnnounceAddress: `127.0.0.1:${port}`,
68
- udpAnnounceAddress: `127.0.0.1:${port}`,
69
- tcpListenAddress: `0.0.0.0:${port}`,
70
- udpListenAddress: `0.0.0.0:${port}`,
71
- bootstrapNodes: boostrapAddrs,
72
- peerCheckIntervalMS: 1000,
73
- minPeerCount: 1,
74
- maxPeerCount: 5,
75
- p2pEnabled: true,
76
- peerIdPrivateKey: Buffer.from(peerId.privateKey).toString('hex'),
77
- };
78
- const discoveryService = new DiscV5Service(peerId, config, telemetry);
79
- const proofVerifier = new AlwaysTrueCircuitVerifier();
80
- // No bootstrap nodes provided as the libp2p service will register them in the constructor
81
- const p2pNode = await createLibp2pNode([], peerId, port, /*enable gossip */ true, /**start */ false);
82
- return new LibP2PService(clientType, config, p2pNode, discoveryService, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, telemetry);
83
- }
84
- // Mock sub protocol handlers
85
- export const MOCK_SUB_PROTOCOL_HANDLERS = {
86
- [ReqRespSubProtocol.PING]: pingHandler,
87
- [ReqRespSubProtocol.STATUS]: statusHandler,
88
- [ReqRespSubProtocol.TX]: (_msg) => Promise.resolve(Buffer.from('tx')),
89
- [ReqRespSubProtocol.GOODBYE]: (_msg) => Promise.resolve(Buffer.from('goodbye')),
90
- [ReqRespSubProtocol.BLOCK]: (_msg) => Promise.resolve(Buffer.from('block')),
91
- };
92
- // By default, all requests are valid
93
- // If you want to test an invalid response, you can override the validator
94
- export const MOCK_SUB_PROTOCOL_VALIDATORS = {
95
- [ReqRespSubProtocol.PING]: noopValidator,
96
- [ReqRespSubProtocol.STATUS]: noopValidator,
97
- [ReqRespSubProtocol.TX]: noopValidator,
98
- [ReqRespSubProtocol.GOODBYE]: noopValidator,
99
- [ReqRespSubProtocol.BLOCK]: noopValidator,
100
- };
101
- /**
102
- * @param numberOfNodes - the number of nodes to create
103
- * @returns An array of the created nodes
104
- */
105
- export const createNodes = (peerScoring, numberOfNodes) => {
106
- return timesParallel(numberOfNodes, () => createReqResp(peerScoring));
107
- };
108
- export const startNodes = async (nodes, subProtocolHandlers = MOCK_SUB_PROTOCOL_HANDLERS, subProtocolValidators = MOCK_SUB_PROTOCOL_VALIDATORS) => {
109
- for (const node of nodes) {
110
- await node.req.start(subProtocolHandlers, subProtocolValidators);
111
- }
112
- };
113
- export const stopNodes = async (nodes) => {
114
- const stopPromises = nodes.flatMap(node => [node.req.stop(), node.p2p.stop()]);
115
- await Promise.all(stopPromises);
116
- };
117
- // Create a req resp node, exposing the underlying p2p node
118
- export const createReqResp = async (peerScoring) => {
119
- const p2p = await createLibp2pNode();
120
- const config = {
121
- overallRequestTimeoutMs: 4000,
122
- individualRequestTimeoutMs: 2000,
123
- };
124
- const req = new ReqResp(config, p2p, peerScoring);
125
- return {
126
- p2p,
127
- req,
128
- };
129
- };
130
- // Given a node list; hand shake all of the nodes with each other
131
- export const connectToPeers = async (nodes) => {
132
- for (const node of nodes) {
133
- for (const otherNode of nodes) {
134
- if (node === otherNode) {
135
- continue;
136
- }
137
- const addr = otherNode.p2p.getMultiaddrs()[0];
138
- await node.p2p.dial(addr);
139
- }
140
- }
141
- };
142
- // Mock circuit verifier for testing - reimplementation from bb to avoid dependency
143
- export class AlwaysTrueCircuitVerifier {
144
- verifyProof(_tx) {
145
- return Promise.resolve(true);
146
- }
147
- }
148
- export class AlwaysFalseCircuitVerifier {
149
- verifyProof(_tx) {
150
- return Promise.resolve(false);
151
- }
152
- }
153
- // Bootnodes
154
- export function createBootstrapNodeConfig(privateKey, port) {
155
- return {
156
- udpListenAddress: `0.0.0.0:${port}`,
157
- udpAnnounceAddress: `127.0.0.1:${port}`,
158
- peerIdPrivateKey: privateKey,
159
- minPeerCount: 10,
160
- maxPeerCount: 100,
161
- dataDirectory: undefined,
162
- dataStoreMapSizeKB: 0,
163
- };
164
- }
165
- export function createBootstrapNodeFromPrivateKey(privateKey, port, telemetry = getTelemetryClient()) {
166
- const config = createBootstrapNodeConfig(privateKey, port);
167
- return startBootstrapNode(config, telemetry);
168
- }
169
- export async function createBootstrapNode(port, telemetry = getTelemetryClient()) {
170
- const peerId = await createSecp256k1PeerId();
171
- const config = createBootstrapNodeConfig(Buffer.from(peerId.privateKey).toString('hex'), port);
172
- return startBootstrapNode(config, telemetry);
173
- }
174
- async function startBootstrapNode(config, telemetry) {
175
- // Open an ephemeral store that will only exist in memory
176
- const store = await openTmpStore('bootstrap-node', true);
177
- const bootstrapNode = new BootstrapNode(store, telemetry);
178
- await bootstrapNode.start(config);
179
- return bootstrapNode;
180
- }
181
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9ja3MvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBUUEsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRTdELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEVBQXdCLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFbkYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3hELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sT0FBTyxNQUFNLFVBQVUsQ0FBQztBQUMvQixPQUFPLEVBQW1DLFlBQVksRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUV2RSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFHMUQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUdyRSxPQUFPLEVBQ0wsa0JBQWtCLEVBR2xCLGFBQWEsR0FDZCxNQUFNLGtDQUFrQyxDQUFDO0FBQzFDLE9BQU8sRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDcEYsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBR3pE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