trac-msb 0.2.8 → 0.2.10

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 (151) hide show
  1. package/.github/workflows/acceptance-tests.yml +7 -11
  2. package/.github/workflows/lint-pr-title.yml +26 -0
  3. package/.github/workflows/unit-tests.yml +2 -8
  4. package/CODE_OF_CONDUCT.md +128 -0
  5. package/README.md +33 -18
  6. package/docker-compose.yml +1 -0
  7. package/docs/trac_network_http_api.openapi.yaml +889 -0
  8. package/msb.mjs +5 -22
  9. package/package.json +14 -10
  10. package/proto/network.proto +74 -0
  11. package/rpc/create_server.js +2 -2
  12. package/rpc/handlers.js +165 -92
  13. package/rpc/routes/v1.js +3 -1
  14. package/rpc/rpc_server.js +4 -4
  15. package/rpc/rpc_services.js +62 -25
  16. package/rpc/utils/helpers.js +83 -52
  17. package/src/config/args.js +46 -0
  18. package/src/config/config.js +78 -5
  19. package/src/config/env.js +70 -3
  20. package/src/core/network/Network.js +34 -70
  21. package/src/core/network/identity/NetworkWalletFactory.js +2 -2
  22. package/src/core/network/protocols/LegacyProtocol.js +67 -0
  23. package/src/core/network/protocols/NetworkMessages.js +48 -0
  24. package/src/core/network/protocols/ProtocolInterface.js +31 -0
  25. package/src/core/network/protocols/ProtocolSession.js +59 -0
  26. package/src/core/network/protocols/V1Protocol.js +64 -0
  27. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +84 -0
  28. package/src/core/network/protocols/legacy/handlers/GetRequestHandler.js +53 -0
  29. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
  30. package/src/core/network/{messaging → protocols/legacy}/validators/base/BaseResponse.js +2 -3
  31. package/src/core/network/protocols/shared/handlers/RoleOperationHandler.js +88 -0
  32. package/src/core/network/protocols/shared/handlers/SubnetworkOperationHandler.js +93 -0
  33. package/src/core/network/{messaging → protocols/shared}/handlers/TransferOperationHandler.js +17 -16
  34. package/src/core/network/{messaging → protocols/shared}/handlers/base/BaseOperationHandler.js +10 -15
  35. package/src/core/network/{messaging → protocols/shared}/validators/PartialBootstrapDeployment.js +2 -2
  36. package/src/core/network/{messaging → protocols/shared}/validators/PartialRoleAccess.js +5 -5
  37. package/src/core/network/{messaging → protocols/shared}/validators/PartialTransaction.js +4 -4
  38. package/src/core/network/{messaging → protocols/shared}/validators/PartialTransfer.js +4 -4
  39. package/src/core/network/{messaging → protocols/shared}/validators/base/PartialOperation.js +14 -12
  40. package/src/core/network/protocols/v1/NetworkMessageRouter.js +15 -0
  41. package/src/core/network/services/ConnectionManager.js +5 -5
  42. package/src/core/network/services/MessageOrchestrator.js +2 -2
  43. package/src/core/network/services/TransactionPoolService.js +5 -6
  44. package/src/core/network/services/TransactionRateLimiterService.js +12 -13
  45. package/src/core/network/services/ValidatorObserverService.js +5 -6
  46. package/src/core/state/State.js +3 -5
  47. package/src/index.js +156 -181
  48. package/src/messages/network/v1/NetworkMessageBuilder.js +325 -0
  49. package/src/messages/network/v1/NetworkMessageDirector.js +137 -0
  50. package/src/messages/network/v1/networkMessageFactory.js +12 -0
  51. package/src/messages/state/ApplyStateMessageBuilder.js +661 -0
  52. package/src/messages/state/ApplyStateMessageDirector.js +516 -0
  53. package/src/messages/state/applyStateMessageFactory.js +12 -0
  54. package/src/utils/buffer.js +53 -1
  55. package/src/utils/check.js +1 -1
  56. package/src/utils/cli.js +0 -8
  57. package/src/utils/constants.js +33 -30
  58. package/src/utils/fileUtils.js +13 -0
  59. package/src/utils/normalizers.js +84 -2
  60. package/src/utils/protobuf/network.cjs +840 -0
  61. package/src/utils/protobuf/operationHelpers.js +10 -0
  62. package/src/utils/type.js +26 -0
  63. package/tests/acceptance/v1/balance/balance.test.mjs +1 -2
  64. package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +26 -30
  65. package/tests/acceptance/v1/health/health.test.mjs +33 -0
  66. package/tests/acceptance/v1/rpc.test.mjs +4 -3
  67. package/tests/acceptance/v1/tx/tx.test.mjs +27 -16
  68. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +26 -12
  69. package/tests/fixtures/check.fixtures.js +33 -32
  70. package/tests/fixtures/networkV1.fixtures.js +85 -0
  71. package/tests/fixtures/protobuf.fixtures.js +109 -25
  72. package/tests/helpers/StateNetworkFactory.js +2 -2
  73. package/tests/helpers/address.js +6 -0
  74. package/tests/helpers/autobaseTestHelpers.js +2 -1
  75. package/tests/helpers/config.js +2 -1
  76. package/tests/helpers/setupApplyTests.js +59 -56
  77. package/tests/unit/messages/messages.test.js +12 -0
  78. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +276 -0
  79. package/tests/unit/messages/network/NetworkMessageDirector.test.js +201 -0
  80. package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +521 -0
  81. package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +233 -0
  82. package/tests/unit/network/ConnectionManager.test.js +6 -5
  83. package/tests/unit/network/networkModule.test.js +3 -2
  84. package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +10 -6
  85. package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +9 -6
  86. package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +10 -7
  87. package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +18 -21
  88. package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +53 -38
  89. package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +46 -35
  90. package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +13 -16
  91. package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +17 -11
  92. package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +11 -12
  93. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +9 -7
  94. package/tests/unit/state/apply/common/commonScenarioHelper.js +15 -14
  95. package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +9 -4
  96. package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +17 -11
  97. package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +19 -14
  98. package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +37 -29
  99. package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +9 -7
  100. package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +11 -9
  101. package/tests/unit/unit.test.js +1 -1
  102. package/tests/unit/utils/buffer/buffer.test.js +62 -1
  103. package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +4 -3
  104. package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +3 -2
  105. package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +3 -2
  106. package/tests/unit/utils/normalizers/normalizers.test.js +469 -0
  107. package/tests/unit/utils/protobuf/operationHelpers.test.js +120 -2
  108. package/tests/unit/utils/type/type.test.js +25 -0
  109. package/tests/unit/utils/utils.test.js +1 -0
  110. package/docs/networking-dualstack-plan.md +0 -75
  111. package/docs/networking-layer-redesign.md +0 -155
  112. package/src/core/network/messaging/NetworkMessages.js +0 -64
  113. package/src/core/network/messaging/handlers/GetRequestHandler.js +0 -113
  114. package/src/core/network/messaging/handlers/ResponseHandler.js +0 -107
  115. package/src/core/network/messaging/handlers/RoleOperationHandler.js +0 -114
  116. package/src/core/network/messaging/handlers/SubnetworkOperationHandler.js +0 -149
  117. package/src/core/network/messaging/routes/NetworkMessageRouter.js +0 -98
  118. package/src/core/network/messaging/validators/AdminResponse.js +0 -58
  119. package/src/core/network/messaging/validators/CustomNodeResponse.js +0 -46
  120. package/src/messages/base/StateBuilder.js +0 -25
  121. package/src/messages/completeStateMessages/CompleteStateMessageBuilder.js +0 -425
  122. package/src/messages/completeStateMessages/CompleteStateMessageDirector.js +0 -252
  123. package/src/messages/completeStateMessages/CompleteStateMessageOperations.js +0 -296
  124. package/src/messages/partialStateMessages/PartialStateMessageBuilder.js +0 -272
  125. package/src/messages/partialStateMessages/PartialStateMessageDirector.js +0 -137
  126. package/src/messages/partialStateMessages/PartialStateMessageOperations.js +0 -138
  127. package/tests/integration/apply/addAdmin/addAdminBasic.test.js +0 -69
  128. package/tests/integration/apply/addAdmin/addAdminRecovery.test.js +0 -126
  129. package/tests/integration/apply/addIndexer.test.js +0 -239
  130. package/tests/integration/apply/addWhitelist.test.js +0 -53
  131. package/tests/integration/apply/addWriter.test.js +0 -245
  132. package/tests/integration/apply/apply.test.js +0 -19
  133. package/tests/integration/apply/banValidator.test.js +0 -116
  134. package/tests/integration/apply/postTx/invalidSubValues.test.js +0 -103
  135. package/tests/integration/apply/postTx/postTx.test.js +0 -196
  136. package/tests/integration/apply/removeIndexer.test.js +0 -132
  137. package/tests/integration/apply/removeWriter.test.js +0 -168
  138. package/tests/integration/apply/transfer.test.js +0 -83
  139. package/tests/integration/integration.test.js +0 -9
  140. package/tests/unit/messageOperations/assembleAddIndexerMessage.test.js +0 -21
  141. package/tests/unit/messageOperations/assembleAddWriterMessage.test.js +0 -17
  142. package/tests/unit/messageOperations/assembleAdminMessage.test.js +0 -68
  143. package/tests/unit/messageOperations/assembleBanWriterMessage.test.js +0 -17
  144. package/tests/unit/messageOperations/assemblePostTransaction.test.js +0 -424
  145. package/tests/unit/messageOperations/assembleRemoveIndexerMessage.test.js +0 -19
  146. package/tests/unit/messageOperations/assembleRemoveWriterMessage.test.js +0 -17
  147. package/tests/unit/messageOperations/assembleWhitelistMessages.test.js +0 -59
  148. package/tests/unit/messageOperations/commonsStateMessageOperationsTest.js +0 -278
  149. package/tests/unit/messageOperations/stateMessageOperations.test.js +0 -19
  150. /package/src/core/network/{messaging → protocols/legacy}/validators/ValidatorResponse.js +0 -0
  151. /package/src/utils/{operations.js → applyOperations.js} +0 -0
