trac-msb 0.2.7 → 0.2.9
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/publish.yml +8 -16
- package/msb.mjs +13 -25
- package/package.json +8 -4
- package/proto/network.proto +74 -0
- package/rpc/{create_server.mjs → create_server.js} +4 -4
- package/rpc/{handlers.mjs → handlers.js} +7 -7
- package/rpc/routes/{index.mjs → index.js} +1 -1
- package/rpc/routes/{v1.mjs → v1.js} +1 -1
- package/rpc/rpc_server.js +10 -0
- package/rpc/rpc_services.js +48 -7
- package/rpc/utils/{helpers.mjs → helpers.js} +1 -1
- package/src/config/config.js +137 -0
- package/src/config/env.js +63 -0
- package/src/core/network/Network.js +133 -119
- package/src/core/network/identity/NetworkWalletFactory.js +5 -6
- package/src/core/network/protocols/LegacyProtocol.js +67 -0
- package/src/core/network/protocols/NetworkMessages.js +48 -0
- package/src/core/network/protocols/ProtocolInterface.js +31 -0
- package/src/core/network/protocols/ProtocolSession.js +59 -0
- package/src/core/network/protocols/V1Protocol.js +64 -0
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +84 -0
- package/src/core/network/protocols/legacy/handlers/GetRequestHandler.js +53 -0
- package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
- package/src/core/network/{messaging → protocols/legacy}/validators/ValidatorResponse.js +2 -2
- package/src/core/network/{messaging → protocols/legacy}/validators/base/BaseResponse.js +13 -6
- package/src/core/network/protocols/shared/handlers/RoleOperationHandler.js +88 -0
- package/src/core/network/protocols/shared/handlers/SubnetworkOperationHandler.js +93 -0
- package/src/core/network/protocols/shared/handlers/TransferOperationHandler.js +57 -0
- package/src/core/network/{messaging → protocols/shared}/handlers/base/BaseOperationHandler.js +21 -26
- package/src/core/network/{messaging → protocols/shared}/validators/PartialBootstrapDeployment.js +3 -3
- package/src/core/network/{messaging → protocols/shared}/validators/PartialRoleAccess.js +15 -12
- package/src/core/network/{messaging → protocols/shared}/validators/PartialTransaction.js +10 -11
- package/src/core/network/{messaging → protocols/shared}/validators/PartialTransfer.js +10 -7
- package/src/core/network/{messaging → protocols/shared}/validators/base/PartialOperation.js +40 -22
- package/src/core/network/protocols/v1/NetworkMessageRouter.js +15 -0
- package/src/core/network/services/ConnectionManager.js +13 -19
- package/src/core/network/services/MessageOrchestrator.js +10 -22
- package/src/core/network/services/TransactionPoolService.js +10 -10
- package/src/core/network/services/TransactionRateLimiterService.js +5 -3
- package/src/core/network/services/ValidatorObserverService.js +46 -21
- package/src/core/state/State.js +137 -141
- package/src/core/state/utils/address.js +18 -16
- package/src/core/state/utils/adminEntry.js +17 -16
- package/src/core/state/utils/deploymentEntry.js +15 -15
- package/src/core/state/utils/transaction.js +3 -95
- package/src/index.js +250 -325
- package/src/messages/network/v1/NetworkMessageBuilder.js +325 -0
- package/src/messages/network/v1/NetworkMessageDirector.js +137 -0
- package/src/messages/network/v1/networkMessageFactory.js +12 -0
- package/src/messages/state/ApplyStateMessageBuilder.js +661 -0
- package/src/messages/state/ApplyStateMessageDirector.js +516 -0
- package/src/messages/state/applyStateMessageFactory.js +12 -0
- package/src/utils/buffer.js +53 -1
- package/src/utils/check.js +21 -17
- package/src/utils/cli.js +0 -8
- package/src/utils/cliCommands.js +11 -11
- package/src/utils/constants.js +36 -24
- package/src/utils/fileUtils.js +1 -4
- package/src/utils/helpers.js +9 -20
- package/src/utils/migrationUtils.js +2 -2
- package/src/utils/normalizers.js +94 -11
- package/src/utils/protobuf/network.cjs +840 -0
- package/src/utils/protobuf/operationHelpers.js +10 -0
- package/tests/acceptance/v1/account/account.test.mjs +2 -2
- package/tests/acceptance/v1/balance/balance.test.mjs +1 -1
- package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +11 -2
- package/tests/acceptance/v1/rpc.test.mjs +10 -10
- package/tests/acceptance/v1/tx/tx.test.mjs +4 -2
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +7 -3
- package/tests/fixtures/check.fixtures.js +42 -42
- package/tests/fixtures/networkV1.fixtures.js +84 -0
- package/tests/fixtures/protobuf.fixtures.js +110 -26
- package/tests/helpers/StateNetworkFactory.js +3 -5
- package/tests/helpers/autobaseTestHelpers.js +1 -2
- package/tests/helpers/config.js +3 -0
- package/tests/helpers/setupApplyTests.js +113 -99
- package/tests/helpers/transactionPayloads.mjs +26 -12
- package/tests/unit/messages/messages.test.js +12 -0
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +276 -0
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +203 -0
- package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +521 -0
- package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +233 -0
- package/tests/unit/network/ConnectionManager.test.js +10 -7
- package/tests/unit/network/NetworkWalletFactory.test.js +14 -14
- package/tests/unit/network/networkModule.test.js +3 -2
- package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +10 -6
- package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +11 -8
- package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +11 -7
- package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +18 -20
- package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +57 -48
- package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +2 -1
- package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +72 -57
- package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +3 -7
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +12 -14
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +18 -13
- package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +2 -1
- package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +2 -1
- package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +27 -30
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +2 -1
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +24 -21
- package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +5 -4
- package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +4 -3
- package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +2 -1
- package/tests/unit/state/apply/common/commonScenarioHelper.js +16 -16
- package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +10 -5
- package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +2 -2
- package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +2 -1
- package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +2 -1
- package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +2 -1
- package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +2 -1
- package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +16 -9
- package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +6 -5
- package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +23 -19
- package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +45 -36
- package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +48 -45
- package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +32 -29
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +2 -1
- package/tests/unit/state/stateModule.test.js +0 -1
- package/tests/unit/state/stateTestUtils.js +7 -3
- package/tests/unit/state/utils/address.test.js +3 -3
- package/tests/unit/state/utils/adminEntry.test.js +10 -9
- package/tests/unit/unit.test.js +1 -1
- package/tests/unit/utils/buffer/buffer.test.js +62 -1
- package/tests/unit/utils/check/adminControlOperation.test.js +3 -3
- package/tests/unit/utils/check/balanceInitializationOperation.test.js +2 -2
- package/tests/unit/utils/check/bootstrapDeploymentOperation.test.js +2 -3
- package/tests/unit/utils/check/common.test.js +7 -6
- package/tests/unit/utils/check/coreAdminOperation.test.js +3 -3
- package/tests/unit/utils/check/roleAccessOperation.test.js +3 -2
- package/tests/unit/utils/check/transactionOperation.test.js +3 -3
- package/tests/unit/utils/check/transferOperation.test.js +3 -3
- package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +2 -1
- package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +2 -1
- package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +7 -0
- package/tests/unit/utils/normalizers/normalizers.test.js +469 -0
- package/tests/unit/utils/protobuf/operationHelpers.test.js +120 -2
- package/tests/unit/utils/utils.test.js +0 -1
- package/rpc/rpc_server.mjs +0 -10
- package/src/core/network/messaging/NetworkMessages.js +0 -63
- package/src/core/network/messaging/handlers/GetRequestHandler.js +0 -112
- package/src/core/network/messaging/handlers/ResponseHandler.js +0 -108
- package/src/core/network/messaging/handlers/RoleOperationHandler.js +0 -116
- package/src/core/network/messaging/handlers/SubnetworkOperationHandler.js +0 -143
- package/src/core/network/messaging/handlers/TransferOperationHandler.js +0 -52
- package/src/core/network/messaging/routes/NetworkMessageRouter.js +0 -94
- package/src/core/network/messaging/validators/AdminResponse.js +0 -58
- package/src/core/network/messaging/validators/CustomNodeResponse.js +0 -46
- package/src/core/state/utils/indexerEntry.js +0 -105
- package/src/messages/base/StateBuilder.js +0 -25
- package/src/messages/completeStateMessages/CompleteStateMessageBuilder.js +0 -421
- package/src/messages/completeStateMessages/CompleteStateMessageDirector.js +0 -252
- package/src/messages/completeStateMessages/CompleteStateMessageOperations.js +0 -299
- package/src/messages/partialStateMessages/PartialStateMessageBuilder.js +0 -272
- package/src/messages/partialStateMessages/PartialStateMessageDirector.js +0 -137
- package/src/messages/partialStateMessages/PartialStateMessageOperations.js +0 -131
- package/src/utils/crypto.js +0 -11
- package/tests/integration/apply/addAdmin/addAdminBasic.test.js +0 -68
- package/tests/integration/apply/addAdmin/addAdminRecovery.test.js +0 -125
- package/tests/integration/apply/addIndexer.test.js +0 -237
- package/tests/integration/apply/addWhitelist.test.js +0 -53
- package/tests/integration/apply/addWriter.test.js +0 -244
- package/tests/integration/apply/apply.test.js +0 -19
- package/tests/integration/apply/banValidator.test.js +0 -109
- package/tests/integration/apply/postTx/invalidSubValues.test.js +0 -103
- package/tests/integration/apply/postTx/postTx.test.js +0 -222
- package/tests/integration/apply/removeIndexer.test.js +0 -128
- package/tests/integration/apply/removeWriter.test.js +0 -167
- package/tests/integration/apply/transfer.test.js +0 -81
- package/tests/integration/integration.test.js +0 -9
- package/tests/unit/messageOperations/assembleAddIndexerMessage.test.js +0 -21
- package/tests/unit/messageOperations/assembleAddWriterMessage.test.js +0 -16
- package/tests/unit/messageOperations/assembleAdminMessage.test.js +0 -69
- package/tests/unit/messageOperations/assembleBanWriterMessage.test.js +0 -16
- package/tests/unit/messageOperations/assemblePostTransaction.test.js +0 -442
- package/tests/unit/messageOperations/assembleRemoveIndexerMessage.test.js +0 -19
- package/tests/unit/messageOperations/assembleRemoveWriterMessage.test.js +0 -17
- package/tests/unit/messageOperations/assembleWhitelistMessages.test.js +0 -58
- package/tests/unit/messageOperations/commonsStateMessageOperationsTest.js +0 -277
- package/tests/unit/messageOperations/stateMessageOperations.test.js +0 -19
- package/tests/unit/state/utils/indexerEntry.test.js +0 -83
- package/tests/unit/state/utils/transaction.test.js +0 -97
- package/tests/unit/utils/crypto/createHash.test.js +0 -15
- /package/rpc/{constants.mjs → constants.js} +0 -0
- /package/rpc/{cors.mjs → cors.js} +0 -0
- /package/rpc/utils/{confirmedParameter.mjs → confirmedParameter.js} +0 -0
- /package/rpc/utils/{url.mjs → url.js} +0 -0
- /package/src/utils/{operations.js → applyOperations.js} +0 -0
|
@@ -2,10 +2,9 @@ import ReadyResource from 'ready-resource';
|
|
|
2
2
|
import Hyperswarm from 'hyperswarm';
|
|
3
3
|
import w from 'protomux-wakeup';
|
|
4
4
|
import b4a from 'b4a';
|
|
5
|
-
|
|
6
5
|
import TransactionPoolService from './services/TransactionPoolService.js';
|
|
7
6
|
import ValidatorObserverService from './services/ValidatorObserverService.js';
|
|
8
|
-
import NetworkMessages from './
|
|
7
|
+
import NetworkMessages from './protocols/NetworkMessages.js';
|
|
9
8
|
import { sleep } from '../../utils/helpers.js';
|
|
10
9
|
import {
|
|
11
10
|
TRAC_NAMESPACE,
|
|
@@ -13,52 +12,61 @@ import {
|
|
|
13
12
|
MAX_PARALLEL,
|
|
14
13
|
MAX_SERVER_CONNECTIONS,
|
|
15
14
|
MAX_CLIENT_CONNECTIONS,
|
|
16
|
-
NETWORK_MESSAGE_TYPES
|
|
17
|
-
DHT_BOOTSTRAPS
|
|
15
|
+
NETWORK_MESSAGE_TYPES
|
|
18
16
|
} from '../../utils/constants.js';
|
|
19
17
|
import ConnectionManager from './services/ConnectionManager.js';
|
|
20
18
|
import MessageOrchestrator from './services/MessageOrchestrator.js';
|
|
21
19
|
import NetworkWalletFactory from './identity/NetworkWalletFactory.js';
|
|
20
|
+
import { EventType } from '../../utils/constants.js';
|
|
21
|
+
import { networkMessageFactory } from '../../messages/network/v1/networkMessageFactory.js';
|
|
22
|
+
import TransactionRateLimiterService from './services/TransactionRateLimiterService.js';
|
|
23
|
+
// -- Debug Mode --
|
|
24
|
+
// TODO: Implement a better debug system in the future. This is just temporary.
|
|
25
|
+
const DEBUG = false;
|
|
26
|
+
const debugLog = (...args) => {
|
|
27
|
+
if (DEBUG) {
|
|
28
|
+
console.log('DEBUG [Network] ==> ', ...args);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
22
32
|
const wakeup = new w();
|
|
23
33
|
|
|
24
34
|
class Network extends ReadyResource {
|
|
25
|
-
#dht_bootstrap = DHT_BOOTSTRAPS;
|
|
26
35
|
#swarm = null;
|
|
27
|
-
#enable_wallet;
|
|
28
|
-
#channel;
|
|
29
36
|
#networkMessages;
|
|
30
37
|
#transactionPoolService;
|
|
31
38
|
#validatorObserverService;
|
|
32
39
|
#validatorConnectionManager;
|
|
33
40
|
#validatorMessageOrchestrator;
|
|
34
|
-
#
|
|
41
|
+
#config;
|
|
35
42
|
#identityProvider = null;
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
#pendingConnections;
|
|
44
|
+
#connectTimeoutMs;
|
|
45
|
+
#maxPendingConnections;
|
|
46
|
+
#rateLimiter;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {State} state
|
|
50
|
+
* @param {object} config
|
|
51
|
+
* @param {string} address
|
|
52
|
+
**/
|
|
53
|
+
constructor(state, config, address = null) {
|
|
38
54
|
super();
|
|
39
|
-
this.#
|
|
40
|
-
this.#
|
|
41
|
-
this.#
|
|
42
|
-
this.#
|
|
43
|
-
this.#
|
|
44
|
-
this.#
|
|
45
|
-
this.#validatorConnectionManager = new ConnectionManager(
|
|
46
|
-
this.#validatorMessageOrchestrator = new MessageOrchestrator(this.#validatorConnectionManager, state);
|
|
47
|
-
|
|
48
|
-
this.admin = null;
|
|
49
|
-
this.validator = null;
|
|
50
|
-
this.custom_stream = null;
|
|
51
|
-
this.custom_node = null;
|
|
55
|
+
this.#config = config
|
|
56
|
+
this.#connectTimeoutMs = config.connectTimeoutMs || 5000;
|
|
57
|
+
this.#maxPendingConnections = config.maxPendingConnections || 50;
|
|
58
|
+
this.#pendingConnections = new Map();
|
|
59
|
+
this.#transactionPoolService = new TransactionPoolService(state, address, this.#config);
|
|
60
|
+
this.#validatorObserverService = new ValidatorObserverService(this, state, address, this.#config);
|
|
61
|
+
this.#validatorConnectionManager = new ConnectionManager(this.#config);
|
|
62
|
+
this.#validatorMessageOrchestrator = new MessageOrchestrator(this.#validatorConnectionManager, state, this.#config);
|
|
63
|
+
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
get swarm() {
|
|
55
67
|
return this.#swarm;
|
|
56
68
|
}
|
|
57
69
|
|
|
58
|
-
get channel() {
|
|
59
|
-
return this.#channel;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
70
|
get transactionPoolService() {
|
|
63
71
|
return this.#transactionPoolService;
|
|
64
72
|
}
|
|
@@ -77,22 +85,64 @@ class Network extends ReadyResource {
|
|
|
77
85
|
|
|
78
86
|
async _open() {
|
|
79
87
|
console.log('Network initialization...');
|
|
88
|
+
|
|
89
|
+
this.setupNetworkListeners();
|
|
90
|
+
|
|
80
91
|
this.transactionPoolService.start();
|
|
81
92
|
this.validatorObserverService.start();
|
|
82
93
|
}
|
|
83
94
|
|
|
84
95
|
async _close() {
|
|
96
|
+
// TODO: Implement better "await" logic for stopping services
|
|
85
97
|
console.log('Network: closing gracefully...');
|
|
86
98
|
this.transactionPoolService.stopPool();
|
|
87
99
|
await sleep(100);
|
|
88
100
|
this.#validatorObserverService.stopValidatorObserver();
|
|
89
101
|
await sleep(5_000);
|
|
90
102
|
|
|
103
|
+
this.cleanupNetworkListeners();
|
|
104
|
+
this.cleanupPendingConnections();
|
|
105
|
+
|
|
91
106
|
if (this.#swarm !== null) {
|
|
92
107
|
this.#swarm.destroy();
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
|
|
111
|
+
setupNetworkListeners() {
|
|
112
|
+
// VALIDATOR_CONNECTION_TIMEOUT
|
|
113
|
+
this.on(EventType.VALIDATOR_CONNECTION_TIMEOUT, ({ publicKey, type, timeoutMs }) => {
|
|
114
|
+
debugLog(`Network Event: VALIDATOR_CONNECTION_TIMEOUT | PublicKey: ${publicKey} | Type: ${type} | TimeoutMs: ${timeoutMs}`);
|
|
115
|
+
this.#pendingConnections.delete(publicKey);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// VALIDATOR_CONNECTION_READY
|
|
119
|
+
this.on(EventType.VALIDATOR_CONNECTION_READY, async ({ publicKey, type, connection }) => {
|
|
120
|
+
debugLog(`Network Event: VALIDATOR_CONNECTION_READY | PublicKey: ${publicKey} | Type: ${type}`);
|
|
121
|
+
const { timeoutId } = this.#pendingConnections.get(publicKey);
|
|
122
|
+
if (!timeoutId) return;
|
|
123
|
+
|
|
124
|
+
clearTimeout(timeoutId);
|
|
125
|
+
this.#pendingConnections.delete(publicKey);
|
|
126
|
+
|
|
127
|
+
if (type === 'validator') {
|
|
128
|
+
await connection.protocolSession.send(NETWORK_MESSAGE_TYPES.GET.VALIDATOR);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
cleanupNetworkListeners() {
|
|
135
|
+
this.removeAllListeners(EventType.VALIDATOR_CONNECTION_TIMEOUT);
|
|
136
|
+
this.removeAllListeners(EventType.VALIDATOR_CONNECTION_READY);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
cleanupPendingConnections() {
|
|
140
|
+
for (const { timeoutId } of this.#pendingConnections.values()) {
|
|
141
|
+
clearTimeout(timeoutId);
|
|
142
|
+
}
|
|
143
|
+
this.#pendingConnections.clear();
|
|
144
|
+
}
|
|
145
|
+
|
|
96
146
|
async replicate(
|
|
97
147
|
state,
|
|
98
148
|
store,
|
|
@@ -103,36 +153,45 @@ class Network extends ReadyResource {
|
|
|
103
153
|
const wrappedWallet = this.#getNetworkWalletWrapper(wallet, keyPair);
|
|
104
154
|
this.#swarm = new Hyperswarm({
|
|
105
155
|
keyPair,
|
|
106
|
-
bootstrap: this.#
|
|
156
|
+
bootstrap: this.#config.dhtBootstrap,
|
|
107
157
|
maxPeers: MAX_PEERS,
|
|
108
158
|
maxParallel: MAX_PARALLEL,
|
|
109
159
|
maxServerConnections: MAX_SERVER_CONNECTIONS,
|
|
110
160
|
maxClientConnections: MAX_CLIENT_CONNECTIONS
|
|
111
161
|
});
|
|
112
162
|
|
|
113
|
-
|
|
114
|
-
this.#networkMessages
|
|
163
|
+
this.#rateLimiter = new TransactionRateLimiterService(this.#swarm);
|
|
164
|
+
this.#networkMessages = new NetworkMessages(
|
|
165
|
+
state,
|
|
166
|
+
wrappedWallet,
|
|
167
|
+
this.#rateLimiter,
|
|
168
|
+
this.#transactionPoolService,
|
|
169
|
+
this.#validatorConnectionManager,
|
|
170
|
+
this.#config
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
console.log(`Channel: ${b4a.toString(this.#config.channel)}`);
|
|
115
174
|
|
|
116
175
|
this.#swarm.on('connection', async (connection) => {
|
|
117
|
-
|
|
118
|
-
|
|
176
|
+
// Per-peer connection initialization:
|
|
177
|
+
// - attach Protomux (legacy + v1 channels/messages)
|
|
178
|
+
// - attach connection.protocolSession (used later by tryConnect / orchestrators to send messages)
|
|
179
|
+
await this.#networkMessages.setupProtomuxMessages(connection);
|
|
119
180
|
|
|
120
181
|
// ATTENTION: Must be called AFTER the protomux init above
|
|
121
182
|
const stream = store.replicate(connection);
|
|
122
183
|
wakeup.addStream(stream);
|
|
123
184
|
|
|
124
|
-
connection.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (this.custom_stream === connection) {
|
|
131
|
-
this.custom_stream = null;
|
|
132
|
-
this.custom_node = null;
|
|
133
|
-
}
|
|
134
|
-
try { message_channel.close() } catch (e) { }
|
|
185
|
+
const publicKey = b4a.toString(connection.remotePublicKey, 'hex');
|
|
186
|
+
if (this.#pendingConnections.has(publicKey)) {
|
|
187
|
+
const { type } = this.#pendingConnections.get(publicKey);
|
|
188
|
+
await this.#finalizeConnection(publicKey, type, connection);
|
|
189
|
+
}
|
|
135
190
|
|
|
191
|
+
connection.on('close', () => {
|
|
192
|
+
this.#swarm.leavePeer(connection.remotePublicKey);
|
|
193
|
+
this.#validatorConnectionManager.remove(publicKey);
|
|
194
|
+
connection.protocolSession.close();
|
|
136
195
|
});
|
|
137
196
|
|
|
138
197
|
connection.on('error', (error) => {
|
|
@@ -146,18 +205,25 @@ class Network extends ReadyResource {
|
|
|
146
205
|
return;
|
|
147
206
|
}
|
|
148
207
|
console.error(error.message)
|
|
149
|
-
|
|
150
208
|
});
|
|
151
209
|
|
|
152
210
|
});
|
|
153
211
|
|
|
154
|
-
this.#swarm.join(this.#channel, { server: true, client: true });
|
|
212
|
+
this.#swarm.join(this.#config.channel, { server: true, client: true });
|
|
155
213
|
this.#swarm.flush();
|
|
156
214
|
}
|
|
157
215
|
}
|
|
158
216
|
|
|
217
|
+
isConnectionPending(publicKey) {
|
|
218
|
+
return this.#pendingConnections.has(publicKey);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
pendingConnectionsCount() {
|
|
222
|
+
return this.#pendingConnections.size;
|
|
223
|
+
}
|
|
224
|
+
|
|
159
225
|
async initializeNetworkingKeyPair(store, wallet) {
|
|
160
|
-
if (!this.#
|
|
226
|
+
if (!this.#config.enableWallet) {
|
|
161
227
|
return await store.createKeyPair(TRAC_NAMESPACE);
|
|
162
228
|
} else {
|
|
163
229
|
return {
|
|
@@ -169,34 +235,29 @@ class Network extends ReadyResource {
|
|
|
169
235
|
|
|
170
236
|
async tryConnect(publicKey, type = null) {
|
|
171
237
|
if (this.#swarm === null) throw new Error('Network swarm is not initialized');
|
|
238
|
+
if (this.#pendingConnections.has(publicKey) || this.#pendingConnections.size >= this.#maxPendingConnections) {
|
|
239
|
+
debugLog(`Network.tryConnect: Connection to peer: ${publicKey} as type: ${type} is already pending or max pending connections reached.`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const timeoutId = setTimeout(() => {
|
|
244
|
+
if (!this.#pendingConnections.has(publicKey)) return;
|
|
245
|
+
this.emit(EventType.VALIDATOR_CONNECTION_TIMEOUT, { publicKey, type, timeoutMs: this.#connectTimeoutMs });
|
|
246
|
+
}, this.#connectTimeoutMs);
|
|
247
|
+
this.#pendingConnections.set(publicKey, { type, timeoutId });
|
|
172
248
|
|
|
173
249
|
const target = b4a.from(publicKey, 'hex');
|
|
174
250
|
if (!this.#swarm.peers.has(publicKey)) {
|
|
175
251
|
this.#swarm.joinPeer(target);
|
|
176
|
-
let cnt = 0;
|
|
177
|
-
while (!this.#swarm.peers.has(publicKey) && cnt < 1500) { // TODO: Get rid of the magic number and add a config option for this
|
|
178
|
-
await sleep(10);
|
|
179
|
-
cnt += 1;
|
|
180
|
-
}
|
|
181
252
|
}
|
|
182
253
|
|
|
183
254
|
const peerInfo = this.#swarm.peers.get(publicKey);
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
while ((!stream || !stream.messenger) && attempts < 1500) { // TODO: Get rid of the magic number and add a config option
|
|
190
|
-
await sleep(10);
|
|
191
|
-
attempts += 1;
|
|
192
|
-
stream = this.#swarm._allConnections.get(peerInfo.publicKey);
|
|
193
|
-
}
|
|
194
|
-
if (!stream || !stream.messenger) return;
|
|
195
|
-
|
|
196
|
-
if (type === 'validator') {
|
|
197
|
-
this.#validatorConnectionManager.addValidator(target, stream);
|
|
255
|
+
if (peerInfo) {
|
|
256
|
+
const connection = this.#swarm._allConnections.get(peerInfo.publicKey);
|
|
257
|
+
if (connection && connection.protocolSession) {
|
|
258
|
+
await this.#finalizeConnection(publicKey, type, connection);
|
|
259
|
+
}
|
|
198
260
|
}
|
|
199
|
-
await this.#sendRequestByType(stream, type);
|
|
200
261
|
}
|
|
201
262
|
|
|
202
263
|
async isConnected(publicKey) {
|
|
@@ -204,66 +265,19 @@ class Network extends ReadyResource {
|
|
|
204
265
|
this.#swarm.peers.get(publicKey).connectedTime != -1
|
|
205
266
|
}
|
|
206
267
|
|
|
207
|
-
async #
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
node: () => this.custom_stream
|
|
212
|
-
}[type];
|
|
213
|
-
|
|
214
|
-
if (type === 'validator') {
|
|
215
|
-
await stream.messenger.send(NETWORK_MESSAGE_TYPES.GET.VALIDATOR);
|
|
216
|
-
} else if (type === 'admin') {
|
|
217
|
-
await stream.messenger.send(NETWORK_MESSAGE_TYPES.GET.ADMIN);
|
|
218
|
-
} else if (type === 'node') {
|
|
219
|
-
await stream.messenger.send(NETWORK_MESSAGE_TYPES.GET.NODE);
|
|
220
|
-
} else {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
await this.spinLock(() => !waitFor())
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
async spinLock(conditionFn, maxIterations = 1500, intervalMs = 10) {
|
|
227
|
-
let counter = 0;
|
|
228
|
-
while (conditionFn() && counter < maxIterations) {
|
|
229
|
-
await sleep(intervalMs);
|
|
230
|
-
counter++;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async sendMessageToNode(nodePublicKey, message) {
|
|
235
|
-
try {
|
|
236
|
-
if (!nodePublicKey || !message) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
await this.tryConnect(nodePublicKey, 'node');
|
|
240
|
-
|
|
241
|
-
await this.spinLock(() =>
|
|
242
|
-
this.custom_stream === null ||
|
|
243
|
-
!b4a.equals(this.custom_node, b4a.from(nodePublicKey, 'hex'))
|
|
244
|
-
);
|
|
245
|
-
if (
|
|
246
|
-
this.custom_stream !== null &&
|
|
247
|
-
this.custom_node !== null &&
|
|
248
|
-
b4a.equals(this.custom_node, b4a.from(nodePublicKey, 'hex'))
|
|
249
|
-
) {
|
|
250
|
-
await this.custom_stream.messenger.send(message);
|
|
251
|
-
} else {
|
|
252
|
-
throw new Error(`Failed to send message to node: ${nodePublicKey}`);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
} catch (e) {
|
|
256
|
-
console.log(e)
|
|
257
|
-
}
|
|
268
|
+
async #finalizeConnection(publicKey, type, connection) {
|
|
269
|
+
if (!this.#pendingConnections.has(publicKey)) return;
|
|
270
|
+
this.emit(EventType.VALIDATOR_CONNECTION_READY, { publicKey, type, connection });
|
|
271
|
+
debugLog(`Network.finalizeConnection: Connected to peer: ${publicKey} as type: ${type}`);
|
|
258
272
|
}
|
|
259
273
|
|
|
260
274
|
#getNetworkWalletWrapper(wallet, keyPair) {
|
|
261
275
|
if (!this.#identityProvider) {
|
|
262
276
|
this.#identityProvider = NetworkWalletFactory.provide({
|
|
263
|
-
enableWallet: this.#
|
|
277
|
+
enableWallet: this.#config.enableWallet,
|
|
264
278
|
wallet,
|
|
265
279
|
keyPair,
|
|
266
|
-
networkPrefix: this.#
|
|
280
|
+
networkPrefix: this.#config.addressPrefix
|
|
267
281
|
});
|
|
268
282
|
}
|
|
269
283
|
return this.#identityProvider;
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import PeerWallet from 'trac-wallet';
|
|
2
|
-
import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js';
|
|
3
2
|
import b4a from 'b4a';
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
class NetworkWalletFactory {
|
|
6
5
|
static provide(options = {}) {
|
|
7
6
|
const {
|
|
8
|
-
enableWallet
|
|
7
|
+
enableWallet,
|
|
9
8
|
wallet,
|
|
10
9
|
keyPair,
|
|
11
|
-
networkPrefix
|
|
10
|
+
networkPrefix
|
|
12
11
|
} = options;
|
|
13
12
|
|
|
14
13
|
if (enableWallet) {
|
|
@@ -29,12 +28,12 @@ export class NetworkWalletFactory {
|
|
|
29
28
|
// TODO: Once Wallet class in trac-wallet exposes a constructor/factory that accepts an existing keyPair
|
|
30
29
|
// (e.g. Wallet.fromKeyPair({ publicKey, secretKey }, networkPrefix)), replace EphemeralWallet
|
|
31
30
|
// with a thin wrapper around that functionality instead of duplicating signing/verification logic.
|
|
32
|
-
class EphemeralWallet {
|
|
31
|
+
export class EphemeralWallet {
|
|
33
32
|
#publicKey;
|
|
34
33
|
#secretKey;
|
|
35
34
|
#address;
|
|
36
35
|
|
|
37
|
-
constructor(keyPair, networkPrefix
|
|
36
|
+
constructor(keyPair, networkPrefix) {
|
|
38
37
|
|
|
39
38
|
if (!keyPair?.publicKey || !keyPair?.secretKey) {
|
|
40
39
|
throw new Error('NetworkIdentityProvider: keyPair with publicKey and secretKey is required');
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Protomux from 'protomux';
|
|
2
|
+
import ProtocolInterface from './ProtocolInterface.js';
|
|
3
|
+
import b4a from 'b4a';
|
|
4
|
+
import c from 'compact-encoding';
|
|
5
|
+
|
|
6
|
+
class LegacyProtocol extends ProtocolInterface {
|
|
7
|
+
#channel;
|
|
8
|
+
#session;
|
|
9
|
+
#config;
|
|
10
|
+
#router;
|
|
11
|
+
|
|
12
|
+
constructor(router, connection, config) {
|
|
13
|
+
super(router, connection, config);
|
|
14
|
+
this.#config = config;
|
|
15
|
+
this.#router = router;
|
|
16
|
+
this.init(connection);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get channel() {
|
|
20
|
+
return this.#channel;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get session() {
|
|
24
|
+
return this.#session;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
init(connection) {
|
|
28
|
+
// TODO: Abstract in a separate function
|
|
29
|
+
const mux = Protomux.from(connection);
|
|
30
|
+
connection.userData = mux;
|
|
31
|
+
|
|
32
|
+
this.#channel = mux.createChannel({
|
|
33
|
+
protocol: b4a.toString(this.#config.channel, 'utf8'),
|
|
34
|
+
onopen() { },
|
|
35
|
+
onclose() { }
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.#channel.open();
|
|
39
|
+
|
|
40
|
+
// Todo: Abstract in a separate function
|
|
41
|
+
this.#session = this.#channel.addMessage({
|
|
42
|
+
encoding: c.json,
|
|
43
|
+
onmessage: async (incomingMessage) => {
|
|
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
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(`NetworkMessages: Failed to handle incoming message: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
send(message) {
|
|
58
|
+
this.#session.send(message);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
close() {
|
|
62
|
+
this.#channel.close();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default LegacyProtocol;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
import b4a from 'b4a';
|
|
3
|
+
import NetworkMessageRouter from './legacy/NetworkMessageRouter.js';
|
|
4
|
+
import NetworkMessageRouterV1 from './v1/NetworkMessageRouter.js';
|
|
5
|
+
import ProtocolSession from './ProtocolSession.js';
|
|
6
|
+
import LegacyProtocol from './LegacyProtocol.js';
|
|
7
|
+
import V1Protocol from './V1Protocol.js';
|
|
8
|
+
class NetworkMessages {
|
|
9
|
+
#legacyMessageRouter;
|
|
10
|
+
#v1MessageRouter;
|
|
11
|
+
#config;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} config
|
|
15
|
+
**/
|
|
16
|
+
constructor(state, wallet, rateLimiterService, txPoolService, connectionManager, config) {
|
|
17
|
+
this.#config = config;
|
|
18
|
+
this.#initializeMessageRouter(state, wallet, rateLimiterService, txPoolService, connectionManager);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#initializeMessageRouter(state, wallet, rateLimiterService, txPoolService, connectionManager) {
|
|
22
|
+
this.#legacyMessageRouter = new NetworkMessageRouter(
|
|
23
|
+
state,
|
|
24
|
+
wallet,
|
|
25
|
+
rateLimiterService,
|
|
26
|
+
txPoolService,
|
|
27
|
+
connectionManager,
|
|
28
|
+
this.#config
|
|
29
|
+
);
|
|
30
|
+
this.#v1MessageRouter = new NetworkMessageRouterV1(this.#config);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async setupProtomuxMessages(connection) {
|
|
34
|
+
// Attach a Protomux instance to this Hyperswarm connection.
|
|
35
|
+
// Protomux multiplexes multiple logical protocol channels over a single encrypted stream.
|
|
36
|
+
|
|
37
|
+
const legacyProtocol = new LegacyProtocol(this.#legacyMessageRouter, connection, this.#config);
|
|
38
|
+
const v1Protocol = new V1Protocol(this.#v1MessageRouter, connection, this.#config);
|
|
39
|
+
|
|
40
|
+
// ProtocolSession is attached to the Hyperswarm connection so other parts of the system (e.g. tryConnect)
|
|
41
|
+
// can send messages without knowing how Protomux was initialized.
|
|
42
|
+
const protocolSession = new ProtocolSession(legacyProtocol, v1Protocol);
|
|
43
|
+
connection.protocolSession = protocolSession;
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default NetworkMessages;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProtocolInterface serves as a base class for all network protocol implementations.
|
|
3
|
+
* @interface
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class ProtocolInterface {
|
|
7
|
+
|
|
8
|
+
// TODO: Refactor this so we don't need to pass a reference for the whole network instance
|
|
9
|
+
constructor(router, connection, config) {
|
|
10
|
+
if (new.target === ProtocolInterface) {
|
|
11
|
+
throw new Error('ProtocolInterface cannot be instantiated directly');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
init(connection) {
|
|
16
|
+
// Abstract method. Need to be implemented by subclasses.
|
|
17
|
+
throw new Error('init() method must be implemented by subclass');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
send(message) {
|
|
21
|
+
// Abstract method. Need to be implemented by subclasses.
|
|
22
|
+
throw new Error('send() method must be implemented by subclass');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
close() {
|
|
26
|
+
// Abstract method. Need to be implemented by subclasses.
|
|
27
|
+
throw new Error('close() method must be implemented by subclass');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default ProtocolInterface;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// ProtocolSession is a per-peer (per Hyperswarm connection) wrapper that exposes the available
|
|
2
|
+
// protocol messengers (legacy JSON, v1 binary) in one place.
|
|
3
|
+
//
|
|
4
|
+
// Why it exists:
|
|
5
|
+
// - `setupProtomuxMessages(connection)` creates Protomux channels/messages for a specific peer.
|
|
6
|
+
class ProtocolSession {
|
|
7
|
+
#legacyProtocol;
|
|
8
|
+
#v1Protocol;
|
|
9
|
+
|
|
10
|
+
constructor(legacyProtocol, v1Protocol) {
|
|
11
|
+
// These are Protomux "message" objects (returned by channel.addMessage).
|
|
12
|
+
// They are connection-scoped and expose .send(...), already wired to the channel's encoding.
|
|
13
|
+
this.#legacyProtocol = legacyProtocol;
|
|
14
|
+
this.#v1Protocol = v1Protocol;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getLegacy() {
|
|
18
|
+
return this.#legacyProtocol;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getV1() {
|
|
22
|
+
return this.#v1Protocol;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get(protocol) {
|
|
26
|
+
if (protocol === 'legacy') return this.#legacyProtocol;
|
|
27
|
+
if (protocol === 'v1') return this.#v1Protocol;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
has(protocol) {
|
|
32
|
+
return Boolean(this.get(protocol));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
send(message) {
|
|
36
|
+
// TODO: Support v1 messages
|
|
37
|
+
this.#legacyProtocol.send(message);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
close() {
|
|
41
|
+
if (this.#legacyProtocol) {
|
|
42
|
+
try {
|
|
43
|
+
this.#legacyProtocol.close();
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error('Failed to close legacy channel:', e); // TODO: Think about throwing instead
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (this.#v1Protocol) {
|
|
50
|
+
try {
|
|
51
|
+
this.#v1Protocol.close();
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.error('Failed to close v1 channel:', e); // TODO: Think about throwing instead
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default ProtocolSession;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Protomux from 'protomux';
|
|
2
|
+
import ProtocolInterface from './ProtocolInterface.js';
|
|
3
|
+
import b4a from 'b4a';
|
|
4
|
+
import c from 'compact-encoding';
|
|
5
|
+
|
|
6
|
+
class V1Protocol extends ProtocolInterface {
|
|
7
|
+
#channel;
|
|
8
|
+
#session;
|
|
9
|
+
#config;
|
|
10
|
+
#router;
|
|
11
|
+
|
|
12
|
+
constructor(router, connection, config) {
|
|
13
|
+
super(router, connection, config);
|
|
14
|
+
this.#config = config;
|
|
15
|
+
this.#router = router;
|
|
16
|
+
this.init(connection);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get channel() {
|
|
20
|
+
return this.#channel;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get session() {
|
|
24
|
+
return this.#session;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
init(connection) {
|
|
28
|
+
const mux = Protomux.from(connection);
|
|
29
|
+
connection.userData = mux;
|
|
30
|
+
|
|
31
|
+
this.#channel = mux.createChannel({
|
|
32
|
+
protocol: 'network/v1',
|
|
33
|
+
onopen() { },
|
|
34
|
+
onclose() { }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.#channel.open();
|
|
38
|
+
|
|
39
|
+
this.#session = this.#channel.addMessage({
|
|
40
|
+
encoding: c.raw,
|
|
41
|
+
onmessage: async (incomingMessage) => {
|
|
42
|
+
try {
|
|
43
|
+
if (b4a.isBuffer(incomingMessage)) {
|
|
44
|
+
await this.#router.route(incomingMessage, connection, this.#session);
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error('NetworkMessages: v1 message must be a buffer');
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(`NetworkMessages: Failed to handle incoming v1 message: ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
send(message) {
|
|
56
|
+
this.#session.send(message);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
close() {
|
|
60
|
+
this.#channel.close();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default V1Protocol;
|