trac-msb 0.2.13 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/.github/workflows/acceptance-tests.yml +38 -0
  2. package/.github/workflows/lint-pr-title.yml +26 -0
  3. package/.github/workflows/publish.yml +33 -0
  4. package/.github/workflows/unit-tests.yml +34 -0
  5. package/package.json +7 -12
  6. package/proto/network.proto +74 -0
  7. package/rpc/rpc_services.js +4 -22
  8. package/scripts/generate-protobufs.js +12 -37
  9. package/src/config/config.js +9 -26
  10. package/src/config/env.js +17 -27
  11. package/src/core/network/Network.js +36 -73
  12. package/src/core/network/protocols/LegacyProtocol.js +11 -21
  13. package/src/core/network/protocols/NetworkMessages.js +17 -38
  14. package/src/core/network/protocols/ProtocolInterface.js +2 -14
  15. package/src/core/network/protocols/ProtocolSession.js +17 -144
  16. package/src/core/network/protocols/V1Protocol.js +18 -37
  17. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +19 -25
  18. package/src/core/network/protocols/legacy/handlers/{LegacyGetRequestHandler.js → GetRequestHandler.js} +6 -6
  19. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
  20. package/src/core/network/protocols/{legacy/handlers/LegacyRoleOperationHandler.js → shared/handlers/RoleOperationHandler.js} +11 -18
  21. package/src/core/network/protocols/{legacy/handlers/LegacySubnetworkOperationHandler.js → shared/handlers/SubnetworkOperationHandler.js} +17 -28
  22. package/src/core/network/protocols/{legacy/handlers/LegacyTransferOperationHandler.js → shared/handlers/TransferOperationHandler.js} +11 -17
  23. package/src/core/network/protocols/{legacy/handlers/BaseStateOperationHandler.js → shared/handlers/base/BaseOperationHandler.js} +12 -23
  24. package/src/core/network/protocols/shared/validators/{PartialBootstrapDeploymentValidator.js → PartialBootstrapDeployment.js} +4 -9
  25. package/src/core/network/protocols/shared/validators/{PartialRoleAccessValidator.js → PartialRoleAccess.js} +17 -51
  26. package/src/core/network/protocols/shared/validators/{PartialTransactionValidator.js → PartialTransaction.js} +7 -21
  27. package/src/core/network/protocols/shared/validators/{PartialTransferValidator.js → PartialTransfer.js} +9 -26
  28. package/src/core/network/protocols/shared/validators/{PartialOperationValidator.js → base/PartialOperation.js} +25 -47
  29. package/src/core/network/protocols/v1/NetworkMessageRouter.js +7 -91
  30. package/src/core/network/services/ConnectionManager.js +94 -146
  31. package/src/core/network/services/MessageOrchestrator.js +27 -151
  32. package/src/core/network/services/TransactionPoolService.js +18 -129
  33. package/src/core/network/services/TransactionRateLimiterService.js +34 -52
  34. package/src/core/network/services/ValidatorObserverService.js +26 -18
  35. package/src/core/state/State.js +19 -70
  36. package/src/index.js +8 -6
  37. package/src/messages/network/v1/NetworkMessageBuilder.js +79 -59
  38. package/src/messages/network/v1/NetworkMessageDirector.js +50 -16
  39. package/src/utils/Scheduler.js +8 -0
  40. package/src/utils/constants.js +5 -71
  41. package/src/utils/helpers.js +1 -10
  42. package/src/utils/normalizers.js +0 -38
  43. package/src/utils/protobuf/network.cjs +840 -0
  44. package/src/utils/protobuf/operationHelpers.js +3 -24
  45. package/tests/acceptance/v1/account/account.test.mjs +2 -8
  46. package/tests/acceptance/v1/tx/tx.test.mjs +1 -23
  47. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +6 -34
  48. package/tests/fixtures/assembleMessage.fixtures.js +8 -7
  49. package/tests/fixtures/networkV1.fixtures.js +28 -2
  50. package/tests/helpers/autobaseTestHelpers.js +5 -2
  51. package/tests/helpers/createTestSignature.js +3 -2
  52. package/tests/helpers/transactionPayloads.mjs +2 -2
  53. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +79 -239
  54. package/tests/unit/messages/network/NetworkMessageDirector.test.js +77 -223
  55. package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +5 -1
  56. package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +5 -1
  57. package/tests/unit/network/ConnectionManager.test.js +191 -0
  58. package/tests/unit/network/networkModule.test.js +1 -4
  59. package/tests/unit/unit.test.js +2 -2
  60. package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +2 -2
  61. package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +2 -2
  62. package/tests/unit/utils/protobuf/operationHelpers.test.js +4 -2
  63. package/tests/unit/utils/utils.test.js +0 -1
  64. package/proto/network/v1/enums/message_type.proto +0 -16
  65. package/proto/network/v1/enums/result_code.proto +0 -84
  66. package/proto/network/v1/messages/broadcast_transaction_request.proto +0 -9
  67. package/proto/network/v1/messages/broadcast_transaction_response.proto +0 -13
  68. package/proto/network/v1/messages/liveness_request.proto +0 -8
  69. package/proto/network/v1/messages/liveness_response.proto +0 -11
  70. package/proto/network/v1/network_message.proto +0 -22
  71. package/src/core/network/protocols/connectionPolicies.js +0 -88
  72. package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +0 -23
  73. package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +0 -27
  74. package/src/core/network/protocols/v1/V1ProtocolError.js +0 -91
  75. package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +0 -65
  76. package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +0 -389
  77. package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +0 -87
  78. package/src/core/network/protocols/v1/validators/V1BaseOperation.js +0 -211
  79. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +0 -26
  80. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +0 -276
  81. package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +0 -15
  82. package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +0 -17
  83. package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +0 -210
  84. package/src/core/network/services/PendingRequestService.js +0 -172
  85. package/src/core/network/services/TransactionCommitService.js +0 -149
  86. package/src/core/network/services/ValidatorHealthCheckService.js +0 -127
  87. package/src/utils/deepEqualApplyPayload.js +0 -40
  88. package/src/utils/logger.js +0 -25
  89. package/src/utils/protobuf/networkV1.generated.cjs +0 -2460
  90. package/tests/unit/network/LegacyNetworkMessageRouter.test.js +0 -54
  91. package/tests/unit/network/ProtocolSession.test.js +0 -127
  92. package/tests/unit/network/services/ConnectionManager.test.js +0 -450
  93. package/tests/unit/network/services/MessageOrchestrator.test.js +0 -445
  94. package/tests/unit/network/services/PendingRequestService.test.js +0 -431
  95. package/tests/unit/network/services/TransactionCommitService.test.js +0 -246
  96. package/tests/unit/network/services/TransactionPoolService.test.js +0 -489
  97. package/tests/unit/network/services/TransactionRateLimiterService.test.js +0 -139
  98. package/tests/unit/network/services/ValidatorHealthCheckService.test.js +0 -115
  99. package/tests/unit/network/services/services.test.js +0 -17
  100. package/tests/unit/network/utils/v1TestUtils.js +0 -153
  101. package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +0 -151
  102. package/tests/unit/network/v1/V1BaseOperation.test.js +0 -356
  103. package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +0 -129
  104. package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +0 -53
  105. package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +0 -512
  106. package/tests/unit/network/v1/V1LivenessRequest.test.js +0 -32
  107. package/tests/unit/network/v1/V1LivenessResponse.test.js +0 -45
  108. package/tests/unit/network/v1/V1ResultCode.test.js +0 -84
  109. package/tests/unit/network/v1/V1ValidationSchema.test.js +0 -13
  110. package/tests/unit/network/v1/connectionPolicies.test.js +0 -49
  111. package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +0 -284
  112. package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +0 -794
  113. package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +0 -193
  114. package/tests/unit/network/v1/v1.handlers.test.js +0 -15
  115. package/tests/unit/network/v1/v1.test.js +0 -19
  116. package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +0 -119
  117. package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +0 -136
  118. package/tests/unit/network/v1/v1ValidationSchema/common.test.js +0 -308
  119. package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +0 -90
  120. package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +0 -133
  121. package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +0 -102
