trac-msb 0.2.19 → 0.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trac-msb",
3
3
  "main": "msb.mjs",
4
- "version": "0.2.19",
4
+ "version": "0.2.20",
5
5
  "pear": {
6
6
  "name": "trac-msb",
7
7
  "type": "terminal"
@@ -39,6 +39,7 @@ class Network extends ReadyResource {
39
39
  #wallet;
40
40
  #validatorHealthCheckService;
41
41
  #logger;
42
+ #closing = false;
42
43
 
43
44
  /**
44
45
  * @param {State} state
@@ -82,6 +83,7 @@ class Network extends ReadyResource {
82
83
 
83
84
  async _open() {
84
85
  this.#logger.info('Network initialization...');
86
+ this.#closing = false;
85
87
 
86
88
  this.setupNetworkListeners();
87
89
 
@@ -91,6 +93,7 @@ class Network extends ReadyResource {
91
93
 
92
94
  async _close() {
93
95
  this.#logger.info('Network: closing gracefully...');
96
+ this.#closing = true;
94
97
  await this.transactionPoolService.stopPool();
95
98
  await sleep(100);
96
99
  await this.#validatorObserverService.stopValidatorObserver();
@@ -104,8 +107,15 @@ class Network extends ReadyResource {
104
107
  this.#pendingRequestsService.close();
105
108
  this.#transactionCommitService.close();
106
109
 
107
- if (this.#swarm !== null) {
108
- this.#swarm.destroy();
110
+ const swarm = this.#swarm;
111
+ if (swarm !== null) {
112
+ if (typeof swarm.removeAllListeners === 'function') {
113
+ swarm.removeAllListeners('connection');
114
+ }
115
+ await swarm.destroy();
116
+ if (this.#swarm === swarm) {
117
+ this.#swarm = null;
118
+ }
109
119
  }
110
120
  }
111
121
 
@@ -167,8 +177,12 @@ class Network extends ReadyResource {
167
177
  store,
168
178
  wallet,
169
179
  ) {
180
+ this.#assertCanReplicate();
181
+
170
182
  if (!this.#swarm) {
171
183
  const { wallet: wrappedWallet, keyPair } = await this.#getOrGenerateWallet(store, wallet);
184
+ this.#assertCanReplicate();
185
+
172
186
  this.#wallet = wrappedWallet
173
187
  this.#validatorMessageOrchestrator.setWallet(this.#wallet);
174
188
 
@@ -193,19 +207,49 @@ class Network extends ReadyResource {
193
207
  );
194
208
  this.#validatorHealthCheckService = new ValidatorHealthCheckService(this.#config);
195
209
  await this.#validatorHealthCheckService.ready();
210
+ this.#assertCanReplicate();
211
+
196
212
  this.#validatorConnectionManager.subscribeToHealthChecks(this.#validatorHealthCheckService);
197
213
 
198
214
  this.#logger.info(`Channel: ${b4a.toString(this.#config.channel)}`);
199
215
 
200
216
  this.#swarm.on('connection', async (connection) => {
201
- // Per-peer connection initialization:
202
- // - attach Protomux (legacy + v1 channels/messages)
203
- // - attach connection.protocolSession (used later by tryConnect / orchestrators to send messages)
204
- await this.#networkMessages.setupProtomuxMessages(connection);
217
+ if (this.#closing) {
218
+ this.#destroyConnection(connection);
219
+ return;
220
+ }
205
221
 
206
- // ATTENTION: Must be called AFTER the protomux init above
207
- const stream = store.replicate(connection);
208
- wakeup.addStream(stream);
222
+ try {
223
+ // Per-peer connection initialization:
224
+ // - attach Protomux (legacy + v1 channels/messages)
225
+ // - attach connection.protocolSession (used later by tryConnect / orchestrators to send messages)
226
+ await this.#networkMessages.setupProtomuxMessages(connection);
227
+
228
+ // Pear v3 can deliver a late swarm connection while shutdown is
229
+ // already closing the Corestore. Do not replicate a connection
230
+ // after close has begun; store.replicate would otherwise throw.
231
+ if (this.#closing || this.#swarm === null) {
232
+ this.#destroyConnection(connection);
233
+ return;
234
+ }
235
+
236
+ // ATTENTION: Must be called AFTER the protomux init above
237
+ const stream = store.replicate(connection);
238
+ wakeup.addStream(stream);
239
+ } catch (error) {
240
+ const publicKey = connection.remotePublicKey
241
+ ? b4a.toString(connection.remotePublicKey, 'hex')
242
+ : 'unknown';
243
+ this.#pendingRequestsService.rejectPendingRequestsForPeer(
244
+ publicKey,
245
+ error ?? new Error('Connection setup failed')
246
+ );
247
+ this.#destroyConnection(connection);
248
+ if (!this.#closing) {
249
+ this.#logger.error(error?.message ?? 'Unknown network connection setup error');
250
+ }
251
+ return;
252
+ }
209
253
 
210
254
  const publicKey = b4a.toString(connection.remotePublicKey, 'hex');
211
255
  if (this.#pendingConnections.has(publicKey)) {
@@ -218,9 +262,13 @@ class Network extends ReadyResource {
218
262
  publicKey,
219
263
  new Error('Connection closed before response')
220
264
  );
221
- this.#swarm.leavePeer(connection.remotePublicKey);
265
+ this.#swarm?.leavePeer(connection.remotePublicKey);
222
266
  this.#validatorConnectionManager.remove(publicKey);
223
- connection.protocolSession.close();
267
+ if (connection.protocolSession) {
268
+ try {
269
+ connection.protocolSession.close();
270
+ } catch {}
271
+ }
224
272
  });
225
273
 
226
274
  connection.on('error', (error) => {
@@ -344,6 +392,26 @@ class Network extends ReadyResource {
344
392
  this.#pendingConnections.delete(publicKeyHex);
345
393
  return true;
346
394
  }
395
+
396
+ #assertCanReplicate() {
397
+ if (this.#closing || this.closed) {
398
+ throw new Error('Network is closing or already closed');
399
+ }
400
+ }
401
+
402
+ #destroyConnection(connection) {
403
+ if (!connection) return;
404
+ if (connection.protocolSession) {
405
+ try {
406
+ connection.protocolSession.close();
407
+ } catch {}
408
+ }
409
+ if (typeof connection.destroy === 'function') {
410
+ connection.destroy();
411
+ } else if (typeof connection.end === 'function') {
412
+ connection.end();
413
+ }
414
+ }
347
415
  }
348
416
 
349
417
  export default Network;