@@ -2,12 +2,20 @@ import test from 'brittle';
2
2
  import b4a from 'b4a';
3
3
 
4
4
  import applyOperations from '../../../../src/utils/protobuf/applyOperations.cjs';
5
+ import {
6
+ decodeV1networkOperation,
7
+ encodeV1networkOperation,
8
+ normalizeIncomingMessage,
9
+ safeDecodeApplyOperation,
10
+ safeEncodeApplyOperation,
11
+ } from '../../../../src/utils/protobuf/operationHelpers.js';
5
12
  import fixtures from '../../../fixtures/protobuf.fixtures.js';
13
+ import networkV1Fixtures from '../../../fixtures/networkV1.fixtures.js';
6
14
 
7
- //TODO add missing operations tests and fill fixtures with them
8
15
  test('Happy path encode/decode roundtrip for protobuf applyOperation payloads', t => {
9
16
  const payloadsHashMap = new Map([
10
17
  ["txComplete", fixtures.validTransactionOperation],
18
+ ["txPartial", fixtures.validPartialTransactionOperation],
11
19
  ["addIndexer", fixtures.validAddIndexer],
12
20
  ["removeIndexer", fixtures.validRemoveIndexer],
13
21
  ["appendWhitelist", fixtures.validAppendWhitelist],
@@ -19,8 +27,12 @@ test('Happy path encode/decode roundtrip for protobuf applyOperation payloads',
19
27
  ["removeWriterPartial", fixtures.validPartialRemoveWriter],
20
28
  ["adminRecoveryComplete", fixtures.validCompleteAdminRecovery],
21
29
  ["adminRecoveryPartial", fixtures.validPartialAdminRecovery],
30
+ ["bootstrapDeploymentComplete", fixtures.validCompleteBootstrapDeployment],
31
+ ["bootstrapDeploymentPartial", fixtures.validPartialBootstrapDeployment],
22
32
  ["transferComplete", fixtures.validTransferOperation],
23
- ["balanceInitialization", fixtures.validBalanceInitOperation]
33
+ ["transferPartial", fixtures.validPartialTransferOperation],
34
+ ["balanceInitialization", fixtures.validBalanceInitOperation],
35
+ ["disableInitialization", fixtures.validDisableInitialization],
24
36
  ]);
25
37
 
26
38
  for (const [key, value] of payloadsHashMap) {
@@ -101,6 +113,13 @@ test('Protobuf encode/decode is order-independent for all operation types', t =>
101
113
  let decoded = applyOperations.Operation.decode(encoded);
102
114
  t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validTransactionOperation), 'TX operation encodes/decodes correctly with shuffled fields');
103
115
 
116
+ // Test TX operation (partial)
117
+ const shuffledPartialTxo = shuffleObject(fixtures.validPartialTransactionOperation.txo);
118
+ const shuffledPartialTx = { ...fixtures.validPartialTransactionOperation, txo: shuffledPartialTxo };
119
+ encoded = applyOperations.Operation.encode(shuffledPartialTx);
120
+ decoded = applyOperations.Operation.decode(encoded);
121
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialTransactionOperation), 'Partial TX operation encodes/decodes correctly with shuffled fields');
122
+
104
123
  // Test TRANSFER operation
105
124
  const shuffledTro = shuffleObject(fixtures.validTransferOperation.tro);
106
125
  const shuffledTransfer = { ...fixtures.validTransferOperation, tro: shuffledTro };
@@ -108,6 +127,13 @@ test('Protobuf encode/decode is order-independent for all operation types', t =>
108
127
  decoded = applyOperations.Operation.decode(encoded);
109
128
  t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validTransferOperation), 'TRANSFER operation encodes/decodes correctly with shuffled fields');
