trac-msb 0.2.9 → 0.2.11

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 (143) hide show
  1. package/CODE_OF_CONDUCT.md +128 -0
  2. package/README.md +33 -18
  3. package/docker-compose.yml +1 -0
  4. package/docs/trac_network_http_api.openapi.yaml +889 -0
  5. package/msb.mjs +4 -21
  6. package/package.json +16 -12
  7. package/proto/network/v1/enums/message_type.proto +16 -0
  8. package/proto/network/v1/enums/result_code.proto +84 -0
  9. package/proto/network/v1/messages/broadcast_transaction_request.proto +9 -0
  10. package/proto/network/v1/messages/broadcast_transaction_response.proto +13 -0
  11. package/proto/network/v1/messages/liveness_request.proto +8 -0
  12. package/proto/network/v1/messages/liveness_response.proto +11 -0
  13. package/proto/network/v1/network_message.proto +22 -0
  14. package/rpc/handlers.js +163 -90
  15. package/rpc/routes/v1.js +3 -1
  16. package/rpc/rpc_server.js +3 -3
  17. package/rpc/rpc_services.js +45 -31
  18. package/rpc/utils/helpers.js +82 -51
  19. package/scripts/generate-protobufs.js +37 -12
  20. package/src/config/args.js +46 -0
  21. package/src/config/config.js +99 -5
  22. package/src/config/env.js +86 -7
  23. package/src/core/network/Network.js +79 -46
  24. package/src/core/network/protocols/LegacyProtocol.js +21 -11
  25. package/src/core/network/protocols/NetworkMessages.js +38 -17
  26. package/src/core/network/protocols/ProtocolInterface.js +14 -2
  27. package/src/core/network/protocols/ProtocolSession.js +144 -17
  28. package/src/core/network/protocols/V1Protocol.js +37 -18
  29. package/src/core/network/protocols/connectionPolicies.js +88 -0
  30. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +26 -20
  31. package/src/core/network/protocols/{shared/handlers/base/BaseOperationHandler.js → legacy/handlers/BaseStateOperationHandler.js} +25 -15
  32. package/src/core/network/protocols/legacy/handlers/{GetRequestHandler.js → LegacyGetRequestHandler.js} +6 -6
  33. package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +23 -0
  34. package/src/core/network/protocols/{shared/handlers/RoleOperationHandler.js → legacy/handlers/LegacyRoleOperationHandler.js} +20 -13
  35. package/src/core/network/protocols/{shared/handlers/SubnetworkOperationHandler.js → legacy/handlers/LegacySubnetworkOperationHandler.js} +29 -18
  36. package/src/core/network/protocols/{shared/handlers/TransferOperationHandler.js → legacy/handlers/LegacyTransferOperationHandler.js} +18 -12
  37. package/src/core/network/protocols/legacy/validators/base/BaseResponse.js +1 -1
  38. package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +27 -0
  39. package/src/core/network/protocols/shared/validators/{PartialBootstrapDeployment.js → PartialBootstrapDeploymentValidator.js} +9 -4
  40. package/src/core/network/protocols/shared/validators/{base/PartialOperation.js → PartialOperationValidator.js} +47 -25
  41. package/src/core/network/protocols/shared/validators/{PartialRoleAccess.js → PartialRoleAccessValidator.js} +51 -17
  42. package/src/core/network/protocols/shared/validators/{PartialTransaction.js → PartialTransactionValidator.js} +21 -7
  43. package/src/core/network/protocols/shared/validators/{PartialTransfer.js → PartialTransferValidator.js} +26 -9
  44. package/src/core/network/protocols/v1/NetworkMessageRouter.js +91 -7
  45. package/src/core/network/protocols/v1/V1ProtocolError.js +91 -0
  46. package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +65 -0
  47. package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +389 -0
  48. package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +87 -0
  49. package/src/core/network/protocols/v1/validators/V1BaseOperation.js +211 -0
  50. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +26 -0
  51. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +276 -0
  52. package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +15 -0
  53. package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +17 -0
  54. package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +210 -0
  55. package/src/core/network/services/ConnectionManager.js +147 -95
  56. package/src/core/network/services/MessageOrchestrator.js +152 -28
  57. package/src/core/network/services/PendingRequestService.js +172 -0
  58. package/src/core/network/services/TransactionCommitService.js +149 -0
  59. package/src/core/network/services/TransactionPoolService.js +133 -22
  60. package/src/core/network/services/TransactionRateLimiterService.js +57 -42
  61. package/src/core/network/services/ValidatorHealthCheckService.js +127 -0
  62. package/src/core/network/services/ValidatorObserverService.js +23 -32
  63. package/src/core/state/State.js +72 -22
  64. package/src/index.js +8 -5
  65. package/src/messages/network/v1/NetworkMessageBuilder.js +61 -81
  66. package/src/messages/network/v1/NetworkMessageDirector.js +16 -50
  67. package/src/messages/state/ApplyStateMessageBuilder.js +1 -1
  68. package/src/utils/Scheduler.js +0 -8
  69. package/src/utils/check.js +1 -1
  70. package/src/utils/constants.js +68 -19
  71. package/src/utils/deepEqualApplyPayload.js +40 -0
  72. package/src/utils/fileUtils.js +13 -0
  73. package/src/utils/helpers.js +10 -1
  74. package/src/utils/logger.js +25 -0
  75. package/src/utils/normalizers.js +38 -0
  76. package/src/utils/protobuf/networkV1.generated.cjs +2460 -0
  77. package/src/utils/protobuf/operationHelpers.js +24 -3
  78. package/src/utils/type.js +26 -0
  79. package/tests/acceptance/v1/account/account.test.mjs +8 -2
  80. package/tests/acceptance/v1/balance/balance.test.mjs +1 -2
  81. package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +26 -30
  82. package/tests/acceptance/v1/health/health.test.mjs +33 -0
  83. package/tests/acceptance/v1/rpc.test.mjs +3 -2
  84. package/tests/acceptance/v1/tx/tx.test.mjs +50 -17
  85. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +60 -18
  86. package/tests/fixtures/check.fixtures.js +33 -32
  87. package/tests/fixtures/networkV1.fixtures.js +2 -27
  88. package/tests/fixtures/protobuf.fixtures.js +33 -32
  89. package/tests/helpers/StateNetworkFactory.js +2 -2
  90. package/tests/helpers/address.js +6 -0
  91. package/tests/helpers/autobaseTestHelpers.js +2 -1
  92. package/tests/helpers/config.js +2 -1
  93. package/tests/helpers/setupApplyTests.js +6 -10
  94. package/tests/helpers/transactionPayloads.mjs +2 -2
  95. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +241 -81
  96. package/tests/unit/messages/network/NetworkMessageDirector.test.js +225 -81
  97. package/tests/unit/network/LegacyNetworkMessageRouter.test.js +54 -0
  98. package/tests/unit/network/ProtocolSession.test.js +127 -0
  99. package/tests/unit/network/networkModule.test.js +4 -1
  100. package/tests/unit/network/services/ConnectionManager.test.js +450 -0
  101. package/tests/unit/network/services/MessageOrchestrator.test.js +445 -0
  102. package/tests/unit/network/services/PendingRequestService.test.js +431 -0
  103. package/tests/unit/network/services/TransactionCommitService.test.js +246 -0
  104. package/tests/unit/network/services/TransactionPoolService.test.js +489 -0
  105. package/tests/unit/network/services/TransactionRateLimiterService.test.js +139 -0
  106. package/tests/unit/network/services/ValidatorHealthCheckService.test.js +115 -0
  107. package/tests/unit/network/services/services.test.js +17 -0
  108. package/tests/unit/network/utils/v1TestUtils.js +153 -0
  109. package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +151 -0
  110. package/tests/unit/network/v1/V1BaseOperation.test.js +356 -0
  111. package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +129 -0
  112. package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +53 -0
  113. package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +512 -0
  114. package/tests/unit/network/v1/V1LivenessRequest.test.js +32 -0
  115. package/tests/unit/network/v1/V1LivenessResponse.test.js +45 -0
  116. package/tests/unit/network/v1/V1ResultCode.test.js +84 -0
  117. package/tests/unit/network/v1/V1ValidationSchema.test.js +13 -0
  118. package/tests/unit/network/v1/connectionPolicies.test.js +49 -0
  119. package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +284 -0
  120. package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +794 -0
  121. package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +193 -0
  122. package/tests/unit/network/v1/v1.handlers.test.js +15 -0
  123. package/tests/unit/network/v1/v1.test.js +19 -0
  124. package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +119 -0
  125. package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +136 -0
  126. package/tests/unit/network/v1/v1ValidationSchema/common.test.js +308 -0
  127. package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +90 -0
  128. package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +133 -0
  129. package/tests/unit/unit.test.js +2 -2
  130. package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +102 -0
  131. package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +4 -3
  132. package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +3 -2
  133. package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +3 -2
  134. package/tests/unit/utils/protobuf/operationHelpers.test.js +2 -4
  135. package/tests/unit/utils/type/type.test.js +25 -0
  136. package/tests/unit/utils/utils.test.js +2 -0
  137. package/.github/workflows/acceptance-tests.yml +0 -42
  138. package/.github/workflows/publish.yml +0 -33
  139. package/.github/workflows/unit-tests.yml +0 -40
  140. package/proto/network.proto +0 -74
  141. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +0 -37
  142. package/src/utils/protobuf/network.cjs +0 -840
  143. package/tests/unit/network/ConnectionManager.test.js +0 -191