@@ -1,445 +0,0 @@
1
- import { hook, test } from 'brittle';
2
- import sinon from 'sinon';
3
- import b4a from 'b4a';
4
-
5
- import { createConfig, ENV } from '../../../../src/config/env.js';
6
- import MessageOrchestrator from '../../../../src/core/network/services/MessageOrchestrator.js';
7
- import { OperationType, ResultCode } from '../../../../src/utils/constants.js';
8
- import NetworkWalletFactory from '../../../../src/core/network/identity/NetworkWalletFactory.js';
9
- import { testKeyPair1, testKeyPair2 } from '../../../fixtures/apply.fixtures.js';
10
- import { publicKeyToAddress } from '../../../../src/utils/helpers.js';
11
- import { ConnectionManagerError } from '../../../../src/core/network/services/ConnectionManager.js';
12
- import { V1TimeoutError } from '../../../../src/core/network/protocols/v1/V1ProtocolError.js';
13
-
14
- const VALIDATOR_KEY = testKeyPair2.publicKey;
15
-
16
- const createWallet = (config) => {
17
- const keyPair = {
18
- publicKey: b4a.from(testKeyPair1.publicKey, 'hex'),
19
- secretKey: b4a.from(testKeyPair1.secretKey, 'hex'),
20
- };
21
- return NetworkWalletFactory.provide({
22
- enableWallet: false,
23
- keyPair,
24
- networkPrefix: config.addressPrefix,
25
- });
26
- };
27
-
28
- const createTransferMessage = (config, wallet) => ({
29
- type: OperationType.TRANSFER,
30
- address: wallet.address,
31
- tro: {
32
- tx: 'aa'.repeat(32),
33
- txv: 'bb'.repeat(32),
34
- in: 'cc'.repeat(32),
35
- to: publicKeyToAddress(testKeyPair2.publicKey, config),
36
- am: '00'.repeat(16),
37
- is: 'dd'.repeat(64),
38
- },
39
- });
40
-
41
- const createConnectionManager = ({
42
- preferredProtocol = 'v1',
43
- sendSingleMessage = sinon.stub().resolves(ResultCode.OK),
44
- sentCount = 0,
45
- connectedValidators = [VALIDATOR_KEY],
46
- } = {}) => ({
47
- pickRandomConnectedValidator: sinon.stub().returns(VALIDATOR_KEY),
48
- pickRandomValidator: sinon.stub().callsFake((validators) => validators[0] ?? null),
49
- connectedValidators: sinon.stub().returns(connectedValidators),
50
- getConnection: sinon.stub().returns({
51
- protocolSession: {
52
- preferredProtocol,
53
- supportedProtocols: {
54
- LEGACY: 'legacy',
55
- V1: 'v1',
56
- }
57
- }
58
- }),
59
- sendSingleMessage,
60
- remove: sinon.stub(),
61
- incrementSentCount: sinon.stub(),
62
- getSentCount: sinon.stub().returns(sentCount),
63
- });
64
-
65
- hook('setup', () => {
66
- sinon.stub(console, 'log');
67
- sinon.stub(console, 'warn');
68
- });
69
-
70
- hook('teardown', () => {
71
- sinon.restore();
72
- });
73
-
74
- test('MessageOrchestrator.send returns false for unsupported protocol', async t => {
75
- const config = createConfig(ENV.DEVELOPMENT, {});
76
- const connectionManager = createConnectionManager({ preferredProtocol: 'unknown' });
77
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
78
- const wallet = createWallet(config);
79
- const message = createTransferMessage(config, wallet);
80
-
81
- orchestrator.setWallet(wallet);
82
- const result = await orchestrator.send(message, 0);
83
-
84
- t.is(result, false);
85
- t.is(connectionManager.sendSingleMessage.callCount, 0);
86
- });
87
-
88
- test('MessageOrchestrator.send V1 matrix: OK -> SUCCESS', async t => {
89
- const config = createConfig(ENV.DEVELOPMENT, {});
90
- const connectionManager = createConnectionManager({
91
- sendSingleMessage: sinon.stub().resolves(ResultCode.OK),
92
- sentCount: 0,
93
- });
94
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
95
- const wallet = createWallet(config);
96
- const message = createTransferMessage(config, wallet);
97
-
98
- orchestrator.setWallet(wallet);
99
- const result = await orchestrator.send(message);
100
-
101
- t.is(result, true);
102
- t.is(connectionManager.incrementSentCount.callCount, 1);
103
- t.is(connectionManager.remove.callCount, 0);
104
- });
105
-
106
- test('MessageOrchestrator.send V1 matrix: TIMEOUT -> ROTATE', async t => {
107
- const config = createConfig(ENV.DEVELOPMENT, {});
108
- const connectionManager = createConnectionManager({
109
- sendSingleMessage: sinon.stub().resolves(ResultCode.TIMEOUT),
110
- });
111
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
112
- const wallet = createWallet(config);
113
- const message = createTransferMessage(config, wallet);
114
-
115
- orchestrator.setWallet(wallet);
116
- const result = await orchestrator.send(message);
117
-
118
- t.is(result, false);
119
- t.is(connectionManager.sendSingleMessage.callCount, 1);
120
- t.is(connectionManager.remove.callCount, 1);
121
- t.is(connectionManager.incrementSentCount.callCount, 0);
122
- });
123
-
124
- test('MessageOrchestrator.send V1 matrix: TX_ALREADY_PENDING -> NO_ROTATE', async t => {
125
- const config = createConfig(ENV.DEVELOPMENT, {});
126
- const connectionManager = createConnectionManager({
127
- sendSingleMessage: sinon.stub().resolves(ResultCode.TX_ALREADY_PENDING),
128
- });
129
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
130
- const wallet = createWallet(config);
131
- const message = createTransferMessage(config, wallet);
132
-
133
- orchestrator.setWallet(wallet);
134
- const result = await orchestrator.send(message);
135
-
136
- t.is(result, false);
137
- t.is(connectionManager.sendSingleMessage.callCount, 1);
138
- t.is(connectionManager.remove.callCount, 0);
139
- t.is(connectionManager.incrementSentCount.callCount, 0);
140
- });
141
-
142
- test('MessageOrchestrator.send V1 matrix: unknown code -> UNDEFINED', async t => {
143
- const config = createConfig(ENV.DEVELOPMENT, {});
144
- const connectionManager = createConnectionManager({
145
- sendSingleMessage: sinon.stub().resolves(99999),
146
- });
147
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
148
- const wallet = createWallet(config);
149
- const message = createTransferMessage(config, wallet);
150
-
151
- orchestrator.setWallet(wallet);
152
- const result = await orchestrator.send(message);
153
-
154
- t.is(result, false);
155
- t.is(connectionManager.sendSingleMessage.callCount, 1);
156
- t.is(connectionManager.remove.callCount, 1);
157
- });
158
-
159
- test('MessageOrchestrator.send removes validator when threshold reached on success', async t => {
160
- const config = createConfig(ENV.DEVELOPMENT, {});
161
- const connectionManager = createConnectionManager({
162
- sendSingleMessage: sinon.stub().resolves(ResultCode.OK),
163
- sentCount: config.messageThreshold,
164
- });
165
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
166
- const wallet = createWallet(config);
167
- const message = createTransferMessage(config, wallet);
168
-
169
- orchestrator.setWallet(wallet);
170
- const result = await orchestrator.send(message);
171
-
172
- t.is(result, true);
173
- t.is(connectionManager.incrementSentCount.callCount, 1);
174
- t.is(connectionManager.remove.callCount, 1);
175
- });
176
-
177
- test('MessageOrchestrator.send retries on ConnectionManagerError without removing validator', async t => {
178
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 2 });
179
- const sendSingleMessage = sinon.stub();
180
- sendSingleMessage.onFirstCall().rejects(new ConnectionManagerError('disconnected'));
181
- sendSingleMessage.onSecondCall().resolves(ResultCode.OK);
182
-
183
- const connectionManager = createConnectionManager({ sendSingleMessage, sentCount: 0 });
184
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
185
- const wallet = createWallet(config);
186
- const message = createTransferMessage(config, wallet);
187
-
188
- orchestrator.setWallet(wallet);
189
- const result = await orchestrator.send(message);
190
-
191
- t.is(result, true);
192
- t.is(sendSingleMessage.callCount, 2);
193
- t.is(connectionManager.remove.callCount, 0);
194
- t.is(connectionManager.incrementSentCount.callCount, 1);
195
- });
196
-
197
- test('MessageOrchestrator.send retries on generic catch error with remove + retry', async t => {
198
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 2 });
199
- const sendSingleMessage = sinon.stub();
200
- sendSingleMessage.onFirstCall().rejects(new Error('response validation failed'));
201
- sendSingleMessage.onSecondCall().resolves(ResultCode.OK);
202
-
203
- const connectionManager = createConnectionManager({ sendSingleMessage, sentCount: 0 });
204
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
205
- const wallet = createWallet(config);
206
- const message = createTransferMessage(config, wallet);
207
-
208
- orchestrator.setWallet(wallet);
209
- const result = await orchestrator.send(message);
210
-
211
- t.is(result, true);
212
- t.is(sendSingleMessage.callCount, 2);
213
- t.is(connectionManager.remove.callCount, 1);
214
- t.is(connectionManager.incrementSentCount.callCount, 1);
215
- });
216
-
217
- test('MessageOrchestrator.send max retries guard returns false immediately', async t => {
218
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 1 });
219
- const connectionManager = createConnectionManager({
220
- sendSingleMessage: sinon.stub().resolves(ResultCode.OK),
221
- });
222
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
223
- const wallet = createWallet(config);
224
- const message = createTransferMessage(config, wallet);
225
-
226
- orchestrator.setWallet(wallet);
227
- const result = await orchestrator.send(message, 2);
228
-
229
- t.is(result, false);
230
- t.is(connectionManager.pickRandomConnectedValidator.callCount, 0);
231
- t.is(connectionManager.sendSingleMessage.callCount, 0);
232
- });
233
-
234
- test('MessageOrchestrator.send timeout split: pending timeout rejection goes through catch and retries', async t => {
235
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 2 });
236
- const sendSingleMessage = sinon.stub();
237
- sendSingleMessage.onFirstCall().rejects(new V1TimeoutError('pending request timeout', false));
238
- sendSingleMessage.onSecondCall().resolves(ResultCode.OK);
239
-
240
- const connectionManager = createConnectionManager({ sendSingleMessage });
241
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
242
- const wallet = createWallet(config);
243
- const message = createTransferMessage(config, wallet);
244
-
245
- orchestrator.setWallet(wallet);
246
- const result = await orchestrator.send(message);
247
-
248
- t.is(result, true);
249
- t.is(sendSingleMessage.callCount, 2);
250
- t.is(connectionManager.remove.callCount, 1);
251
- });
252
-
253
- test('MessageOrchestrator.send timeout split: TIMEOUT result code stays in then path and does not retry', async t => {
254
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 2 });
255
- const sendSingleMessage = sinon.stub().resolves(ResultCode.TIMEOUT);
256
- const connectionManager = createConnectionManager({ sendSingleMessage });
257
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
258
- const wallet = createWallet(config);
259
- const message = createTransferMessage(config, wallet);
260
-
261
- orchestrator.setWallet(wallet);
262
- const result = await orchestrator.send(message);
263
-
264
- t.is(result, false);
265
- t.is(sendSingleMessage.callCount, 1);
266
- t.is(connectionManager.remove.callCount, 1);
267
- });
268
-
269
- test('MessageOrchestrator.send validation split: thrown validation error goes through catch', async t => {
270
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 2 });
271
- const sendSingleMessage = sinon.stub();
272
- sendSingleMessage.onFirstCall().rejects(new Error('validator response validation failed'));
273
- sendSingleMessage.onSecondCall().resolves(ResultCode.OK);
274
-
275
- const connectionManager = createConnectionManager({ sendSingleMessage });
276
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
277
- const wallet = createWallet(config);
278
- const message = createTransferMessage(config, wallet);
279
-
280
- orchestrator.setWallet(wallet);
281
- const result = await orchestrator.send(message);
282
-
283
- t.is(result, true);
284
- t.is(sendSingleMessage.callCount, 2);
285
- t.is(connectionManager.remove.callCount, 1);
286
- });
287
-
288
- test('MessageOrchestrator.send validation split: non-OK result code stays in then and uses policy', async t => {
289
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 2 });
290
- const sendSingleMessage = sinon.stub().resolves(ResultCode.SCHEMA_VALIDATION_FAILED);
291
- const connectionManager = createConnectionManager({ sendSingleMessage });
292
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
293
- const wallet = createWallet(config);
294
- const message = createTransferMessage(config, wallet);
295
-
296
- orchestrator.setWallet(wallet);
297
- const result = await orchestrator.send(message);
298
-
299
- t.is(result, false);
300
- t.is(sendSingleMessage.callCount, 1);
301
- t.is(connectionManager.remove.callCount, 1);
302
- });
303
-
304
- test('MessageOrchestrator.send legacy path succeeds and increments sent count', async t => {
305
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 0 });
306
- const sendSingleMessage = sinon.stub().resolves(true);
307
- const connectionManager = createConnectionManager({
308
- preferredProtocol: 'legacy',
309
- sendSingleMessage,
310
- sentCount: 0,
311
- });
312
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
313
- const wallet = createWallet(config);
314
- const message = createTransferMessage(config, wallet);
315
- sinon.stub(orchestrator, 'waitForUnsignedState').resolves(true);
316
-
317
- orchestrator.setWallet(wallet);
318
- const result = await orchestrator.send(message);
319
-
320
- t.is(result, true);
321
- t.is(sendSingleMessage.callCount, 1);
322
- t.is(connectionManager.incrementSentCount.callCount, 1);
323
- t.is(connectionManager.remove.callCount, 0);
324
- });
325
-
326
- test('MessageOrchestrator.send legacy path false result removes validator and retries', async t => {
327
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 1 });
328
- const sendSingleMessage = sinon.stub().resolves(true);
329
- const connectionManager = createConnectionManager({
330
- preferredProtocol: 'legacy',
331
- sendSingleMessage,
332
- });
333
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
334
- const wallet = createWallet(config);
335
- const message = createTransferMessage(config, wallet);
336
- const waitStub = sinon.stub(orchestrator, 'waitForUnsignedState');
337
- waitStub.onFirstCall().resolves(false);
338
- waitStub.onSecondCall().resolves(true);
339
-
340
- orchestrator.setWallet(wallet);
341
- const result = await orchestrator.send(message);
342
-
343
- t.is(result, true);
344
- t.is(sendSingleMessage.callCount, 2);
345
- t.is(connectionManager.remove.callCount, 1);
346
- });
347
-
348
- test('MessageOrchestrator.send legacy path catches send error and retries', async t => {
349
- const config = createConfig(ENV.DEVELOPMENT, { maxRetries: 1 });
350
- const sendSingleMessage = sinon.stub();
351
- sendSingleMessage.onFirstCall().rejects(new Error('legacy send failed'));
352
- sendSingleMessage.onSecondCall().resolves(true);
353
-
354
- const connectionManager = createConnectionManager({
355
- preferredProtocol: 'legacy',
356
- sendSingleMessage,
357
- });
358
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
359
- const wallet = createWallet(config);
360
- const message = createTransferMessage(config, wallet);
361
- sinon.stub(orchestrator, 'waitForUnsignedState').resolves(true);
362
-
363
- orchestrator.setWallet(wallet);
364
- const result = await orchestrator.send(message);
365
-
366
- t.is(result, true);
367
- t.is(sendSingleMessage.callCount, 2);
368
- t.is(connectionManager.remove.callCount, 0);
369
- });
370
-
371
- test('MessageOrchestrator.waitForUnsignedState returns true when state entry appears', async t => {
372
- const clock = sinon.useFakeTimers({ now: 1 });
373
- try {
374
- const config = createConfig(ENV.DEVELOPMENT, {});
375
- const state = {
376
- get: sinon.stub()
377
- .onFirstCall().resolves(null)
378
- .onSecondCall().resolves({ tx: 'found' }),
379
- };
380
- const orchestrator = new MessageOrchestrator(createConnectionManager(), state, config);
381
-
382
- const pending = orchestrator.waitForUnsignedState('tx-hash', 500);
383
- await clock.tickAsync(450);
384
- const result = await pending;
385
-
386
- t.is(result, true);
387
- t.ok(state.get.callCount >= 2);
388
- } finally {
389
- clock.restore();
390
- }
391
- });
392
-
393
- test('MessageOrchestrator.waitForUnsignedState returns false on timeout', async t => {
394
- const clock = sinon.useFakeTimers({ now: 1 });
395
- try {
396
- const config = createConfig(ENV.DEVELOPMENT, {});
397
- const state = { get: sinon.stub().resolves(null) };
398
- const orchestrator = new MessageOrchestrator(createConnectionManager(), state, config);
399
-
400
- const pending = orchestrator.waitForUnsignedState('tx-hash', 400);
401
- await clock.tickAsync(1000);
402
- const result = await pending;
403
-
404
- t.is(result, false);
405
- t.ok(state.get.callCount >= 1);
406
- } finally {
407
- clock.restore();
408
- }
409
- });
410
-
411
- test('MessageOrchestrator.send V1 avoids selecting validator with requester address when possible', async t => {
412
- const config = createConfig(ENV.DEVELOPMENT, {});
413
- const requesterValidatorKey = testKeyPair1.publicKey;
414
- const otherValidatorKey = testKeyPair2.publicKey;
415
- const sendSingleMessage = sinon.stub().resolves(ResultCode.OK);
416
-
417
- const connectionManager = createConnectionManager({
418
- sendSingleMessage,
419
- connectedValidators: [requesterValidatorKey, otherValidatorKey],
420
- });
421
- connectionManager.getConnection = sinon.stub().returns({
422
- protocolSession: {
423
- preferredProtocol: 'v1',
424
- supportedProtocols: {
425
- LEGACY: 'legacy',
426
- V1: 'v1',
427
- }
428
- }
429
- });
430
-
431
- const orchestrator = new MessageOrchestrator(connectionManager, { get: async () => null }, config);
432
- const requesterAddress = publicKeyToAddress(requesterValidatorKey, config);
433
- const wallet = createWallet(config);
434
- const message = {
435
- ...createTransferMessage(config, wallet),
436
- address: requesterAddress,
437
- };
438
-
439
- orchestrator.setWallet(wallet);
440
- const result = await orchestrator.send(message);
441
-
442
- t.is(result, true);
443
- t.is(sendSingleMessage.callCount, 1);
444
- t.is(sendSingleMessage.firstCall.args[1], otherValidatorKey);
445
- });