110
129
 
130
+ // Test TRANSFER operation (partial)
131
+ const shuffledPartialTro = shuffleObject(fixtures.validPartialTransferOperation.tro);
132
+ const shuffledPartialTransfer = { ...fixtures.validPartialTransferOperation, tro: shuffledPartialTro };
133
+ encoded = applyOperations.Operation.encode(shuffledPartialTransfer);
134
+ decoded = applyOperations.Operation.decode(encoded);
135
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialTransferOperation), 'Partial TRANSFER operation encodes/decodes correctly with shuffled fields');
136
+
111
137
  // Test ADD_INDEXER operation
112
138
  const shuffledAco = shuffleObject(fixtures.validAddIndexer.aco);
113
139
  const shuffledAddIndexer = { ...fixtures.validAddIndexer, aco: shuffledAco };
@@ -184,4 +210,96 @@ test('Protobuf encode/decode is order-independent for all operation types', t =>
184
210
  encoded = applyOperations.Operation.encode(shuffledPartialAdminRecovery);
185
211
  decoded = applyOperations.Operation.decode(encoded);
186
212
  t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialAdminRecovery), 'Partial ADMIN_RECOVERY operation encodes/decodes correctly with shuffled fields');
213
+
214
+ // Test BOOTSTRAP_DEPLOYMENT (complete) operation
215
+ const shuffledCompleteBootstrapBdo = shuffleObject(fixtures.validCompleteBootstrapDeployment.bdo);
216
+ const shuffledCompleteBootstrap = { ...fixtures.validCompleteBootstrapDeployment, bdo: shuffledCompleteBootstrapBdo };
217
+ encoded = applyOperations.Operation.encode(shuffledCompleteBootstrap);
218
+ decoded = applyOperations.Operation.decode(encoded);
219
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validCompleteBootstrapDeployment), 'Complete BOOTSTRAP_DEPLOYMENT operation encodes/decodes correctly with shuffled fields');
220
+
221
+ // Test BOOTSTRAP_DEPLOYMENT (partial) operation
222
+ const shuffledPartialBootstrapBdo = shuffleObject(fixtures.validPartialBootstrapDeployment.bdo);
223
+ const shuffledPartialBootstrap = { ...fixtures.validPartialBootstrapDeployment, bdo: shuffledPartialBootstrapBdo };
224
+ encoded = applyOperations.Operation.encode(shuffledPartialBootstrap);
225
+ decoded = applyOperations.Operation.decode(encoded);
226
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialBootstrapDeployment), 'Partial BOOTSTRAP_DEPLOYMENT operation encodes/decodes correctly with shuffled fields');
227
+
228
+ // Test BALANCE_INITIALIZATION operation
229
+ const shuffledBio = shuffleObject(fixtures.validBalanceInitOperation.bio);
230
+ const shuffledBalanceInit = { ...fixtures.validBalanceInitOperation, bio: shuffledBio };
231
+ encoded = applyOperations.Operation.encode(shuffledBalanceInit);
232
+ decoded = applyOperations.Operation.decode(encoded);
233
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validBalanceInitOperation), 'BALANCE_INITIALIZATION operation encodes/decodes correctly with shuffled fields');
234
+
235
+ // Test DISABLE_INITIALIZATION operation
236
+ const shuffledDisableCao = shuffleObject(fixtures.validDisableInitialization.cao);
237
+ const shuffledDisableInitialization = { ...fixtures.validDisableInitialization, cao: shuffledDisableCao };
238
+ encoded = applyOperations.Operation.encode(shuffledDisableInitialization);
239
+ decoded = applyOperations.Operation.decode(encoded);
240
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validDisableInitialization), 'DISABLE_INITIALIZATION operation encodes/decodes correctly with shuffled fields');
241
+ });
242
+
243
+ test('encodeV1networkOperation/decodeV1networkOperation roundtrip for network v1 payloads', t => {
244
+ const payloadsHashMap = new Map([
245
+ ['validatorConnectionRequest', networkV1Fixtures.payloadValidatorConnectionRequest],
246
+ ['validatorConnectionResponse', networkV1Fixtures.payloadValidatorConnectionResponse],
247
+ ['livenessRequest', networkV1Fixtures.payloadLivenessRequest],
248
+ ['livenessResponse', networkV1Fixtures.payloadLivenessResponse],
249
+ ['broadcastTransactionRequest', networkV1Fixtures.payloadBroadcastTransactionRequest],
250
+ ['broadcastTransactionResponse', networkV1Fixtures.payloadBroadcastTransactionResponse],
251
+ ]);
252
+
253
+ for (const [key, payload] of payloadsHashMap) {
254
+ const encoded = encodeV1networkOperation(payload);
255
+ const decoded = decodeV1networkOperation(encoded);
256
+ t.ok(b4a.isBuffer(encoded) && encoded.length > 0, `Payload ${key} encodes to a non-empty buffer`);
257
+ t.ok(JSON.stringify(decoded) === JSON.stringify(payload), `Payload ${key} decodes back correctly`);
258
+ }
259
+ });
260
+
261
+ test('safeEncodeApplyOperation returns an empty buffer on encode errors (and does not throw)', t => {
262
+ const originalLog = console.log;
263
+ console.log = () => {};
264
+ try {
265
+ const encoded = safeEncodeApplyOperation(fixtures.invalidPayloadWithMultipleOneOfKeys);
266
+ t.ok(b4a.isBuffer(encoded));
267
+ t.is(encoded.length, 0);
268
+ } finally {
269
+ console.log = originalLog;
270
+ }
271
+ });
272
+
273
+ test('safeEncodeApplyOperation encodes valid payloads to a non-empty buffer', t => {
274
+ const encoded = safeEncodeApplyOperation(fixtures.validTransactionOperation);
275
+ t.ok(b4a.isBuffer(encoded));
276
+ t.ok(encoded.length > 0);
277
+ });
278
+
279
+ test('safeDecodeApplyOperation returns null for invalid input (and does not throw)', t => {
280
+ t.is(safeDecodeApplyOperation(null), null);
281
+ t.is(safeDecodeApplyOperation({}), null);
282
+ t.is(safeDecodeApplyOperation('not-a-buffer'), null);
283
+
284
+ const originalLog = console.log;
285
+ console.log = () => {};
286
+ try {
287
+ t.is(safeDecodeApplyOperation(b4a.from([0x0F])), null);
288
+ } finally {
289
+ console.log = originalLog;
290
+ }
291
+ });
292
+
293
+ test('normalizeIncomingMessage decodes buffers and JSON buffers', t => {
294
+ const payload = fixtures.validTransactionOperation;
295
+ const encoded = applyOperations.Operation.encode(payload);
296
+
297
+ const decodedFromBuffer = normalizeIncomingMessage(encoded);
298
+ t.ok(JSON.stringify(decodedFromBuffer) === JSON.stringify(payload));
299
+
300
+ const decodedFromJsonBuffer = normalizeIncomingMessage({ type: 'Buffer', data: Array.from(encoded) });
301
+ t.ok(JSON.stringify(decodedFromJsonBuffer) === JSON.stringify(payload));
302
+
303
+ t.is(normalizeIncomingMessage(null), null);
304
+ t.is(normalizeIncomingMessage({ type: 'nope', data: [] }), null);
187
305
  });
