trac-msb 0.2.13 → 0.2.15
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/.github/workflows/acceptance-tests.yml +38 -0
- package/.github/workflows/lint-pr-title.yml +26 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/unit-tests.yml +34 -0
- package/package.json +7 -12
- package/proto/network.proto +74 -0
- package/rpc/rpc_services.js +4 -22
- package/scripts/generate-protobufs.js +12 -37
- package/src/config/config.js +9 -26
- package/src/config/env.js +17 -27
- package/src/core/network/Network.js +36 -73
- package/src/core/network/protocols/LegacyProtocol.js +11 -21
- package/src/core/network/protocols/NetworkMessages.js +17 -38
- package/src/core/network/protocols/ProtocolInterface.js +2 -14
- package/src/core/network/protocols/ProtocolSession.js +17 -144
- package/src/core/network/protocols/V1Protocol.js +18 -37
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +19 -25
- package/src/core/network/protocols/legacy/handlers/{LegacyGetRequestHandler.js → GetRequestHandler.js} +6 -6
- package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
- package/src/core/network/protocols/{legacy/handlers/LegacyRoleOperationHandler.js → shared/handlers/RoleOperationHandler.js} +11 -18
- package/src/core/network/protocols/{legacy/handlers/LegacySubnetworkOperationHandler.js → shared/handlers/SubnetworkOperationHandler.js} +17 -28
- package/src/core/network/protocols/{legacy/handlers/LegacyTransferOperationHandler.js → shared/handlers/TransferOperationHandler.js} +11 -17
- package/src/core/network/protocols/{legacy/handlers/BaseStateOperationHandler.js → shared/handlers/base/BaseOperationHandler.js} +12 -23
- package/src/core/network/protocols/shared/validators/{PartialBootstrapDeploymentValidator.js → PartialBootstrapDeployment.js} +4 -9
- package/src/core/network/protocols/shared/validators/{PartialRoleAccessValidator.js → PartialRoleAccess.js} +17 -51
- package/src/core/network/protocols/shared/validators/{PartialTransactionValidator.js → PartialTransaction.js} +7 -21
- package/src/core/network/protocols/shared/validators/{PartialTransferValidator.js → PartialTransfer.js} +9 -26
- package/src/core/network/protocols/shared/validators/{PartialOperationValidator.js → base/PartialOperation.js} +25 -47
- package/src/core/network/protocols/v1/NetworkMessageRouter.js +7 -91
- package/src/core/network/services/ConnectionManager.js +94 -146
- package/src/core/network/services/MessageOrchestrator.js +27 -151
- package/src/core/network/services/TransactionPoolService.js +18 -129
- package/src/core/network/services/TransactionRateLimiterService.js +34 -52
- package/src/core/network/services/ValidatorObserverService.js +26 -18
- package/src/core/state/State.js +19 -70
- package/src/index.js +8 -6
- package/src/messages/network/v1/NetworkMessageBuilder.js +79 -59
- package/src/messages/network/v1/NetworkMessageDirector.js +50 -16
- package/src/utils/Scheduler.js +8 -0
- package/src/utils/constants.js +5 -71
- package/src/utils/helpers.js +1 -10
- package/src/utils/normalizers.js +0 -38
- package/src/utils/protobuf/network.cjs +840 -0
- package/src/utils/protobuf/operationHelpers.js +3 -24
- package/tests/acceptance/v1/account/account.test.mjs +2 -8
- package/tests/acceptance/v1/tx/tx.test.mjs +1 -23
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +6 -34
- package/tests/fixtures/assembleMessage.fixtures.js +8 -7
- package/tests/fixtures/networkV1.fixtures.js +28 -2
- package/tests/helpers/autobaseTestHelpers.js +5 -2
- package/tests/helpers/createTestSignature.js +3 -2
- package/tests/helpers/transactionPayloads.mjs +2 -2
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +79 -239
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +77 -223
- package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +5 -1
- package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +5 -1
- package/tests/unit/network/ConnectionManager.test.js +191 -0
- package/tests/unit/network/networkModule.test.js +1 -4
- package/tests/unit/unit.test.js +2 -2
- package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +2 -2
- package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +2 -2
- package/tests/unit/utils/protobuf/operationHelpers.test.js +4 -2
- package/tests/unit/utils/utils.test.js +0 -1
- package/proto/network/v1/enums/message_type.proto +0 -16
- package/proto/network/v1/enums/result_code.proto +0 -84
- package/proto/network/v1/messages/broadcast_transaction_request.proto +0 -9
- package/proto/network/v1/messages/broadcast_transaction_response.proto +0 -13
- package/proto/network/v1/messages/liveness_request.proto +0 -8
- package/proto/network/v1/messages/liveness_response.proto +0 -11
- package/proto/network/v1/network_message.proto +0 -22
- package/src/core/network/protocols/connectionPolicies.js +0 -88
- package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +0 -23
- package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +0 -27
- package/src/core/network/protocols/v1/V1ProtocolError.js +0 -91
- package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +0 -65
- package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +0 -389
- package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +0 -87
- package/src/core/network/protocols/v1/validators/V1BaseOperation.js +0 -211
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +0 -26
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +0 -276
- package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +0 -15
- package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +0 -17
- package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +0 -210
- package/src/core/network/services/PendingRequestService.js +0 -172
- package/src/core/network/services/TransactionCommitService.js +0 -149
- package/src/core/network/services/ValidatorHealthCheckService.js +0 -127
- package/src/utils/deepEqualApplyPayload.js +0 -40
- package/src/utils/logger.js +0 -25
- package/src/utils/protobuf/networkV1.generated.cjs +0 -2460
- package/tests/unit/network/LegacyNetworkMessageRouter.test.js +0 -54
- package/tests/unit/network/ProtocolSession.test.js +0 -127
- package/tests/unit/network/services/ConnectionManager.test.js +0 -450
- package/tests/unit/network/services/MessageOrchestrator.test.js +0 -445
- package/tests/unit/network/services/PendingRequestService.test.js +0 -431
- package/tests/unit/network/services/TransactionCommitService.test.js +0 -246
- package/tests/unit/network/services/TransactionPoolService.test.js +0 -489
- package/tests/unit/network/services/TransactionRateLimiterService.test.js +0 -139
- package/tests/unit/network/services/ValidatorHealthCheckService.test.js +0 -115
- package/tests/unit/network/services/services.test.js +0 -17
- package/tests/unit/network/utils/v1TestUtils.js +0 -153
- package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +0 -151
- package/tests/unit/network/v1/V1BaseOperation.test.js +0 -356
- package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +0 -129
- package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +0 -53
- package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +0 -512
- package/tests/unit/network/v1/V1LivenessRequest.test.js +0 -32
- package/tests/unit/network/v1/V1LivenessResponse.test.js +0 -45
- package/tests/unit/network/v1/V1ResultCode.test.js +0 -84
- package/tests/unit/network/v1/V1ValidationSchema.test.js +0 -13
- package/tests/unit/network/v1/connectionPolicies.test.js +0 -49
- package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +0 -284
- package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +0 -794
- package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +0 -193
- package/tests/unit/network/v1/v1.handlers.test.js +0 -15
- package/tests/unit/network/v1/v1.test.js +0 -19
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +0 -119
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +0 -136
- package/tests/unit/network/v1/v1ValidationSchema/common.test.js +0 -308
- package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +0 -90
- package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +0 -133
- package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +0 -102
|
@@ -8,16 +8,22 @@ import NetworkMessages from './protocols/NetworkMessages.js';
|
|
|
8
8
|
import { sleep } from '../../utils/helpers.js';
|
|
9
9
|
import {
|
|
10
10
|
TRAC_NAMESPACE,
|
|
11
|
-
|
|
11
|
+
NETWORK_MESSAGE_TYPES
|
|
12
12
|
} from '../../utils/constants.js';
|
|
13
13
|
import ConnectionManager from './services/ConnectionManager.js';
|
|
14
14
|
import MessageOrchestrator from './services/MessageOrchestrator.js';
|
|
15
15
|
import NetworkWalletFactory from './identity/NetworkWalletFactory.js';
|
|
16
|
+
import { EventType } from '../../utils/constants.js';
|
|
17
|
+
import { networkMessageFactory } from '../../messages/network/v1/networkMessageFactory.js';
|
|
16
18
|
import TransactionRateLimiterService from './services/TransactionRateLimiterService.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// -- Debug Mode --
|
|
20
|
+
// TODO: Implement a better debug system in the future. This is just temporary.
|
|
21
|
+
const DEBUG = false;
|
|
22
|
+
const debugLog = (...args) => {
|
|
23
|
+
if (DEBUG) {
|
|
24
|
+
console.log('DEBUG [Network] ==> ', ...args);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
21
27
|
|
|
22
28
|
const wakeup = new w();
|
|
23
29
|
|
|
@@ -34,11 +40,6 @@ class Network extends ReadyResource {
|
|
|
34
40
|
#connectTimeoutMs;
|
|
35
41
|
#maxPendingConnections;
|
|
36
42
|
#rateLimiter;
|
|
37
|
-
#pendingRequestsService;
|
|
38
|
-
#transactionCommitService;
|
|
39
|
-
#wallet;
|
|
40
|
-
#validatorHealthCheckService;
|
|
41
|
-
#logger;
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* @param {State} state
|
|
@@ -51,13 +52,11 @@ class Network extends ReadyResource {
|
|
|
51
52
|
this.#connectTimeoutMs = config.connectTimeoutMs || 5000;
|
|
52
53
|
this.#maxPendingConnections = config.maxPendingConnections || 50;
|
|
53
54
|
this.#pendingConnections = new Map();
|
|
54
|
-
this.#
|
|
55
|
-
this.#transactionPoolService = new TransactionPoolService(state, address, this.#transactionCommitService ,this.#config);
|
|
55
|
+
this.#transactionPoolService = new TransactionPoolService(state, address, this.#config);
|
|
56
56
|
this.#validatorObserverService = new ValidatorObserverService(this, state, address, this.#config);
|
|
57
57
|
this.#validatorConnectionManager = new ConnectionManager(this.#config);
|
|
58
58
|
this.#validatorMessageOrchestrator = new MessageOrchestrator(this.#validatorConnectionManager, state, this.#config);
|
|
59
|
-
|
|
60
|
-
this.#logger = new Logger(this.#config);
|
|
59
|
+
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
get swarm() {
|
|
@@ -81,7 +80,7 @@ class Network extends ReadyResource {
|
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
async _open() {
|
|
84
|
-
|
|
83
|
+
console.log('Network initialization...');
|
|
85
84
|
|
|
86
85
|
this.setupNetworkListeners();
|
|
87
86
|
|
|
@@ -90,19 +89,15 @@ class Network extends ReadyResource {
|
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
async _close() {
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
// TODO: Implement better "await" logic for stopping services
|
|
93
|
+
console.log('Network: closing gracefully...');
|
|
94
|
+
this.transactionPoolService.stopPool();
|
|
95
95
|
await sleep(100);
|
|
96
|
-
|
|
96
|
+
this.#validatorObserverService.stopValidatorObserver();
|
|
97
97
|
await sleep(5_000);
|
|
98
|
-
if (this.#validatorHealthCheckService) {
|
|
99
|
-
await this.#validatorHealthCheckService.close();
|
|
100
|
-
}
|
|
101
98
|
|
|
102
99
|
this.cleanupNetworkListeners();
|
|
103
100
|
this.cleanupPendingConnections();
|
|
104
|
-
this.#pendingRequestsService.close();
|
|
105
|
-
this.#transactionCommitService.close();
|
|
106
101
|
|
|
107
102
|
if (this.#swarm !== null) {
|
|
108
103
|
this.#swarm.destroy();
|
|
@@ -110,41 +105,23 @@ class Network extends ReadyResource {
|
|
|
110
105
|
}
|
|
111
106
|
|
|
112
107
|
setupNetworkListeners() {
|
|
108
|
+
// VALIDATOR_CONNECTION_TIMEOUT
|
|
113
109
|
this.on(EventType.VALIDATOR_CONNECTION_TIMEOUT, ({ publicKey, type, timeoutMs }) => {
|
|
114
|
-
|
|
110
|
+
debugLog(`Network Event: VALIDATOR_CONNECTION_TIMEOUT | PublicKey: ${publicKey} | Type: ${type} | TimeoutMs: ${timeoutMs}`);
|
|
115
111
|
this.#pendingConnections.delete(publicKey);
|
|
116
112
|
});
|
|
117
113
|
|
|
114
|
+
// VALIDATOR_CONNECTION_READY
|
|
118
115
|
this.on(EventType.VALIDATOR_CONNECTION_READY, async ({ publicKey, type, connection }) => {
|
|
119
|
-
|
|
116
|
+
debugLog(`Network Event: VALIDATOR_CONNECTION_READY | PublicKey: ${publicKey} | Type: ${type}`);
|
|
120
117
|
const { timeoutId } = this.#pendingConnections.get(publicKey);
|
|
121
|
-
|
|
122
118
|
if (!timeoutId) return;
|
|
123
119
|
|
|
124
120
|
clearTimeout(timeoutId);
|
|
125
121
|
this.#pendingConnections.delete(publicKey);
|
|
126
122
|
|
|
127
123
|
if (type === 'validator') {
|
|
128
|
-
|
|
129
|
-
await connection.protocolSession.probe();
|
|
130
|
-
} catch (err) {
|
|
131
|
-
this.#logger.debug(`failed to probe peer with publicKey ${publicKey}: ${err?.message ?? err}`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
this.#validatorConnectionManager.addValidator(publicKey, connection);
|
|
135
|
-
|
|
136
|
-
let healthCheckSupported = false;
|
|
137
|
-
try {
|
|
138
|
-
healthCheckSupported = connection.protocolSession.isHealthCheckSupported();
|
|
139
|
-
} catch (err) {
|
|
140
|
-
this.#logger.debug(`health check support unknown for peer with publicKey ${publicKey}: ${err?.message ?? err}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (healthCheckSupported) {
|
|
144
|
-
this.#validatorHealthCheckService.start(publicKey);
|
|
145
|
-
} else {
|
|
146
|
-
this.#validatorHealthCheckService.stop(publicKey);
|
|
147
|
-
}
|
|
124
|
+
await connection.protocolSession.send(NETWORK_MESSAGE_TYPES.GET.VALIDATOR);
|
|
148
125
|
}
|
|
149
126
|
|
|
150
127
|
});
|
|
@@ -169,9 +146,7 @@ class Network extends ReadyResource {
|
|
|
169
146
|
) {
|
|
170
147
|
if (!this.#swarm) {
|
|
171
148
|
const keyPair = await this.initializeNetworkingKeyPair(store, wallet);
|
|
172
|
-
|
|
173
|
-
this.#validatorMessageOrchestrator.setWallet(this.#wallet);
|
|
174
|
-
|
|
149
|
+
const wrappedWallet = this.#getNetworkWalletWrapper(wallet, keyPair);
|
|
175
150
|
this.#swarm = new Hyperswarm({
|
|
176
151
|
keyPair,
|
|
177
152
|
bootstrap: this.#config.dhtBootstrap,
|
|
@@ -184,18 +159,14 @@ class Network extends ReadyResource {
|
|
|
184
159
|
this.#rateLimiter = new TransactionRateLimiterService(this.#swarm, this.#config);
|
|
185
160
|
this.#networkMessages = new NetworkMessages(
|
|
186
161
|
state,
|
|
187
|
-
|
|
162
|
+
wrappedWallet,
|
|
188
163
|
this.#rateLimiter,
|
|
189
164
|
this.#transactionPoolService,
|
|
190
|
-
this.#
|
|
191
|
-
this.#transactionCommitService,
|
|
165
|
+
this.#validatorConnectionManager,
|
|
192
166
|
this.#config
|
|
193
167
|
);
|
|
194
|
-
this.#validatorHealthCheckService = new ValidatorHealthCheckService(this.#config);
|
|
195
|
-
await this.#validatorHealthCheckService.ready();
|
|
196
|
-
this.#validatorConnectionManager.subscribeToHealthChecks(this.#validatorHealthCheckService);
|
|
197
168
|
|
|
198
|
-
|
|
169
|
+
console.log(`Channel: ${b4a.toString(this.#config.channel)}`);
|
|
199
170
|
|
|
200
171
|
this.#swarm.on('connection', async (connection) => {
|
|
201
172
|
// Per-peer connection initialization:
|
|
@@ -214,20 +185,12 @@ class Network extends ReadyResource {
|
|
|
214
185
|
}
|
|
215
186
|
|
|
216
187
|
connection.on('close', () => {
|
|
217
|
-
this.#pendingRequestsService.rejectPendingRequestsForPeer(
|
|
218
|
-
publicKey,
|
|
219
|
-
new Error('Connection closed before response')
|
|
220
|
-
);
|
|
221
188
|
this.#swarm.leavePeer(connection.remotePublicKey);
|
|
222
189
|
this.#validatorConnectionManager.remove(publicKey);
|
|
223
190
|
connection.protocolSession.close();
|
|
224
191
|
});
|
|
225
192
|
|
|
226
193
|
connection.on('error', (error) => {
|
|
227
|
-
this.#pendingRequestsService.rejectPendingRequestsForPeer(
|
|
228
|
-
publicKey,
|
|
229
|
-
error ?? new Error('Connection error before response')
|
|
230
|
-
);
|
|
231
194
|
if (
|
|
232
195
|
error && error.message && (
|
|
233
196
|
error.message.includes('connection reset by peer') ||
|
|
@@ -237,7 +200,7 @@ class Network extends ReadyResource {
|
|
|
237
200
|
// TODO: decide if we want to handle this error in a specific way. It generates a lot of logs.
|
|
238
201
|
return;
|
|
239
202
|
}
|
|
240
|
-
|
|
203
|
+
console.error(error.message)
|
|
241
204
|
});
|
|
242
205
|
|
|
243
206
|
});
|
|
@@ -269,7 +232,7 @@ class Network extends ReadyResource {
|
|
|
269
232
|
async tryConnect(publicKey, type = null) {
|
|
270
233
|
if (this.#swarm === null) throw new Error('Network swarm is not initialized');
|
|
271
234
|
if (this.#pendingConnections.has(publicKey) || this.#pendingConnections.size >= this.#maxPendingConnections) {
|
|
272
|
-
|
|
235
|
+
debugLog(`Network.tryConnect: Connection to peer: ${publicKey} as type: ${type} is already pending or max pending connections reached.`);
|
|
273
236
|
return;
|
|
274
237
|
}
|
|
275
238
|
|
|
@@ -287,21 +250,21 @@ class Network extends ReadyResource {
|
|
|
287
250
|
const peerInfo = this.#swarm.peers.get(publicKey);
|
|
288
251
|
if (peerInfo) {
|
|
289
252
|
const connection = this.#swarm._allConnections.get(peerInfo.publicKey);
|
|
290
|
-
|
|
291
|
-
if (connection &&
|
|
292
|
-
connection.protocolSession &&
|
|
293
|
-
!connection.protocolSession.isProbed() &&
|
|
294
|
-
!this.#pendingRequestsService.isProbePending(connection.remotePublicKey.toString('hex'))
|
|
295
|
-
) {
|
|
253
|
+
if (connection && connection.protocolSession) {
|
|
296
254
|
await this.#finalizeConnection(publicKey, type, connection);
|
|
297
255
|
}
|
|
298
256
|
}
|
|
299
257
|
}
|
|
300
258
|
|
|
259
|
+
async isConnected(publicKey) {
|
|
260
|
+
return this.#swarm.peers.has(publicKey) &&
|
|
261
|
+
this.#swarm.peers.get(publicKey).connectedTime != -1
|
|
262
|
+
}
|
|
263
|
+
|
|
301
264
|
async #finalizeConnection(publicKey, type, connection) {
|
|
302
265
|
if (!this.#pendingConnections.has(publicKey)) return;
|
|
303
266
|
this.emit(EventType.VALIDATOR_CONNECTION_READY, { publicKey, type, connection });
|
|
304
|
-
|
|
267
|
+
debugLog(`Network.finalizeConnection: Connected to peer: ${publicKey} as type: ${type}`);
|
|
305
268
|
}
|
|
306
269
|
|
|
307
270
|
#getNetworkWalletWrapper(wallet, keyPair) {
|
|
@@ -9,8 +9,8 @@ class LegacyProtocol extends ProtocolInterface {
|
|
|
9
9
|
#config;
|
|
10
10
|
#router;
|
|
11
11
|
|
|
12
|
-
constructor(router, connection,
|
|
13
|
-
super(router, connection,
|
|
12
|
+
constructor(router, connection, config) {
|
|
13
|
+
super(router, connection, config);
|
|
14
14
|
this.#config = config;
|
|
15
15
|
this.#router = router;
|
|
16
16
|
this.init(connection);
|
|
@@ -41,30 +41,20 @@ class LegacyProtocol extends ProtocolInterface {
|
|
|
41
41
|
this.#session = this.#channel.addMessage({
|
|
42
42
|
encoding: c.json,
|
|
43
43
|
onmessage: async (incomingMessage) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
try {
|
|
45
|
+
if (typeof incomingMessage === 'object' || typeof incomingMessage === 'string') {
|
|
46
|
+
await this.#router.route(incomingMessage, connection, this.#session);
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error('NetworkMessages: Received message is undefined');
|
|
49
49
|
}
|
|
50
|
-
})
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(`NetworkMessages: Failed to handle incoming message: ${error.message}`);
|
|
52
|
+
}
|
|
51
53
|
}
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
decode(message) {
|
|
57
|
-
// No-op for legacy protocol
|
|
58
|
-
return message;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async send(message) {
|
|
62
|
-
this.sendAndForget(message);
|
|
63
|
-
// TODO: Change 'null' to an appropriate response if needed in the future
|
|
64
|
-
return Promise.resolve(null); // This is to maintain consistency with the ProtocolInterface and v1 protocol.
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
sendAndForget(message) {
|
|
57
|
+
send(message) {
|
|
68
58
|
this.#session.send(message);
|
|
69
59
|
}
|
|
70
60
|
|
|
@@ -1,68 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import b4a from 'b4a';
|
|
1
3
|
import NetworkMessageRouter from './legacy/NetworkMessageRouter.js';
|
|
2
4
|
import NetworkMessageRouterV1 from './v1/NetworkMessageRouter.js';
|
|
3
5
|
import ProtocolSession from './ProtocolSession.js';
|
|
4
6
|
import LegacyProtocol from './LegacyProtocol.js';
|
|
5
7
|
import V1Protocol from './V1Protocol.js';
|
|
6
|
-
|
|
7
8
|
class NetworkMessages {
|
|
8
9
|
#legacyMessageRouter;
|
|
9
10
|
#v1MessageRouter;
|
|
10
11
|
#config;
|
|
11
|
-
#wallet;
|
|
12
|
-
#pendingRequestsService;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
txPoolService,
|
|
19
|
-
pendingRequestsService,
|
|
20
|
-
transactionCommitService,
|
|
21
|
-
config
|
|
22
|
-
) {
|
|
13
|
+
/**
|
|
14
|
+
* @param {Config} config
|
|
15
|
+
**/
|
|
16
|
+
constructor(state, wallet, rateLimiterService, txPoolService, connectionManager, config) {
|
|
23
17
|
this.#config = config;
|
|
24
|
-
this.#wallet
|
|
25
|
-
|
|
26
|
-
this.#legacyMessageRouter = new NetworkMessageRouter(
|
|
27
|
-
state,
|
|
28
|
-
wallet,
|
|
29
|
-
rateLimiterService,
|
|
30
|
-
txPoolService,
|
|
31
|
-
this.#config
|
|
32
|
-
);
|
|
18
|
+
this.#initializeMessageRouter(state, wallet, rateLimiterService, txPoolService, connectionManager);
|
|
19
|
+
}
|
|
33
20
|
|
|
34
|
-
|
|
21
|
+
#initializeMessageRouter(state, wallet, rateLimiterService, txPoolService, connectionManager) {
|
|
22
|
+
this.#legacyMessageRouter = new NetworkMessageRouter(
|
|
35
23
|
state,
|
|
36
24
|
wallet,
|
|
37
25
|
rateLimiterService,
|
|
38
26
|
txPoolService,
|
|
39
|
-
|
|
40
|
-
transactionCommitService,
|
|
27
|
+
connectionManager,
|
|
41
28
|
this.#config
|
|
42
29
|
);
|
|
30
|
+
this.#v1MessageRouter = new NetworkMessageRouterV1(this.#config);
|
|
43
31
|
}
|
|
44
32
|
|
|
45
33
|
async setupProtomuxMessages(connection) {
|
|
46
34
|
// Attach a Protomux instance to this Hyperswarm connection.
|
|
47
35
|
// Protomux multiplexes multiple logical protocol channels over a single encrypted stream.
|
|
48
36
|
|
|
49
|
-
const legacyProtocol = new LegacyProtocol(
|
|
50
|
-
|
|
51
|
-
connection,
|
|
52
|
-
null,
|
|
53
|
-
this.#config
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
const v1Protocol = new V1Protocol(
|
|
57
|
-
this.#v1MessageRouter,
|
|
58
|
-
connection,
|
|
59
|
-
this.#pendingRequestsService,
|
|
60
|
-
this.#config
|
|
61
|
-
);
|
|
37
|
+
const legacyProtocol = new LegacyProtocol(this.#legacyMessageRouter, connection, this.#config);
|
|
38
|
+
const v1Protocol = new V1Protocol(this.#v1MessageRouter, connection, this.#config);
|
|
62
39
|
|
|
63
40
|
// ProtocolSession is attached to the Hyperswarm connection so other parts of the system (e.g. tryConnect)
|
|
64
41
|
// can send messages without knowing how Protomux was initialized.
|
|
65
|
-
|
|
42
|
+
const protocolSession = new ProtocolSession(legacyProtocol, v1Protocol);
|
|
43
|
+
connection.protocolSession = protocolSession;
|
|
44
|
+
|
|
66
45
|
}
|
|
67
46
|
}
|
|
68
47
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
class ProtocolInterface {
|
|
7
7
|
|
|
8
8
|
// TODO: Refactor this so we don't need to pass a reference for the whole network instance
|
|
9
|
-
constructor(router, connection,
|
|
9
|
+
constructor(router, connection, config) {
|
|
10
10
|
if (new.target === ProtocolInterface) {
|
|
11
11
|
throw new Error('ProtocolInterface cannot be instantiated directly');
|
|
12
12
|
}
|
|
@@ -17,23 +17,11 @@ class ProtocolInterface {
|
|
|
17
17
|
throw new Error('init() method must be implemented by subclass');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
// Remove it after we finish refactoring v1 protocol
|
|
22
|
-
decode(message) {
|
|
23
|
-
// Abstract method. Need to be implemented by subclasses.
|
|
24
|
-
throw new Error('decode() method must be implemented by subclass');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async send(message) {
|
|
20
|
+
send(message) {
|
|
28
21
|
// Abstract method. Need to be implemented by subclasses.
|
|
29
22
|
throw new Error('send() method must be implemented by subclass');
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
sendAndForget(message) {
|
|
33
|
-
// Abstract method. Need to be implemented by subclasses.
|
|
34
|
-
throw new Error('sendAndForget() method must be implemented by subclass');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
25
|
close() {
|
|
38
26
|
// Abstract method. Need to be implemented by subclasses.
|
|
39
27
|
throw new Error('close() method must be implemented by subclass');
|
|
@@ -1,158 +1,40 @@
|
|
|
1
|
-
// ProtocolSession is a per-peer (per Hyperswarm connection)
|
|
1
|
+
// ProtocolSession is a per-peer (per Hyperswarm connection) wrapper that exposes the available
|
|
2
2
|
// protocol messengers (legacy JSON, v1 binary) in one place.
|
|
3
3
|
//
|
|
4
4
|
// Why it exists:
|
|
5
5
|
// - `setupProtomuxMessages(connection)` creates Protomux channels/messages for a specific peer.
|
|
6
|
-
|
|
7
|
-
import { networkMessageFactory } from '../../../messages/network/v1/networkMessageFactory.js';
|
|
8
|
-
import { generateUUID } from '../../../utils/helpers.js';
|
|
9
|
-
import { NETWORK_CAPABILITIES, ResultCode } from '../../../utils/constants.js';
|
|
10
|
-
import { Logger } from '../../../utils/logger.js';
|
|
11
|
-
|
|
12
6
|
class ProtocolSession {
|
|
13
7
|
#legacyProtocol;
|
|
14
8
|
#v1Protocol;
|
|
15
|
-
#preferredProtocol = null;
|
|
16
|
-
#activeProtocol = null;
|
|
17
|
-
#supportedProtocols = {
|
|
18
|
-
LEGACY: 'legacy',
|
|
19
|
-
V1: 'v1'
|
|
20
|
-
}
|
|
21
|
-
#wallet;
|
|
22
|
-
#config;
|
|
23
|
-
#capabilities;
|
|
24
|
-
#logger;
|
|
25
9
|
|
|
26
|
-
constructor(legacyProtocol, v1Protocol
|
|
10
|
+
constructor(legacyProtocol, v1Protocol) {
|
|
27
11
|
// These are Protomux "message" objects (returned by channel.addMessage).
|
|
28
12
|
// They are connection-scoped and expose .send(...), already wired to the channel's encoding.
|
|
29
13
|
this.#legacyProtocol = legacyProtocol;
|
|
30
14
|
this.#v1Protocol = v1Protocol;
|
|
31
|
-
|
|
32
|
-
this.#activeProtocol = this.#v1Protocol;
|
|
33
|
-
this.#wallet = wallet;
|
|
34
|
-
this.#config = config;
|
|
35
|
-
this.#capabilities = NETWORK_CAPABILITIES;
|
|
36
|
-
this.#logger = new Logger(config);
|
|
37
15
|
}
|
|
38
16
|
|
|
39
|
-
|
|
40
|
-
return this.#
|
|
17
|
+
getLegacy() {
|
|
18
|
+
return this.#legacyProtocol;
|
|
41
19
|
}
|
|
42
20
|
|
|
43
|
-
|
|
44
|
-
return this.#
|
|
21
|
+
getV1() {
|
|
22
|
+
return this.#v1Protocol;
|
|
45
23
|
}
|
|
46
24
|
|
|
47
|
-
|
|
48
|
-
return this.#
|
|
25
|
+
get(protocol) {
|
|
26
|
+
if (protocol === 'legacy') return this.#legacyProtocol;
|
|
27
|
+
if (protocol === 'v1') return this.#v1Protocol;
|
|
28
|
+
return null;
|
|
49
29
|
}
|
|
50
30
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// TODO: SOMETIMES WE ARE PROBING NODE FOR MULTIPLE AMOUNT OF TIME, THIS IS BAD. WE NEED TO INVESTIGATE THIS.
|
|
54
|
-
//this.#logger.warn(`ProtocolSession: preferred protocol is already set and cannot be changed to LEGACY. Current preferred protocol: ${this.#preferredProtocol}`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
this.#preferredProtocol = this.#supportedProtocols.LEGACY;
|
|
58
|
-
this.#activeProtocol = this.#legacyProtocol;
|
|
59
|
-
this.#logger.debug('ProtocolSession: set preferred protocol to LEGACY');
|
|
31
|
+
has(protocol) {
|
|
32
|
+
return Boolean(this.get(protocol));
|
|
60
33
|
}
|
|
61
34
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//this.#logger.warn(`ProtocolSession: preferred protocol is already set and cannot be changed to V1. Current preferred protocol: ${this.#preferredProtocol}`);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this.#preferredProtocol = this.#supportedProtocols.V1;
|
|
70
|
-
this.#activeProtocol = this.#v1Protocol;
|
|
71
|
-
this.#logger.debug('ProtocolSession: set preferred protocol to V1');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Probes the peer to determine which protocol version they support/prefer.
|
|
76
|
-
* This is needed to know if the connected peer supports the new v1 protocol
|
|
77
|
-
* or if we should fall back to legacy for this connection.
|
|
78
|
-
*
|
|
79
|
-
* TODO: After legacy protocol is retired, we can remove the concept of "probing" and just use v1 directly.
|
|
80
|
-
* For now, this is needed to determine which protocol to use for health checks.
|
|
81
|
-
* A good future improvement would be to implement a more robust negotiation mechanism that doesn't rely on timeouts
|
|
82
|
-
* (e.g. peer sends a "hello" message indicating supported protocol versions right after connection is established).
|
|
83
|
-
*/
|
|
84
|
-
async probe() {
|
|
85
|
-
if (this.isProbed()) {
|
|
86
|
-
this.#logger.warn(`ProtocolSession: preferred protocol is already set. Skipping probe. Current preferred protocol: ${this.#preferredProtocol}`);
|
|
87
|
-
return; // TODO: Consider not returning silently
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const message = await this.#buildLivenessRequest();
|
|
92
|
-
if (!this.#v1Protocol) {
|
|
93
|
-
throw new Error('ProtocolSession: v1 protocol not available for probing');
|
|
94
|
-
}
|
|
95
|
-
const result = await this.#v1Protocol.send(message);
|
|
96
|
-
if (result !== ResultCode.OK) {
|
|
97
|
-
// TODO: Think about how to handle failure result codes after legacy protocol is retired
|
|
98
|
-
this.#logger.warn(`ProtocolSession: v1 protocol probe failed with non-OK result code: ${result}`);
|
|
99
|
-
this.setLegacyAsPreferredProtocol();
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
this.setV1AsPreferredProtocol();
|
|
103
|
-
} catch (err) {
|
|
104
|
-
this.#logger.debug(`ProtocolSession: v1 protocol probe failed, falling back to legacy. Details: ${err?.message ?? err}`);
|
|
105
|
-
this.setLegacyAsPreferredProtocol();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Sends a single health check message to a peer.
|
|
111
|
-
* This is used by the ValidatorHealthCheckService.
|
|
112
|
-
* @returns {Promise<ResultCode>} Result code indicating success or failure of the health check.
|
|
113
|
-
*/
|
|
114
|
-
async sendHealthCheck() {
|
|
115
|
-
switch (this.#preferredProtocol) {
|
|
116
|
-
case this.#supportedProtocols.V1:
|
|
117
|
-
try {
|
|
118
|
-
const message = await this.#buildLivenessRequest();
|
|
119
|
-
return await this.#v1Protocol.send(message);
|
|
120
|
-
}
|
|
121
|
-
catch (err) {
|
|
122
|
-
this.#logger.error(`ProtocolSession: v1 health check failed: ${err?.message ?? err}`);
|
|
123
|
-
return ResultCode.UNEXPECTED_ERROR; // TODO: Consider just propagating the error instead
|
|
124
|
-
}
|
|
125
|
-
case this.#supportedProtocols.LEGACY:
|
|
126
|
-
this.#logger.warn('ProtocolSession: health check not supported on LEGACY protocol');
|
|
127
|
-
return ResultCode.OK; // TODO: Consider implementing a new result code (e.g. NOT_SUPPORTED) instead of returning OK
|
|
128
|
-
default:
|
|
129
|
-
this.#logger.warn('ProtocolSession: preferred protocol not set. Call probe() first.');
|
|
130
|
-
return ResultCode.UNSPECIFIED; // TODO: Define a more specific result code.
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Tells whether the connected peer supports health checks, which is a feature of the v1 protocol.
|
|
136
|
-
* @returns {Boolean} True if health checks are supported in the preferred protocol, false otherwise.
|
|
137
|
-
*/
|
|
138
|
-
isHealthCheckSupported() {
|
|
139
|
-
if (this.#preferredProtocol === null) {
|
|
140
|
-
throw new Error('ProtocolSession: preferred protocol not set. Call probe() first.');
|
|
141
|
-
}
|
|
142
|
-
return this.#preferredProtocol === this.#supportedProtocols.V1;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// TODO: Consider moving this method to be used only in V1 internally, just like 'encode'
|
|
146
|
-
decode(message) {
|
|
147
|
-
return this.#activeProtocol.decode(message);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async send(message) {
|
|
151
|
-
return this.#activeProtocol.send(message);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
sendAndForget(message) {
|
|
155
|
-
this.#activeProtocol.sendAndForget(message);
|
|
35
|
+
send(message) {
|
|
36
|
+
// TODO: Support v1 messages
|
|
37
|
+
this.#legacyProtocol.send(message);
|
|
156
38
|
}
|
|
157
39
|
|
|
158
40
|
close() {
|
|
@@ -160,7 +42,7 @@ class ProtocolSession {
|
|
|
160
42
|
try {
|
|
161
43
|
this.#legacyProtocol.close();
|
|
162
44
|
} catch (e) {
|
|
163
|
-
|
|
45
|
+
console.error('Failed to close legacy channel:', e); // TODO: Think about throwing instead
|
|
164
46
|
}
|
|
165
47
|
}
|
|
166
48
|
|
|
@@ -168,19 +50,10 @@ class ProtocolSession {
|
|
|
168
50
|
try {
|
|
169
51
|
this.#v1Protocol.close();
|
|
170
52
|
} catch (e) {
|
|
171
|
-
|
|
53
|
+
console.error('Failed to close v1 channel:', e); // TODO: Think about throwing instead
|
|
172
54
|
}
|
|
173
55
|
}
|
|
174
56
|
}
|
|
175
|
-
|
|
176
|
-
async #buildLivenessRequest() {
|
|
177
|
-
if (!this.#wallet || !this.#config) {
|
|
178
|
-
throw new Error('ProtocolSession: wallet/config not set for liveness request');
|
|
179
|
-
}
|
|
180
|
-
const requestId = generateUUID();
|
|
181
|
-
return await networkMessageFactory(this.#wallet, this.#config)
|
|
182
|
-
.buildLivenessRequest(requestId, this.#capabilities);
|
|
183
|
-
}
|
|
184
57
|
}
|
|
185
58
|
|
|
186
59
|
export default ProtocolSession;
|