package/src/config/env.js CHANGED
@@ -1,4 +1,5 @@
1
- import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js';
1
+ import { TRAC_NETWORK_MSB_MAINNET_PREFIX, TRAC_NETWORK_MSB_TESTNET1_PREFIX } from 'trac-wallet/constants.js';
2
+ import { TRAC_NETWORK_TESTNET_ID, TRAC_NETWORK_MAINNET_ID } from 'trac-crypto-api/constants.js';
2
3
  import { Config } from './config.js';
3
4
 
4
5
  export const ENV = {
@@ -6,11 +7,52 @@ export const ENV = {
6
7
  DEVELOPMENT: 'development',
7
8
  TESTNET1: 'testnet1'
8
9
  }
9
- // TODO: CREATE TEST ENV CONFIG SIMILAR TO MAINNET AND USE IT IN TESTS.
10
- // TODO: CREATE TESTNET1 ENV CONFIG and update npm scripts to run node witn mainnet or testnet1.
11
10
 
12
11
  const configData = {
12
+ [ENV.TESTNET1]: {
13
+ debug: false,
14
+ addressLength: 67,
15
+ addressPrefix: TRAC_NETWORK_MSB_TESTNET1_PREFIX,
16
+ addressPrefixLength: TRAC_NETWORK_MSB_TESTNET1_PREFIX.length,
17
+ bech32mHrpLength: TRAC_NETWORK_MSB_TESTNET1_PREFIX.length + 1, // len(addressPrefix + separator)
18
+ bootstrap: 'a7c0c2cf8e4722129097f89b2a29a092f23d6268fcca3fd9570ba5b702b99b95',
19
+ channel: '1111trac1network1msb1testnet1111',
20
+ dhtBootstrap: ['116.202.214.149:10001', '157.180.12.214:10001', 'node1.hyperdht.org:49737', 'node2.hyperdht.org:49737', 'node3.hyperdht.org:49737'], // these are used to peer discovery
21
+ disableRateLimit: false,
22
+ enableErrorApplyLogs: true,
23
+ enableInteractiveMode: true,
24
+ enableRoleRequester: false,
25
+ enableTxApplyLogs: true,
26
+ enableValidatorObserver: true,
27
+ enableWallet: true,
28
+ maxValidators: 50,
29
+ maxRetries: 3,
30
+ messageThreshold: 3,
31
+ messageValidatorRetryDelay: 1000, //How long to wait before retrying (ms) MESSAGE_VALIDATOR_RETRY_DELAY_MS
32
+ messageValidatorResponseTimeout: 3 * 3 * 1000, //Overall timeout for sending a message (ms). This is 3 * maxRetries * messageValidatorRetryDelay;
33
+ host: 'localhost',
34
+ port: 5000,
35
+ networkId: TRAC_NETWORK_TESTNET_ID,
36
+ maxPeers: 64, // Connectivity constants
37
+ maxParallel: 64, // Connectivity constants
38
+ maxServerConnections: Infinity, // Connectivity constants
39
+ maxClientConnections: Infinity, // Connectivity constants
40
+ maxWritersForAdminIndexerConnection: 10, // Connectivity constants
41
+ processIntervalMs: 50, // Pool constants
42
+ transactionPoolSize: 1000, // Operation handler constants
43
+ rateLimitCleanupIntervalMs: 120_000, // Rate limiting constants
44
+ rateLimitConnectionTimeoutMs: 60_000, // Rate limiting constants
45
+ rateLimitMaxTransactionsPerSecond: 50, // Rate limiting constants
46
+ maxPendingRequestsInPendingRequestsService: 50_000, // Maximum number of pending requests in PendingRequestService (This value should not exceed 256MB)
47
+ pendingRequestTimeout: 3000, // constant after which time the transaction will be considered invalid
48
+ txCommitTimeout: 2200,
49
+ txPoolSize: 1000, // size of transaction pool
50
+ validatorHealthCheckInterval: 5 * 60 * 1000, // How often to check validator health (ms)
51
+ storesDirectory: 'stores/',
52
+ storeName: 'testnet',
53
+ },
13
54
  [ENV.MAINNET]: {
55
+ debug: false,
14
56
  addressLength: 63,
15
57
  addressPrefix: TRAC_NETWORK_MSB_MAINNET_PREFIX,
16
58
  addressPrefixLength: TRAC_NETWORK_MSB_MAINNET_PREFIX.length,
@@ -30,10 +72,29 @@ const configData = {
30
72
  messageThreshold: 3,
31
73
  messageValidatorRetryDelay: 1000, //How long to wait before retrying (ms) MESSAGE_VALIDATOR_RETRY_DELAY_MS
32
74
  messageValidatorResponseTimeout: 3 * 3 * 1000, //Overall timeout for sending a message (ms). This is 3 * maxRetries * messageValidatorRetryDelay;
33
- networkId: 918,
75
+ host: 'localhost',
76
+ port: 5000,
77
+ networkId: TRAC_NETWORK_MAINNET_ID,
78
+ maxPeers: 64, // Connectivity constants
79
+ maxParallel: 64, // Connectivity constants
80
+ maxServerConnections: Infinity, // Connectivity constants
81
+ maxClientConnections: Infinity, // Connectivity constants
82
+ maxWritersForAdminIndexerConnection: 10, // Connectivity constants
83
+ processIntervalMs: 50, // Pool constants
84
+ transactionPoolSize: 1000, // Operation handler constants
85
+ rateLimitCleanupIntervalMs: 120_000, // Rate limiting constants
86
+ rateLimitConnectionTimeoutMs: 60_000, // Rate limiting constants
87
+ rateLimitMaxTransactionsPerSecond: 50, // Rate limiting constants
88
+ maxPendingRequestsInPendingRequestsService: 50_000, // Maximum number of pending requests in PendingRequestService (This value should not exceed 256MB)
89
+ pendingRequestTimeout: 3000, // constant after which time the transaction will be considered invalid
90
+ txCommitTimeout: 2200,
91
+ txPoolSize: 1000, // size of transaction pool
92
+ validatorHealthCheckInterval: 5 * 60 * 1000, // How often to check validator health (ms)
34
93
  storesDirectory: 'stores/',
94
+ storeName: 'mainnet',
35
95
  },
36
96
  [ENV.DEVELOPMENT]: {
97
+ debug: true,
37
98
  addressLength: 63,
38
99
  addressPrefix: TRAC_NETWORK_MSB_MAINNET_PREFIX,
39
100
  addressPrefixLength: TRAC_NETWORK_MSB_MAINNET_PREFIX.length,
@@ -49,12 +110,30 @@ const configData = {
49
110
  enableValidatorObserver: true,
50
111
  enableWallet: true,
51
112
  maxValidators: 6,
52
- maxRetries: 0,
113
+ maxRetries: 3,
53
114
  messageThreshold: 1000,
54
115
  messageValidatorRetryDelay: 1000, //How long to wait before retrying (ms) MESSAGE_VALIDATOR_RETRY_DELAY_MS
55
116
  messageValidatorResponseTimeout: 3 * 3 * 1000, //Overall timeout for sending a message (ms). This is 3 * maxRetries * messageValidatorRetryDelay;
56
- networkId: 918,
57
- storesDirectory : 'stores/',
117
+ host: 'localhost',
118
+ port: 5000,
119
+ networkId: TRAC_NETWORK_TESTNET_ID,
120
+ maxPeers: 64, // Connectivity constants
121
+ maxParallel: 64, // Connectivity constants
122
+ maxServerConnections: Infinity, // Connectivity constants
123
+ maxClientConnections: Infinity, // Connectivity constants
124
+ maxWritersForAdminIndexerConnection: 10, // Connectivity constants
125
+ processIntervalMs: 50, // Pool constants
126
+ transactionPoolSize: 1000, // Operation handler constants
127
+ rateLimitCleanupIntervalMs: 120_000, // Rate limiting constants
128
+ rateLimitConnectionTimeoutMs: 60_000, // Rate limiting constants
129
+ rateLimitMaxTransactionsPerSecond: 50, // Rate limiting constants
130
+ maxPendingRequestsInPendingRequestsService: 50_000, // Maximum number of pending requests in PendingRequestService (This value should not exceed 256MB)
131
+ pendingRequestTimeout: 3000, // constant after which time the transaction will be considered invalid
132
+ txCommitTimeout: 2200,
133
+ txPoolSize: 1000, // size of transaction pool
134
+ validatorHealthCheckInterval: 5 * 60 * 1000, // How often to check validator health (ms)
135
+ storesDirectory: 'stores/',
136
+ storeName: 'development',
58
137
  }
59
138
  }
60
139
 
@@ -8,26 +8,16 @@ import NetworkMessages from './protocols/NetworkMessages.js';
8
8
  import { sleep } from '../../utils/helpers.js';
9
9
  import {
10
10
  TRAC_NAMESPACE,
11
- MAX_PEERS,
12
- MAX_PARALLEL,
13
- MAX_SERVER_CONNECTIONS,
14
- MAX_CLIENT_CONNECTIONS,
15
- NETWORK_MESSAGE_TYPES
11
+ EventType
16
12
  } from '../../utils/constants.js';
17
13
  import ConnectionManager from './services/ConnectionManager.js';
18
14
  import MessageOrchestrator from './services/MessageOrchestrator.js';
19
15
  import NetworkWalletFactory from './identity/NetworkWalletFactory.js';
20
- import { EventType } from '../../utils/constants.js';
21
- import { networkMessageFactory } from '../../messages/network/v1/networkMessageFactory.js';
22
16
  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
- };
17
+ import PendingRequestService from './services/PendingRequestService.js';
18
+ import TransactionCommitService from "./services/TransactionCommitService.js";
19
+ import ValidatorHealthCheckService from './services/ValidatorHealthCheckService.js';
20
+ import { Logger } from '../../utils/logger.js';
31
21
 
32
22
  const wakeup = new w();
33
23
 
@@ -44,10 +34,15 @@ class Network extends ReadyResource {
44
34
  #connectTimeoutMs;
45
35
  #maxPendingConnections;
46
36
  #rateLimiter;
37
+ #pendingRequestsService;
38
+ #transactionCommitService;
39
+ #wallet;
40
+ #validatorHealthCheckService;
41
+ #logger;
47
42
 
48
43
  /**
49
44
  * @param {State} state
50
- * @param {object} config
45
+ * @param {Config} config
51
46
  * @param {string} address
52
47
  **/
53
48
  constructor(state, config, address = null) {
@@ -56,11 +51,13 @@ class Network extends ReadyResource {
56
51
  this.#connectTimeoutMs = config.connectTimeoutMs || 5000;
57
52
  this.#maxPendingConnections = config.maxPendingConnections || 50;
58
53
  this.#pendingConnections = new Map();
59
- this.#transactionPoolService = new TransactionPoolService(state, address, this.#config);
54
+ this.#transactionCommitService = new TransactionCommitService(this.#config);
55
+ this.#transactionPoolService = new TransactionPoolService(state, address, this.#transactionCommitService ,this.#config);
60
56
  this.#validatorObserverService = new ValidatorObserverService(this, state, address, this.#config);
61
57
  this.#validatorConnectionManager = new ConnectionManager(this.#config);
62
58
  this.#validatorMessageOrchestrator = new MessageOrchestrator(this.#validatorConnectionManager, state, this.#config);
63
-
59
+ this.#pendingRequestsService = new PendingRequestService(this.#config);
60
+ this.#logger = new Logger(this.#config);
64
61
  }
65
62
 
66
63
  get swarm() {
@@ -84,7 +81,7 @@ class Network extends ReadyResource {
84
81
  }
85
82
 
86
83
  async _open() {
87
- console.log('Network initialization...');
84
+ this.#logger.info('Network initialization...');
88
85
 
89
86
  this.setupNetworkListeners();
90
87
 
@@ -93,15 +90,19 @@ class Network extends ReadyResource {
93
90
  }
94
91
 
95
92
  async _close() {
96
- // TODO: Implement better "await" logic for stopping services
97
- console.log('Network: closing gracefully...');
98
- this.transactionPoolService.stopPool();
93
+ this.#logger.info('Network: closing gracefully...');
94
+ await this.transactionPoolService.stopPool();
99
95
  await sleep(100);
100
- this.#validatorObserverService.stopValidatorObserver();
96
+ await this.#validatorObserverService.stopValidatorObserver();
101
97
  await sleep(5_000);
98
+ if (this.#validatorHealthCheckService) {
99
+ await this.#validatorHealthCheckService.close();
100
+ }
102
101
 
103
102
  this.cleanupNetworkListeners();
104
103
  this.cleanupPendingConnections();
104
+ this.#pendingRequestsService.close();
105
+ this.#transactionCommitService.close();
105
106
 
106
107
  if (this.#swarm !== null) {
107
108
  this.#swarm.destroy();
@@ -109,23 +110,41 @@ class Network extends ReadyResource {
109
110
  }
110
111
 
111
112
  setupNetworkListeners() {
112
- // VALIDATOR_CONNECTION_TIMEOUT
113
113
  this.on(EventType.VALIDATOR_CONNECTION_TIMEOUT, ({ publicKey, type, timeoutMs }) => {
114
- debugLog(`Network Event: VALIDATOR_CONNECTION_TIMEOUT | PublicKey: ${publicKey} | Type: ${type} | TimeoutMs: ${timeoutMs}`);
114
+ this.#logger.debug(`Network Event: VALIDATOR_CONNECTION_TIMEOUT | PublicKey: ${publicKey} | Type: ${type} | TimeoutMs: ${timeoutMs}`);
115
115
  this.#pendingConnections.delete(publicKey);
116
116
  });
117
117
 
118
- // VALIDATOR_CONNECTION_READY
119
118
  this.on(EventType.VALIDATOR_CONNECTION_READY, async ({ publicKey, type, connection }) => {
120
- debugLog(`Network Event: VALIDATOR_CONNECTION_READY | PublicKey: ${publicKey} | Type: ${type}`);
119
+ this.#logger.debug(`Network Event: VALIDATOR_CONNECTION_READY | PublicKey: ${publicKey} | Type: ${type}`);
121
120
  const { timeoutId } = this.#pendingConnections.get(publicKey);
121
+
122
122
  if (!timeoutId) return;
123
123
 
124
124
  clearTimeout(timeoutId);
125
125
  this.#pendingConnections.delete(publicKey);
126
126
 
127
127
  if (type === 'validator') {
128
- await connection.protocolSession.send(NETWORK_MESSAGE_TYPES.GET.VALIDATOR);
128
+ try {
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
+ }
129
148
  }
130
149
 
131
150
  });
@@ -150,27 +169,33 @@ class Network extends ReadyResource {
150
169
  ) {
151
170
  if (!this.#swarm) {
152
171
  const keyPair = await this.initializeNetworkingKeyPair(store, wallet);
153
- const wrappedWallet = this.#getNetworkWalletWrapper(wallet, keyPair);
172
+ this.#wallet = this.#getNetworkWalletWrapper(wallet, keyPair);
173
+ this.#validatorMessageOrchestrator.setWallet(this.#wallet);
174
+
154
175
  this.#swarm = new Hyperswarm({
155
176
  keyPair,
156
177
  bootstrap: this.#config.dhtBootstrap,
157
- maxPeers: MAX_PEERS,
158
- maxParallel: MAX_PARALLEL,
159
- maxServerConnections: MAX_SERVER_CONNECTIONS,
160
- maxClientConnections: MAX_CLIENT_CONNECTIONS
178
+ maxPeers: this.#config.maxPeers,
179
+ maxParallel: this.#config.maxParallel,
180
+ maxServerConnections: this.#config.maxServerConnections,
181
+ maxClientConnections: this.#config.maxClientConnections
161
182
  });
162
183
 
163
- this.#rateLimiter = new TransactionRateLimiterService(this.#swarm);
184
+ this.#rateLimiter = new TransactionRateLimiterService(this.#swarm, this.#config);
164
185
  this.#networkMessages = new NetworkMessages(
165
186
  state,
166
- wrappedWallet,
187
+ this.#wallet,
167
188
  this.#rateLimiter,
168
189
  this.#transactionPoolService,
169
- this.#validatorConnectionManager,
190
+ this.#pendingRequestsService,
191
+ this.#transactionCommitService,
170
192
  this.#config
171
193
  );
194
+ this.#validatorHealthCheckService = new ValidatorHealthCheckService(this.#config);
195
+ await this.#validatorHealthCheckService.ready();
196
+ this.#validatorConnectionManager.subscribeToHealthChecks(this.#validatorHealthCheckService);
172
197
 
173
- console.log(`Channel: ${b4a.toString(this.#config.channel)}`);
198
+ this.#logger.info(`Channel: ${b4a.toString(this.#config.channel)}`);
174
199
 
175
200
  this.#swarm.on('connection', async (connection) => {
176
201
  // Per-peer connection initialization:
@@ -189,12 +214,20 @@ class Network extends ReadyResource {
189
214
  }
190
215
 
191
216
  connection.on('close', () => {
217
+ this.#pendingRequestsService.rejectPendingRequestsForPeer(
218
+ publicKey,
219
+ new Error('Connection closed before response')
220
+ );
192
221
  this.#swarm.leavePeer(connection.remotePublicKey);
193
222
  this.#validatorConnectionManager.remove(publicKey);
194
223
  connection.protocolSession.close();
195
224
  });
196
225
 
197
226
  connection.on('error', (error) => {
227
+ this.#pendingRequestsService.rejectPendingRequestsForPeer(
228
+ publicKey,
229
+ error ?? new Error('Connection error before response')
230
+ );
198
231
  if (
199
232
  error && error.message && (
200
233
  error.message.includes('connection reset by peer') ||
@@ -204,7 +237,7 @@ class Network extends ReadyResource {
204
237
  // TODO: decide if we want to handle this error in a specific way. It generates a lot of logs.
205
238
  return;
206
239
  }
207
- console.error(error.message)
240
+ this.#logger.error(error?.message ?? 'Unknown network connection error');
208
241
  });
209
242
 
210
243
  });
@@ -236,7 +269,7 @@ class Network extends ReadyResource {
236
269
  async tryConnect(publicKey, type = null) {
237
270
  if (this.#swarm === null) throw new Error('Network swarm is not initialized');
238
271
  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.`);
272
+ this.#logger.debug(`Network.tryConnect: Connection to peer: ${publicKey} as type: ${type} is already pending or max pending connections reached.`);
240
273
  return;
241
274
  }
242
275
 
@@ -254,21 +287,21 @@ class Network extends ReadyResource {
254
287
  const peerInfo = this.#swarm.peers.get(publicKey);
255
288
  if (peerInfo) {
256
289
  const connection = this.#swarm._allConnections.get(peerInfo.publicKey);
257
- if (connection && connection.protocolSession) {
290
+
291
+ if (connection &&
292
+ connection.protocolSession &&
293
+ !connection.protocolSession.isProbed() &&
294
+ !this.#pendingRequestsService.isProbePending(connection.remotePublicKey.toString('hex'))
295
+ ) {
258
296
  await this.#finalizeConnection(publicKey, type, connection);
259
297
  }
260
298
  }
261
299
  }
262
300
 
263
- async isConnected(publicKey) {
264
- return this.#swarm.peers.has(publicKey) &&
265
- this.#swarm.peers.get(publicKey).connectedTime != -1
266
- }
267
-
268
301
  async #finalizeConnection(publicKey, type, connection) {
269
302
  if (!this.#pendingConnections.has(publicKey)) return;
270
303
  this.emit(EventType.VALIDATOR_CONNECTION_READY, { publicKey, type, connection });
271
- debugLog(`Network.finalizeConnection: Connected to peer: ${publicKey} as type: ${type}`);
304
+ this.#logger.debug(`Network.finalizeConnection: Connected to peer: ${publicKey} as type: ${type}`);
272
305
  }
273
306
 
274
307
  #getNetworkWalletWrapper(wallet, keyPair) {
@@ -9,8 +9,8 @@ class LegacyProtocol extends ProtocolInterface {
9
9
  #config;
10
10
  #router;
11
11
 
12
- constructor(router, connection, config) {
13
- super(router, connection, config);
12
+ constructor(router, connection, pendingRequestServiceInstance = null, config) {
13
+ super(router, connection, pendingRequestServiceInstance, config);
14
14
  this.#config = config;
15
15
  this.#router = router;
16
16
  this.init(connection);
@@ -41,20 +41,30 @@ class LegacyProtocol extends ProtocolInterface {
41
41
  this.#session = this.#channel.addMessage({
42
42
  encoding: c.json,
43
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');
44
+ this.#router.route(incomingMessage, connection).catch((err) => {
45
+ console.error(`LegacyProtocol: unhandled router error: ${err.message}`);
46
+ try {
47
+ connection.end();
48
+ } catch {
49
49
  }
50
- } catch (error) {
51
- console.error(`NetworkMessages: Failed to handle incoming message: ${error.message}`);
52
- }
50
+ });
53
51
  }
54
52
  });
55
53
  }
56
54
 
57
- send(message) {
55
+ // TODO: Legacy protocol does not require encoding. Consider removing this method after refactoring v1 and the protocol interface
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) {
58
68
  this.#session.send(message);
59
69
  }
60
70
 
@@ -1,47 +1,68 @@
1
-
2
- import b4a from 'b4a';
3
1
  import NetworkMessageRouter from './legacy/NetworkMessageRouter.js';
4
2
  import NetworkMessageRouterV1 from './v1/NetworkMessageRouter.js';
5
3
  import ProtocolSession from './ProtocolSession.js';
6
4
  import LegacyProtocol from './LegacyProtocol.js';
7
5
  import V1Protocol from './V1Protocol.js';
6
+
8
7
  class NetworkMessages {
9
8
  #legacyMessageRouter;
10
9
  #v1MessageRouter;
11
10
  #config;
11
+ #wallet;
12
+ #pendingRequestsService;
12
13
 
13
- /**
14
- * @param {object} config
15
- **/
16
- constructor(state, wallet, rateLimiterService, txPoolService, connectionManager, config) {
14
+ constructor(
15
+ state,
16
+ wallet,
17
+ rateLimiterService,
18
+ txPoolService,
19
+ pendingRequestsService,
20
+ transactionCommitService,
21
+ config
22
+ ) {
17
23
  this.#config = config;
18
- this.#initializeMessageRouter(state, wallet, rateLimiterService, txPoolService, connectionManager);
19
- }
20
-
21
- #initializeMessageRouter(state, wallet, rateLimiterService, txPoolService, connectionManager) {
24
+ this.#wallet = wallet;
25
+ this.#pendingRequestsService = pendingRequestsService;
22
26
  this.#legacyMessageRouter = new NetworkMessageRouter(
23
27
  state,
24
28
  wallet,
25
29
  rateLimiterService,
26
30
  txPoolService,
27
- connectionManager,
28
31
  this.#config
29
32
  );
30
- this.#v1MessageRouter = new NetworkMessageRouterV1(this.#config);
33
+
34
+ this.#v1MessageRouter = new NetworkMessageRouterV1(
35
+ state,
36
+ wallet,
37
+ rateLimiterService,
38
+ txPoolService,
39
+ pendingRequestsService,
40
+ transactionCommitService,
41
+ this.#config
42
+ );
31
43
  }
32
44
 
33
45
  async setupProtomuxMessages(connection) {
34
46
  // Attach a Protomux instance to this Hyperswarm connection.
35
47
  // Protomux multiplexes multiple logical protocol channels over a single encrypted stream.
36
48
 
37
- const legacyProtocol = new LegacyProtocol(this.#legacyMessageRouter, connection, this.#config);
38
- const v1Protocol = new V1Protocol(this.#v1MessageRouter, connection, this.#config);
49
+ const legacyProtocol = new LegacyProtocol(
50
+ this.#legacyMessageRouter,
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
+ );
39
62
 
40
63
  // ProtocolSession is attached to the Hyperswarm connection so other parts of the system (e.g. tryConnect)
41
64
  // can send messages without knowing how Protomux was initialized.
42
- const protocolSession = new ProtocolSession(legacyProtocol, v1Protocol);
43
- connection.protocolSession = protocolSession;
44
-
65
+ connection.protocolSession = new ProtocolSession(legacyProtocol, v1Protocol, this.#wallet, this.#config);
45
66
  }
46
67
  }
47
68
 
@@ -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, config) {
9
+ constructor(router, connection, pendingRequestService, config) {
10
10
  if (new.target === ProtocolInterface) {
11
11
  throw new Error('ProtocolInterface cannot be instantiated directly');
12
12
  }
@@ -17,11 +17,23 @@ class ProtocolInterface {
17
17
  throw new Error('init() method must be implemented by subclass');
18
18
  }
19
19
 
20
- send(message) {
20
+ // TODO: This method is only kept here because of v1, but it should be probably removed from the interface
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) {
21
28
  // Abstract method. Need to be implemented by subclasses.
22
29
  throw new Error('send() method must be implemented by subclass');
23
30
  }
24
31
 
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
+
25
37
  close() {
26
38
  // Abstract method. Need to be implemented by subclasses.
27
39
  throw new Error('close() method must be implemented by subclass');