@@ -0,0 +1,25 @@
1
+ import test from 'brittle';
2
+ import { isDefined } from '../../../../src/utils/type.js';
3
+
4
+ const assertIsDefined = (t, value, expected, message) => {
5
+ try {
6
+ t.is(isDefined(value), expected, message);
7
+ } catch (err) {
8
+ t.fail(`${message}. Threw: ${err.message}`);
9
+ }
10
+ };
11
+
12
+ test('isDefined returns false for nullish and NaN', t => {
13
+ assertIsDefined(t, null, false, 'null should be treated as not defined');
14
+ assertIsDefined(t, void 0, false, 'undefined should be treated as not defined');
15
+ assertIsDefined(t, NaN, false, 'NaN should be treated as not defined');
16
+ });
17
+
18
+ test('isDefined returns true for defined non-NaN values', t => {
19
+ assertIsDefined(t, 0, true, '0 should be treated as defined');
20
+ assertIsDefined(t, Infinity, true, 'Infinity should be treated as defined');
21
+ assertIsDefined(t, '', true, 'empty string should be treated as defined');
22
+ assertIsDefined(t, false, true, 'boolean false should be treated as defined');
23
+ assertIsDefined(t, {}, true, 'object should be treated as defined');
24
+ assertIsDefined(t, [], true, 'array should be treated as defined');
25
+ });
@@ -12,6 +12,7 @@ async function runTests() {
12
12
  await import('./fileUtils/readBalanceMigrationFile.test.js');
13
13
  await import('./migrationUtils/validateAddressFromIncomingFile.test.js');
14
14
  await import('./buffer/buffer.test.js')
15
+ await import('./type/type.test.js');
15
16
  await import('./amountSerialization/amountSerialization.test.js');
16
17
  test.resume();
17
18
  }
@@ -1,75 +0,0 @@
1
- # Dual-stack plan with design patterns (legacy JSON + network/v1)
2
-
3
- This is a design plan only (no implementation yet).
4
-
5
- ## Project map (current)
6
- - `src/core/network/Network.js`: facade for swarm, services, and message setup.
7
- - `src/core/network/protocols/NetworkMessages.js`: Protomux wiring for legacy JSON.
8
- - `src/core/network/protocols/legacy/`: legacy router + handlers + validators.
9
- - `src/core/network/protocols/shared/`: shared operation handlers + validators.
10
- - `src/core/network/services/ConnectionManager.js`: validator pool and messaging.
11
- - `src/core/network/services/MessageOrchestrator.js`: legacy broadcast heuristic.
12
- - `src/messages/network/v1/`: builder/director/factory for v1 messages.
13
- - `proto/network.proto` + `src/utils/protobuf/network.cjs`: v1 schema/codec.
14
-
15
- ## Design patterns to use (and where)
16
- - Strategy + Adapter: protocol-specific adapters per version (`legacy`, `v1`, future `v2`).
17
- - Registry + Factory: centralized list of protocol descriptors, created once.
18
- - Chain of Responsibility: routing table of predicates -> handlers (replace long if/else).
19
- - Template Method: already in `BaseOperationHandler`, keep as shared entry point.
20
- - Command: operation handlers as commands dispatched by a protocol router.
21
- - State Machine: handshake + capability negotiation for `network/v1`.
22
- - Mediator: `MessageOrchestrator` coordinates `ConnectionManager` and state.
23
- - Observer: emit connection/protocol events to interested services.
24
- - Decorator: wrap handlers with rate limit, logging, metrics.
25
- - Null Object: no-op messenger to avoid null checks in send paths.
26
-
27
- ## Target architecture (pattern-oriented)
28
- 1. ProtocolAdapter interface (Strategy + Adapter)
29
- - Shape: `id`, `protocolName`, `encoding`, `router`, `handshake`, `encode`, `decode`.
30
- - `LegacyProtocolAdapter` wraps `legacy/NetworkMessageRouter` with `c.json`.
31
- - `V1ProtocolAdapter` wraps protobuf decode/encode and a v1 router.
32
-
33
- 2. ProtocolRegistry (Registry + Factory)
34
- - Single source of enabled protocols: `[legacyAdapter, v1Adapter]`.
35
- - Owns protocol names (`legacy` channel from config, `network/v1`).
36
- - Exposed to `NetworkMessages` for channel creation.
37
-
38
- 3. ProtocolSession per connection (Facade + Null Object)
39
- - `session.messengers.{legacy,v1}` with a `NullMessenger` fallback.
40
- - `session.protocols` is a `Set` of negotiated protocols.
41
- - Stored in `ConnectionManager` alongside peer metadata.
42
-
43
- 4. Router refactor (Chain of Responsibility + Command)
44
- - `legacy` router: table of `canHandle(message)` + `handler.execute()`.
45
- - `v1` router: map `MessageType -> handler`.
46
- - Shared operation handlers remain in `shared/handlers/`.
47
-
48
- 5. Handshake state machine (State)
49
- - Per-connection state: `init -> awaiting_ack -> open -> failed`.
50
- - On success, mark `session.protocols.add('v1')`.
51
- - On timeout, keep `legacy` only (no disconnect).
52
-
53
- 6. Orchestrator protocol selection (Strategy)
54
- - `ProtocolSelector`: choose v1 if peer supports it, else legacy.
55
- - v1 ACK uses response codes; legacy uses state-based heuristic.
56
- - Optional strict verify for v1: wait for append before success.
57
-
58
- 7. Validators layout (Template Method + Adapter)
59
- - Keep shared validators in `shared/validators/`.
60
- - Protocol-specific validators live under `legacy/validators/` and `v1/validators/`.
61
- - Protocol adapter decides which validator set to use.
62
-
63
- ## Step-by-step plan
64
- 1. Add `ProtocolRegistry` and `ProtocolAdapter` definitions (design only).
65
- 2. Update `NetworkMessages` to iterate registry and open one channel per protocol.
66
- 3. Introduce `ProtocolSession` and store it in `ConnectionManager`.
67
- 4. Implement v1 router map based on `proto/network.proto` types.
68
- 5. Add v1 handshake state machine and capability negotiation.
69
- 6. Add `ProtocolSelector` in `MessageOrchestrator` with legacy fallback.
70
- 7. Align validators: shared core + protocol-specific wrappers.
71
- 8. Tests (future): legacy-only peers, v1 negotiation, mixed cluster.
72
-
73
- ## Notes for v2
74
- - Add `network/v2` adapter + protobuf schema.
75
- - Registry chooses the highest common protocol per peer.
@@ -1,155 +0,0 @@
1
- # Networking layer redesign (draft)
2
-
3
- Cel: uporządkować warstwę sieciową w `src/core/network`, wprowadzić binarne wiadomości (Protobuf + Protomux), system ACK/heartbeat, zunifikowane kody błędów oraz nowy endpoint RPC, bez ingerencji w istniejącą implementację (ten dokument jest tylko projektem).
4
-
5
- ## Założenia
6
- - Komunikacja p2p przez Hyperswarm zostaje, ale wiadomości idą binarnie (Protobuf) na kanałach Protomux.
7
- - Każdy typ wiadomości zwraca `Result` z kodem sukcesu/błędu (własne kody, nie HTTP).
8
- - Heartbeat/liveness co ~10s z losowym jitterem; łatwa konfiguracja z `config`/`constants`.
9
- - Handshake z zachowaniem PKI, ale mniej round-tripów i z ochroną DoS.
10
- - Modularna architektura (klasy/serwisy), separacja transportu, protokołu, zdrowia, bezpieczeństwa i RPC.
11
-
12
- ## Architektura warstw
13
- - **Transport (HyperswarmTransport)**: zarządza swarmem, tworzy kanały Protomux (binary encoding), egzekwuje limity połączeń.
14
- - **Protocol (EnvelopeCodec, Protobuf schemas)**: wspólny `Envelope` + `Result`; mapowanie typów wiadomości; validacja długości/pól przed dekodowaniem.
15
- - **Handshake (ValidatorHandshake)**: szybki trójfazowy handshake z wyzwaniem nonce + podpis, negocjacja wersji/capabilities, limity prób.
16
- - **Health (HeartbeatScheduler + LivenessStore)**: wysyła heartbeat do wszystkich znanych peerów (z konekcji lub z listy validatorów), aktualizuje liveness, generuje zdarzenia dla RPC.
17
- - **Security (DosGuard)**: rate limit per peer/endpoint, maks. rozmiary payloadów, kolejki handshake, banlista czasowa.
18
- - **RPC adapter (ValidatorStatusController)**: odczytuje `LivenessStore` i zwraca status walidatora.
19
-
20
- ## Proponowane moduły / pliki (kierunek, bez implementacji)
21
- - `src/core/network/transport/HyperswarmTransport.js`: opakowanie na Hyperswarm + Protomux, jednolity interfejs kanałów binarnych.
22
- - `src/core/network/protocol/EnvelopeCodec.js`: enkodowanie/dekodowanie Protobuf, walidacja schematu, mapowanie `MessageType` -> handler.
23
- - `src/core/network/protocol/error-codes.js`: stałe kodów (`ErrorCode` enum) współdzielone między node'ami.
24
- - `src/core/network/handshake/ValidatorHandshake.js`: stan maszyny handshake, ochrona DoS, fallbacki wersji.
25
- - `src/core/network/health/HeartbeatScheduler.js`: harmonogram heartbeatów + jitter, retry/backoff.
26
- - `src/core/network/health/LivenessStore.js`: przechowuje `lastSeen`, `missed`, `state=active/inactive/banned`.
27
- - `src/core/network/security/DosGuard.js`: token bucket + ograniczenia równoległych handshake, limit rozmiaru wiadomości.
28
- - `src/core/network/rpc/ValidatorStatusController.js`: integracja z istniejącym RPC routerem (`/validators/:address/status`).
29
-
30
- ## Szkic Protobuf (draft, do dodania jako nowy `.proto`)
31
- ```proto
32
- syntax = "proto3";
33
- package network.v1;
34
-
35
- enum MessageType {
36
- UNKNOWN = 0;
37
- HANDSHAKE_INIT = 1;
38
- HANDSHAKE_ACK = 2;
39
- HEARTBEAT_PING = 3;
40
- HEARTBEAT_ACK = 4;
41
- VALIDATOR_STATUS_REQUEST = 5;
42
- VALIDATOR_STATUS_RESPONSE = 6;
43
- // ...pozostałe typy operacyjne (transfer/role) mogą być zmapowane później
44
- }
45
-
46
- enum ResultCode {
47
- RESULT_OK = 0;
48
- RESULT_RETRY = 1; // używane do łagodnego backoffu
49
- RESULT_INVALID_PAYLOAD = 2;
50
- RESULT_UNAUTHORIZED = 3;
51
- RESULT_UNAUTHENTICATED = 4;
52
- RESULT_UNSUPPORTED_VERSION = 5;
53
- RESULT_RATE_LIMITED = 6;
54
- RESULT_TOO_MANY_CONNECTIONS = 7;
55
- RESULT_BUSY = 8;
56
- RESULT_TIMEOUT = 9;
57
- RESULT_PEER_BANNED = 10;
58
- RESULT_SIGNATURE_INVALID = 11;
59
- RESULT_NOT_VALIDATOR = 12;
60
- RESULT_INTERNAL = 13;
61
- }
62
-
63
- message Result {
64
- ResultCode code = 1;
65
- string reason = 2;
66
- }
67
-
68
- message Envelope {
69
- uint64 session_id = 1;
70
- MessageType type = 3;
71
- bytes payload = 4; // zakodowany message specyficzny dla type
72
- Result result = 5; // dla odpowiedzi/ACK
73
- uint64 timestamp = 6;
74
- }
75
-
76
- message HandshakeInit {
77
- bytes node_pk = 1;
78
- uint32 version = 2;
79
- bytes nonce = 3;
80
- bytes signature = 4; // sig(nonce || channel || version)
81
- repeated string capabilities = 5; // np. ["binary", "hb:v1"]
82
- }
83
-
84
- message HandshakeAck {
85
- bytes nonce = 1; // nonce od inicjatora
86
- bytes signature = 2; // sig(nonce || channel || version)
87
- uint32 version = 3; // negocjowany
88
- Result result = 4; // ERROR_UNSUPPORTED_VERSION itd.
89
- }
90
-
91
- message HeartbeatPing {
92
- bytes node_pk = 1;
93
- uint64 sequence = 2;
94
- uint64 timestamp_ms = 3;
95
- }
96
-
97
- message HeartbeatAck {
98
- uint64 sequence = 1;
99
- Result result = 2; // ERROR_OK lub powód degradacji
100
- uint64 timestamp_ms = 3;
101
- }
102
-
103
- message ValidatorStatusRequest {
104
- bytes validator_pk = 1;
105
- }
106
-
107
- message ValidatorStatusResponse {
108
- Result result = 1;
109
- bool active = 2;
110
- uint64 last_seen_ms = 3;
111
- uint32 missed_heartbeats = 4;
112
- }
113
- ```
114
-
115
- ## Handshake (przykładowy przebieg)
116
- 1. **Init**: inicjator wysyła `HandshakeInit` na dedykowany kanał Protomux `protocol:handshake/v1` (binary). Dodajemy `DosGuard` limitujący równoległe init-y i weryfikujący rozmiar.
117
- 2. **Ack**: odbiorca weryfikuje podpis i wersję, odpowiada `HandshakeAck` (sukces lub błąd). Przy sukcesie odkłada połączenie do `ConnectionManager` razem z metadanymi (rola, capabilities).
118
- 3. **Promotion**: dopiero po sukcesie handshake kanały operacyjne/heartbeat są otwierane; w przeciwnym razie połączenie jest zamykane i peer może trafić na banlistę czasową.
119
-
120
- Minimalizuje to round-trip (1.5 RTT), utrzymuje PKI (nonce + podpis) i pozwala na rollback do niższej wersji jeśli różne węzły.
121
-
122
- ## Heartbeat / liveness
123
- - **Interwał**: domyślnie 10s ± losowy jitter (np. 0–2s) by uniknąć szpilek. Parametryzacja w constants/config.
124
- - **Zakres**: każda znana para (aktywny validator + inne role) dostaje `HeartbeatPing`. Po 3 nieudanych ACK (lub braków w oknie czasowym) peer przechodzi w `inactive`, po większej liczbie w `unhealthy`.
125
- - **Integracja**: `HeartbeatScheduler` korzysta z `ConnectionManager` do nadawania; `LivenessStore` aktualizuje się na podstawie `HeartbeatAck` i czasu.
126
- - **Odpowiedź**: `HeartbeatAck.result.code` pozwala zakomunikować throttle (`ERROR_RATE_LIMITED`) lub przepełnienie (`ERROR_BUSY`), co zmniejsza logowanie błędów.
127
-
128
- ## Nowy endpoint RPC
129
- - Ścieżka: `GET /validators/:address/status` w warstwie RPC.
130
- - Źródło prawdy: `LivenessStore` (ostatni heartbeat, liczba nieodebranych, stan). `active=true` gdy ostatni heartbeat < 3*interval i brak poważnych błędów.
131
- - Payload (200): `{ address, active, lastSeenMs, missed, reason }`. Brak zależności od kodów HTTP dla logiki aplikacyjnej; HTTP 404 tylko gdy adres niepoprawny (opcjonalnie 200 z `active:false` i `result.code=ERROR_NOT_VALIDATOR`).
132
- - Przygotować kontrakt dla klientów (CLI/SDK) by interpretowali `Result`.
133
-
134
- ## Ochrona przed DoS
135
- - **Rate limiting**: token bucket per peer na handshake i heartbeat, limity równoległych handshake w `DosGuard`.
136
- - **Rozmiary**: twarde limity na rozmiar pakietu Protobuf przed deserializacją; odrzucenie z `ERROR_INVALID_PAYLOAD`.
137
- - **Kolejki**: ograniczona kolejka zadań handshake/heartbeat; przy przepełnieniu `ERROR_BUSY` w ACK i zamknięcie połączenia.
138
- - **Bany czasowe**: peer wysyłający zbyt wiele złych podpisów lub oversize payloadów trafia na banlistę na X minut.
139
- - **Separacja kanałów**: osobne kanały Protomux (handshake, control/heartbeat, operations) by odciąć hałas od głównych operacji.
140
-
141
- ## Kody błędów (wspólny enum)
142
- - Sukces: `ERROR_OK`.
143
- - Autoryzacja: `ERROR_UNAUTHENTICATED`, `ERROR_UNAUTHORIZED`, `ERROR_SIGNATURE_INVALID`, `ERROR_NOT_VALIDATOR`.
144
- - Dostępność: `ERROR_RATE_LIMITED`, `ERROR_BUSY`, `ERROR_TOO_MANY_CONNECTIONS`, `ERROR_PEER_BANNED`.
145
- - Protokół: `ERROR_INVALID_PAYLOAD`, `ERROR_UNSUPPORTED_VERSION`, `ERROR_TIMEOUT`.
146
- - Ogólne: `ERROR_RETRY` (miękkie), `ERROR_INTERNAL`.
147
-
148
- ## Plan wdrożenia (wysoki poziom)
149
- 1. Dodać schemat `.proto` z Envelope/Result/Handshake/Heartbeat/Status.
150
- 2. Wydzielić `transport/` + `protocol/` + `handshake/` + `health/` + `security/` katalogi i przenieść istniejące odpowiedniki (`NetworkMessages`, router, itp.) stopniowo.
151
- 3. Zaimplementować `EnvelopeCodec` w Protomux (binary), przełączyć routing na typy wiadomości zamiast JSON.
152
- 4. Zaimplementować handshake (1.5 RTT) i dołożyć `DosGuard` oraz limity na poziomie Protomux channel.
153
- 5. Dodać HeartbeatScheduler + LivenessStore, zasilić je danymi do RPC.
154
- 6. Dodać endpoint RPC `/validators/:address/status`, mapujący dane z LivenessStore.
155
- 7. Testy: symulacja DoS (próby handshake > limit, oversize payload), degradacja heartbeat, negocjacja wersji i kompatybilności.
@@ -1,64 +0,0 @@
1
-
2
- import Protomux from 'protomux';
3
- import b4a from 'b4a';
4
- import c from 'compact-encoding';
5
- import NetworkMessageRouter from './routes/NetworkMessageRouter.js';
6
- import Network from '../Network.js';
7
-
8
- class NetworkMessages {
9
- #messageRouter;
10
- #network;
11
- #config;
12
-
13
- /**
14
- * @param {Network} network
15
- * @param {object} config
16
- **/
17
- constructor(network, config) {
18
- this.#network = network;
19
- this.#config = config;
20
- }
21
-
22
- initializeMessageRouter(state, wallet) {
23
- this.#messageRouter = new NetworkMessageRouter(
24
- this.#network,
25
- state,
26
- wallet,
27
- this.#config
28
- );
29
- }
30
-
31
- async setupProtomuxMessages(connection) {
32
- const mux = Protomux.from(connection);
33
- connection.userData = mux;
34
- const message_channel = mux.createChannel({
35
- protocol: b4a.toString(this.#config.channel, 'utf8'),
36
- onopen() { },
37
- onclose() { }
38
- });
39
-
40
- message_channel.open();
41
-
42
- const messageProtomux = message_channel.addMessage({
43
- encoding: c.json,
44
- onmessage: async (incomingMessage) => {
45
- try {
46
- if (typeof incomingMessage === 'object' || typeof incomingMessage === 'string') {
47
- await this.#messageRouter.route(incomingMessage, connection, messageProtomux);
48
- } else {
49
- throw new Error('NetworkMessages: Received message is undefined');
50
- }
51
- } catch (error) {
52
- console.error(`NetworkMessages: Failed to handle incoming message: ${error.message}`);
53
- } finally {
54
- this.#network.swarm.leavePeer(connection.remotePublicKey);
55
- }
56
- }
57
- });
58
-
59
- connection.messenger = messageProtomux;
60
- return { message_channel, message: messageProtomux };
61
- }
62
- }
63
-
64
- export default NetworkMessages;
@@ -1,113 +0,0 @@
1
- import { NETWORK_MESSAGE_TYPES } from '../../../../utils/constants.js';
2
- import PeerWallet from 'trac-wallet';
3
- import b4a from 'b4a';
4
-
5
- class GetRequestHandler {
6
- #wallet;
7
- #state;
8
-
9
- constructor(wallet, state) {
10
- this.#wallet = wallet;
11
- this.#state = state;
12
- }
13
-
14
- get state() {
15
- return this.#state;
16
- }
17
-
18
- async handle(message, messageProtomux, connection, channelString) {
19
- switch (message) {
20
- case NETWORK_MESSAGE_TYPES.GET.VALIDATOR:
21
- await this.handleGetValidatorResponse(messageProtomux, connection, channelString);
22
- break;
23
- case NETWORK_MESSAGE_TYPES.GET.ADMIN:
24
- await this.handleGetAdminRequest(messageProtomux, connection, channelString);
25
- break;
26
- case NETWORK_MESSAGE_TYPES.GET.NODE:
27
- await this.handleCustomNodeRequest(messageProtomux, connection, channelString);
28
- break;
29
- default:
30
- throw new Error(`Unhandled GET type: ${message}`);
31
- }
32
- }
33
-
34
- async handleGetValidatorResponse(messageProtomux, connection, channelString) {
35
- const nonce = PeerWallet.generateNonce().toString('hex');
36
- const payload = {
37
- op: 'validatorResponse',
38
- wk: this.state.writingKey.toString('hex'),
39
- address: this.#wallet.address,
40
- nonce: nonce,
41
- channel: channelString,
42
- issuer: connection.remotePublicKey.toString('hex'),
43
- timestamp: Date.now(),
44
- };
45
-
46
-
47
- const hashInput = b4a.from(JSON.stringify(payload), 'utf8');
48
- const hash = await PeerWallet.blake3(hashInput);
49
- const sig = this.#wallet.sign(hash);
50
-
51
- const responseMessage = {
52
- ...payload,
53
- sig: sig.toString('hex'),
54
- };
55
- messageProtomux.send(responseMessage);
56
- }
57
-
58
- async handleGetAdminRequest(messageProtomux, connection, channelString) {
59
- const adminEntry = await this.state.getAdminEntry();
60
- if (adminEntry === null) {
61
- throw new Error("Admin entry is null. This is not possible to create admin stream.");
62
- }
63
-
64
- const adminPublicKey = PeerWallet.decodeBech32m(adminEntry.address);
65
-
66
- if (!b4a.equals(this.#wallet.publicKey, adminPublicKey)) {
67
- throw new Error("You are not an admin. This is not possible to create admin stream.");
68
- }
69
-
70
- const nonce = PeerWallet.generateNonce().toString('hex');
71
- const payload = {
72
- op: 'adminResponse',
73
- wk: this.state.writingKey.toString('hex'),
74
- address: this.#wallet.address,
75
- nonce: nonce,
76
- channel: channelString,
77
- issuer: connection.remotePublicKey.toString('hex'),
78
- timestamp: Date.now(),
79
- };
80
- const hash = await PeerWallet.blake3(JSON.stringify(payload));
81
- const sig = this.#wallet.sign(hash);
82
-
83
- const responseMessage = {
84
- ...payload,
85
- sig: sig.toString('hex'),
86
- };
87
- messageProtomux.send(responseMessage);
88
- }
89
-
90
- async handleCustomNodeRequest(messageProtomux, connection, channelString) {
91
- const nonce = PeerWallet.generateNonce().toString('hex');
92
- const payload = {
93
- op: 'nodeResponse',
94
- address: this.#wallet.address,
95
- nonce: nonce,
96
- channel: channelString,
97
- issuer: connection.remotePublicKey.toString('hex'),
98
- timestamp: Date.now(),
99
- };
100
-
101
- const hash = await PeerWallet.blake3(JSON.stringify(payload));
102
- const sig = this.#wallet.sign(hash);
103
-
104
- const responseMessage = {
105
- ...payload,
106
- sig: sig.toString('hex'),
107
- };
108
- messageProtomux.send(responseMessage);
109
-
110
- }
111
- }
112
-
113
- export default GetRequestHandler;