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/index.js CHANGED
@@ -44,6 +44,7 @@ import {
44
44
  getLicenseCountCommand
45
45
  } from "./utils/cliCommands.js";
46
46
  import {safeEncodeApplyOperation} from "./utils/protobuf/operationHelpers.js";
47
+ import {Config} from "./config/config.js";
47
48
 
48
49
  export class MainSettlementBus extends ReadyResource {
49
50
  #store;
@@ -55,7 +56,7 @@ export class MainSettlementBus extends ReadyResource {
55
56
  #config
56
57
 
57
58
  /**
58
- * @param {object} config
59
+ * @param {Config} config
59
60
  **/
60
61
  constructor(config) {
61
62
  super();
@@ -96,6 +97,8 @@ export class MainSettlementBus extends ReadyResource {
96
97
  }
97
98
 
98
99
  async _open() {
100
+ await fileUtils.ensureCoresStoreDir(this.#config);
101
+
99
102
  if (this.#config.enableWallet) {
100
103
  await this.#wallet.initKeyPair(
101
104
  this.#config.keyPairPath,
@@ -301,7 +304,7 @@ export class MainSettlementBus extends ReadyResource {
301
304
  const success = await this.broadcastPartialTransaction(adminRecoveryMessage);
302
305
 
303
306
  if (!success) {
304
- throw new Error("Failed to broadcast transaction after multiple attempts.");
307
+ throw new Error("Failed to broadcast transaction. Try again later.");
305
308
  }
306
309
 
307
310
  console.info(`Transaction hash: ${adminRecoveryMessage.rao.tx}`);
@@ -422,7 +425,7 @@ export class MainSettlementBus extends ReadyResource {
422
425
  const success = await this.broadcastPartialTransaction(assembledMessage);
423
426
 
424
427
  if (!success) {
425
- throw new Error("Failed to broadcast transaction after multiple attempts.");
428
+ throw new Error("Failed to broadcast transaction. Try again later.");
426
429
  }
427
430
 
428
431
  console.info(`Transaction hash: ${assembledMessage.rao.tx}`);
@@ -456,7 +459,7 @@ export class MainSettlementBus extends ReadyResource {
456
459
  const success = await this.broadcastPartialTransaction(assembledMessage);
457
460
 
458
461
  if (!success) {
459
- throw new Error("Failed to broadcast transaction after multiple attempts.");
462
+ throw new Error("Failed to broadcast transaction. Try again later.");
460
463
  }
461
464
 
462
465
  console.info(`Transaction hash: ${assembledMessage.rao.tx}`);
@@ -680,7 +683,7 @@ export class MainSettlementBus extends ReadyResource {
680
683
  const success = await this.broadcastPartialTransaction(payload);
681
684
 
682
685
  if (!success) {
683
- throw new Error("Failed to broadcast transaction after multiple attempts.");
686
+ throw new Error("Failed to broadcast transaction. Try again later.");
684
687
  }
685
688
 
686
689
  console.info(`Transaction hash: ${payload.bdo.tx}`);
@@ -8,7 +8,7 @@ import {encodeCapabilities} from "../../../utils/buffer.js";
8
8
  /**
9
9
  * Builder for v1 internal network protocol messages.
10
10
  * @param {PeerWallet} wallet
11
- * @param {object} config
11
+ * @param {Config} config
12
12
  */
13
13
  class NetworkMessageBuilder {
14
14
  #wallet;
@@ -19,6 +19,8 @@ class NetworkMessageBuilder {
19
19
  #issuerAddress;
20
20
  #resultCode;
21
21
  #data;
22
+ #proof;
23
+ #timestamp_ledger;
22
24
  #header;
23
25
  #payloadKey;
24
26
  #body;
@@ -26,7 +28,7 @@ class NetworkMessageBuilder {
26
28
 
27
29
  /**
28
30
  * @param {PeerWallet} wallet
29
- * @param {object} config
31
+ * @param {Config} config
30
32
  */
31
33
  constructor(wallet, config) {
32
34
  this.#config = config;
@@ -85,6 +87,9 @@ class NetworkMessageBuilder {
85
87
  }
86
88
 
87
89
  setData(data) {
90
+ // case when response have to be empty.
91
+ if (data === undefined || data === null) data = b4a.alloc(0);
92
+
88
93
  if (!b4a.isBuffer(data)) {
89
94
  throw new Error(`Data must be a buffer.`);
90
95
  }
@@ -92,6 +97,30 @@ class NetworkMessageBuilder {
92
97
  return this;
93
98
  }
94
99
 
100
+ setProof(proof) {
101
+ if (proof === undefined || proof === null) proof = b4a.alloc(0);
102
+ if (!b4a.isBuffer(proof)) {
103
+ throw new Error(`Proof must be a buffer.`);
104
+ }
105
+ this.#proof = proof;
106
+ return this;
107
+ }
108
+
109
+ setTimestampLedger(timestamp) {
110
+ if (timestamp === undefined || timestamp === null) {
111
+ this.#timestamp_ledger = null;
112
+ return this;
113
+ }
114
+
115
+ const value = timestamp instanceof Date ? timestamp.getTime() : timestamp;
116
+ if (!Number.isSafeInteger(value) || value < 0) {
117
+ throw new Error('timestamp must be a non-negative safe integer or Date.');
118
+ }
119
+
120
+ this.#timestamp_ledger = value;
121
+ return this;
122
+ }
123
+
95
124
  #setHeader() {
96
125
  if (!this.#type) throw new Error('Header requires type to be set');
97
126
  if (!this.#id) throw new Error('Header requires id to be set');
@@ -107,76 +136,6 @@ class NetworkMessageBuilder {
107
136
  return this;
108
137
  }
109
138
 
110
- async #buildValidatorConnectionRequestPayload() {
111
- const issuer = this.#issuerAddress
112
- if (!isAddressValid(issuer, this.#config.addressPrefix)) {
113
- throw new Error('Issuer address must be a valid TRAC address');
114
- }
115
-
116
- if (this.#issuerAddress !== this.#wallet.address) {
117
- throw new Error('Issuer address must be the signer address');
118
- }
119
-
120
- const nonce = PeerWallet.generateNonce();
121
- const tsBuf = timestampToBuffer(this.#timestamp);
122
- const idBuf = idToBuffer(this.#id);
123
- const message = createMessage(
124
- this.#type,
125
- idBuf,
126
- tsBuf,
127
- addressToBuffer(issuer, this.#config.addressPrefix),
128
- nonce,
129
- encodeCapabilities(this.#capabilities),
130
- );
131
- const hash = await PeerWallet.blake3(message);
132
- const signature = this.#wallet.sign(hash);
133
-
134
- this.#payloadKey = 'validator_connection_request';
135
- this.#body = {
136
- issuer_address: issuer,
137
- nonce,
138
- signature
139
- };
140
- }
141
-
142
- async #buildValidatorConnectionResponsePayload() {
143
- const issuer = this.#issuerAddress
144
- if (!isAddressValid(issuer, this.#config.addressPrefix)) {
145
- throw new Error('Issuer address must be a valid TRAC address');
146
- }
147
-
148
- if (this.#issuerAddress === this.#wallet.address) {
149
- throw new Error('Issuer address must be the different than the signer address');
150
- }
151
-
152
- if (this.#resultCode === null || this.#resultCode === undefined) {
153
- throw new Error('Result code must be set before building validator connection response');
154
- }
155
-
156
- const nonce = PeerWallet.generateNonce();
157
- const tsBuf = timestampToBuffer(this.#timestamp);
158
- const idBuf = idToBuffer(this.#id);
159
- const message = createMessage(
160
- this.#type,
161
- idBuf,
162
- tsBuf,
163
- addressToBuffer(issuer, this.#config.addressPrefix),
164
- nonce,
165
- safeWriteUInt32BE(this.#resultCode, 0),
166
- encodeCapabilities(this.#capabilities),
167
- );
168
- const hash = await PeerWallet.blake3(message);
169
- const signature = this.#wallet.sign(hash);
170
-
171
- this.#payloadKey = 'validator_connection_response';
172
- this.#body = {
173
- issuer_address: issuer,
174
- nonce,
175
- signature,
176
- result: this.#resultCode
177
- };
178
- }
179
-
180
139
  async #buildLivenessRequestPayload() {
181
140
  const nonce = PeerWallet.generateNonce();
182
141
  const tsBuf = timestampToBuffer(this.#timestamp);
@@ -258,21 +217,50 @@ class NetworkMessageBuilder {
258
217
  const nonce = PeerWallet.generateNonce();
259
218
  const tsBuf = timestampToBuffer(this.#timestamp);
260
219
  const idBuf = idToBuffer(this.#id);
220
+ const proof = b4a.isBuffer(this.#proof) ? this.#proof : b4a.alloc(0);
221
+ const hasProof = proof.length > 0;
222
+ const timestamp = Number.isSafeInteger(this.#timestamp_ledger) ? this.#timestamp_ledger : 0;
223
+ const hasTimestamp = timestamp > 0;
224
+
225
+ if (this.#resultCode === ResultCode.OK) {
226
+ if (!hasProof || !hasTimestamp) {
227
+ throw new Error('Result code OK requires non-empty proof and timestamp > 0.');
228
+ }
229
+ } else if (this.#resultCode === ResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE) {
230
+ if (hasProof) {
231
+ throw new Error('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires empty proof.');
232
+ }
233
+ if (!hasTimestamp) {
234
+ throw new Error('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires timestamp > 0.');
235
+ }
236
+ } else {
237
+ if (hasProof) {
238
+ throw new Error('Non-OK result code requires empty proof.');
239
+ }
240
+ if (timestamp !== 0) {
241
+ throw new Error('Non-OK result code requires timestamp to be 0, except TX_ACCEPTED_PROOF_UNAVAILABLE.');
242
+ }
243
+ }
244
+
261
245
  const message = createMessage(
262
246
  this.#type,
263
247
  idBuf,
264
248
  tsBuf,
265
249
  nonce,
250
+ proof,
251
+ timestampToBuffer(timestamp),
266
252
  safeWriteUInt32BE(this.#resultCode, 0),
267
253
  encodeCapabilities(this.#capabilities),
268
254
  );
255
+
269
256
  const hash = await PeerWallet.blake3(message);
270
257
  const signature = this.#wallet.sign(hash);
271
-
272
258
  this.#payloadKey = 'broadcast_transaction_response';
273
259
  this.#body = {
274
260
  nonce,
275
261
  signature,
262
+ proof,
263
+ timestamp: timestamp,
276
264
  result: this.#resultCode
277
265
  };
278
266
  }
@@ -281,14 +269,6 @@ class NetworkMessageBuilder {
281
269
  this.#setHeader();
282
270
 
283
271
  switch (this.#type) {
284
- case NetworkOperationType.VALIDATOR_CONNECTION_REQUEST: {
285
- await this.#buildValidatorConnectionRequestPayload();
286
- break;
287
- }
288
- case NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE: {
289
- await this.#buildValidatorConnectionResponsePayload();
290
- break;
291
- }
292
272
  case NetworkOperationType.LIVENESS_REQUEST: {
293
273
  await this.#buildLivenessRequestPayload();
294
274
  break;
@@ -1,4 +1,4 @@
1
- import { NetworkOperationType } from '../../../utils/constants.js';
1
+ import {NetworkOperationType} from '../../../utils/constants.js';
2
2
 
3
3
  /**
4
4
  * Director for v1 internal network protocol messages.
@@ -13,47 +13,6 @@ class NetworkMessageDirector {
13
13
  this.#builder = builderInstance;
14
14
  }
15
15
 
16
- /**
17
- * Build a validator connection request message.
18
- * @param {string} id
19
- * @param {string} issuerAddress
20
- * @param {string[]} capabilities
21
- * @returns {Promise<object>}
22
- */
23
- async buildValidatorConnectionRequest(id, issuerAddress, capabilities) {
24
- await this.#builder
25
- .setType(NetworkOperationType.VALIDATOR_CONNECTION_REQUEST)
26
- .setId(id)
27
- .setTimestamp()
28
- .setIssuerAddress(issuerAddress)
29
- .setCapabilities(capabilities)
30
- .buildPayload()
31
-
32
-
33
- return this.#builder.getResult();
34
- }
35
-
36
- /**
37
- * Build a validator connection response message.
38
- * @param {string} id
39
- * @param {string} issuerAddress
40
- * @param {string[]} capabilities
41
- * @param {number} statusCode
42
- * @returns {Promise<object>}
43
- */
44
- async buildValidatorConnectionResponse(id, issuerAddress, capabilities, statusCode) {
45
- await this.#builder
46
- .setType(NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE)
47
- .setId(id)
48
- .setTimestamp()
49
- .setIssuerAddress(issuerAddress)
50
- .setCapabilities(capabilities)
51
- .setResultCode(statusCode)
52
- .buildPayload()
53
-
54
- return this.#builder.getResult();
55
- }
56
-
57
16
  /**
58
17
  * Build a liveness request message.
59
18
  * @param {string} id
@@ -61,12 +20,11 @@ class NetworkMessageDirector {
61
20
  * @param {string[]} capabilities
62
21
  * @returns {Promise<object>}
63
22
  */
64
- async buildLivenessRequest(id, data, capabilities) {
23
+ async buildLivenessRequest(id, capabilities) {
65
24
  await this.#builder
66
25
  .setType(NetworkOperationType.LIVENESS_REQUEST)
67
26
  .setId(id)
68
27
  .setTimestamp()
69
- .setData(data)
70
28
  .setCapabilities(capabilities)
71
29
  .buildPayload();
72
30
 
@@ -76,17 +34,15 @@ class NetworkMessageDirector {
76
34
  /**
77
35
  * Build a liveness response message.
78
36
  * @param {string} id
79
- * @param {Buffer} data
80
37
  * @param {string[]} capabilities
81
38
  * @param {number} statusCode
82
39
  * @returns {Promise<object>}
83
40
  */
84
- async buildLivenessResponse(id, data, capabilities, statusCode) {
41
+ async buildLivenessResponse(id, capabilities, statusCode) {
85
42
  await this.#builder
86
43
  .setType(NetworkOperationType.LIVENESS_RESPONSE)
87
44
  .setId(id)
88
45
  .setTimestamp()
89
- .setData(data)
90
46
  .setCapabilities(capabilities)
91
47
  .setResultCode(statusCode)
92
48
  .buildPayload();
@@ -115,18 +71,28 @@ class NetworkMessageDirector {
115
71
 
116
72
  /**
117
73
  * Build a broadcast transaction response message.
74
+ *
75
+ * Allowed payload variants:
76
+ * 1) resultCode === OK - proof must be non-empty and timestamp must be > 0.
77
+ * 2) resultCode === TX_ACCEPTED_PROOF_UNAVAILABLE - proof must be empty and timestamp must be > 0.
78
+ * 3) resultCode !== OK and resultCode !== TX_ACCEPTED_PROOF_UNAVAILABLE - proof must be empty and timestamp must be 0.
79
+ *
118
80
  * @param {string} id
119
81
  * @param {string[]} capabilities
120
- * @param {number} statusCode
82
+ * @param {number} resultCode
83
+ * @param {Buffer|null|undefined} proof
84
+ * @param {number|Date|null|undefined} timestamp - When transaction has been appended by the validator
121
85
  * @returns {Promise<object>}
122
86
  */
123
- async buildBroadcastTransactionResponse(id, capabilities, statusCode) {
87
+ async buildBroadcastTransactionResponse(id, capabilities, resultCode, proof = null, timestamp = null) {
124
88
  await this.#builder
125
89
  .setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
126
90
  .setId(id)
127
91
  .setTimestamp()
128
92
  .setCapabilities(capabilities)
129
- .setResultCode(statusCode)
93
+ .setProof(proof)
94
+ .setTimestampLedger(timestamp)
95
+ .setResultCode(resultCode)
130
96
  .buildPayload();
131
97
 
132
98
  return this.#builder.getResult();
@@ -23,7 +23,7 @@ import { isHexString } from '../../utils/helpers.js';
23
23
  /**
24
24
  * Builder for partial/complete ApplyState messages.
25
25
  * @param {PeerWallet} wallet
26
- * @param {object} config
26
+ * @param {Config} config
27
27
  */
28
28
  class ApplyStateMessageBuilder {
29
29
  #address;
@@ -42,14 +42,6 @@ class Scheduler {
42
42
  return this.#defaultInterval;
43
43
  }
44
44
 
45
- get timer() {
46
- return this.#timer;
47
- }
48
-
49
- get currentWorkerRun() {
50
- return this.#currentWorkerRun;
51
- }
52
-
53
45
  static #validateDelay(delayMs, scope = 'delayMs') {
54
46
  const ms = Number(delayMs);
55
47
  if (!Number.isFinite(ms) || ms < 0) {
@@ -26,7 +26,7 @@ class Check {
26
26
  #config
27
27
 
28
28
  /**
29
- * @param {object} config
29
+ * @param {Config} config
30
30
  **/
31
31
  constructor(config) {
32
32
  this.#config = config
@@ -1,8 +1,10 @@
1
1
  import { OperationType as ApplyOperationType } from './protobuf/applyOperations.cjs';
2
- import { MessageType as NetworkMessageType, ResultCode as NetworkResultCode } from './protobuf/network.cjs';
2
+ import networkV1Generated from './protobuf/networkV1.generated.cjs';
3
3
  import b4a from 'b4a'
4
4
  // TODO: We are going to have a lot of contstants. It would be good, to separate them into different files.
5
5
 
6
+ const { MessageType: NetworkMessageType, ResultCode: NetworkResultCode } = networkV1Generated.network.v1;
7
+
6
8
  //ATTENTION - THIS IS USED IN THE APPLY FUNCTION!
7
9
  export const EntryType = Object.freeze({
8
10
  ADMIN: 'admin',
@@ -34,8 +36,6 @@ export const OperationType = Object.freeze({
34
36
  });
35
37
 
36
38
  export const NetworkOperationType = Object.freeze({
37
- VALIDATOR_CONNECTION_REQUEST: NetworkMessageType.MESSAGE_TYPE_VALIDATOR_CONNECTION_REQUEST,
38
- VALIDATOR_CONNECTION_RESPONSE: NetworkMessageType.MESSAGE_TYPE_VALIDATOR_CONNECTION_RESPONSE,
39
39
  LIVENESS_REQUEST: NetworkMessageType.MESSAGE_TYPE_LIVENESS_REQUEST,
40
40
  LIVENESS_RESPONSE: NetworkMessageType.MESSAGE_TYPE_LIVENESS_RESPONSE,
41
41
  BROADCAST_TRANSACTION_REQUEST: NetworkMessageType.MESSAGE_TYPE_BROADCAST_TRANSACTION_REQUEST,
@@ -43,12 +43,68 @@ export const NetworkOperationType = Object.freeze({
43
43
  });
44
44
 
45
45
  export const ResultCode = Object.freeze({
46
+ UNSPECIFIED: NetworkResultCode.RESULT_CODE_UNSPECIFIED,
46
47
  OK: NetworkResultCode.RESULT_CODE_OK,
47
48
  INVALID_PAYLOAD: NetworkResultCode.RESULT_CODE_INVALID_PAYLOAD,
48
- UNSUPPORTED_VERSION: NetworkResultCode.RESULT_CODE_UNSUPPORTED_VERSION,
49
49
  RATE_LIMITED: NetworkResultCode.RESULT_CODE_RATE_LIMITED,
50
- TIMEOUT: NetworkResultCode.RESULT_CODE_TIMEOUT,
51
50
  SIGNATURE_INVALID: NetworkResultCode.RESULT_CODE_SIGNATURE_INVALID,
51
+ UNEXPECTED_ERROR: NetworkResultCode.RESULT_CODE_UNEXPECTED_ERROR,
52
+ TIMEOUT: NetworkResultCode.RESULT_CODE_TIMEOUT,
53
+ NODE_HAS_NO_WRITE_ACCESS: NetworkResultCode.RESULT_CODE_NODE_HAS_NO_WRITE_ACCESS,
54
+ TX_ACCEPTED_PROOF_UNAVAILABLE: NetworkResultCode.RESULT_CODE_TX_ACCEPTED_PROOF_UNAVAILABLE,
55
+ NODE_OVERLOADED: NetworkResultCode.RESULT_CODE_NODE_OVERLOADED,
56
+ TX_ALREADY_PENDING: NetworkResultCode.RESULT_CODE_TX_ALREADY_PENDING,
57
+ OPERATION_TYPE_UNKNOWN: NetworkResultCode.RESULT_CODE_OPERATION_TYPE_UNKNOWN,
58
+ SCHEMA_VALIDATION_FAILED: NetworkResultCode.RESULT_CODE_SCHEMA_VALIDATION_FAILED,
59
+ REQUESTER_ADDRESS_INVALID: NetworkResultCode.RESULT_CODE_REQUESTER_ADDRESS_INVALID,
60
+ REQUESTER_PUBLIC_KEY_INVALID: NetworkResultCode.RESULT_CODE_REQUESTER_PUBLIC_KEY_INVALID,
61
+ TX_HASH_MISMATCH: NetworkResultCode.RESULT_CODE_TX_HASH_MISMATCH,
62
+ TX_SIGNATURE_INVALID: NetworkResultCode.RESULT_CODE_TX_SIGNATURE_INVALID,
63
+ TX_EXPIRED: NetworkResultCode.RESULT_CODE_TX_EXPIRED,
64
+ TX_ALREADY_EXISTS: NetworkResultCode.RESULT_CODE_TX_ALREADY_EXISTS,
65
+ OPERATION_ALREADY_COMPLETED: NetworkResultCode.RESULT_CODE_OPERATION_ALREADY_COMPLETED,
66
+ REQUESTER_NOT_FOUND: NetworkResultCode.RESULT_CODE_REQUESTER_NOT_FOUND,
67
+ INSUFFICIENT_FEE_BALANCE: NetworkResultCode.RESULT_CODE_INSUFFICIENT_FEE_BALANCE,
68
+ EXTERNAL_BOOTSTRAP_EQUALS_MSB_BOOTSTRAP: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_EQUALS_MSB_BOOTSTRAP,
69
+ SELF_VALIDATION_FORBIDDEN: NetworkResultCode.RESULT_CODE_SELF_VALIDATION_FORBIDDEN,
70
+ ROLE_NODE_ENTRY_NOT_FOUND: NetworkResultCode.RESULT_CODE_ROLE_NODE_ENTRY_NOT_FOUND,
71
+ ROLE_NODE_ALREADY_WRITER: NetworkResultCode.RESULT_CODE_ROLE_NODE_ALREADY_WRITER,
72
+ ROLE_NODE_NOT_WHITELISTED: NetworkResultCode.RESULT_CODE_ROLE_NODE_NOT_WHITELISTED,
73
+ ROLE_NODE_NOT_WRITER: NetworkResultCode.RESULT_CODE_ROLE_NODE_NOT_WRITER,
74
+ ROLE_NODE_IS_INDEXER: NetworkResultCode.RESULT_CODE_ROLE_NODE_IS_INDEXER,
75
+ ROLE_ADMIN_ENTRY_MISSING: NetworkResultCode.RESULT_CODE_ROLE_ADMIN_ENTRY_MISSING,
76
+ ROLE_INVALID_RECOVERY_CASE: NetworkResultCode.RESULT_CODE_ROLE_INVALID_RECOVERY_CASE,
77
+ ROLE_UNKNOWN_OPERATION: NetworkResultCode.RESULT_CODE_ROLE_UNKNOWN_OPERATION,
78
+ ROLE_INVALID_WRITER_KEY: NetworkResultCode.RESULT_CODE_ROLE_INVALID_WRITER_KEY,
79
+ ROLE_INSUFFICIENT_FEE_BALANCE: NetworkResultCode.RESULT_CODE_ROLE_INSUFFICIENT_FEE_BALANCE,
80
+ MSB_BOOTSTRAP_MISMATCH: NetworkResultCode.RESULT_CODE_MSB_BOOTSTRAP_MISMATCH,
81
+ EXTERNAL_BOOTSTRAP_NOT_DEPLOYED: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_NOT_DEPLOYED,
82
+ EXTERNAL_BOOTSTRAP_TX_MISSING: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_TX_MISSING,
83
+ EXTERNAL_BOOTSTRAP_MISMATCH: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_MISMATCH,
84
+ BOOTSTRAP_ALREADY_EXISTS: NetworkResultCode.RESULT_CODE_BOOTSTRAP_ALREADY_EXISTS,
85
+ TRANSFER_RECIPIENT_ADDRESS_INVALID: NetworkResultCode.RESULT_CODE_TRANSFER_RECIPIENT_ADDRESS_INVALID,
86
+ TRANSFER_RECIPIENT_PUBLIC_KEY_INVALID: NetworkResultCode.RESULT_CODE_TRANSFER_RECIPIENT_PUBLIC_KEY_INVALID,
87
+ TRANSFER_AMOUNT_TOO_LARGE: NetworkResultCode.RESULT_CODE_TRANSFER_AMOUNT_TOO_LARGE,
88
+ TRANSFER_SENDER_NOT_FOUND: NetworkResultCode.RESULT_CODE_TRANSFER_SENDER_NOT_FOUND,
89
+ TRANSFER_INSUFFICIENT_BALANCE: NetworkResultCode.RESULT_CODE_TRANSFER_INSUFFICIENT_BALANCE,
90
+ TRANSFER_RECIPIENT_BALANCE_OVERFLOW: NetworkResultCode.RESULT_CODE_TRANSFER_RECIPIENT_BALANCE_OVERFLOW,
91
+ TX_HASH_INVALID_FORMAT: NetworkResultCode.RESULT_CODE_TX_HASH_INVALID_FORMAT,
92
+ INTERNAL_ENQUEUE_VALIDATION_FAILED: NetworkResultCode.RESULT_CODE_INTERNAL_ENQUEUE_VALIDATION_FAILED,
93
+ TX_COMMITTED_RECEIPT_MISSING: NetworkResultCode.RESULT_CODE_TX_COMMITTED_RECEIPT_MISSING,
94
+ VALIDATOR_RESPONSE_TX_TYPE_INVALID: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_TX_TYPE_INVALID,
95
+ VALIDATOR_RESPONSE_TX_TYPE_UNKNOWN: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_TX_TYPE_UNKNOWN,
96
+ VALIDATOR_RESPONSE_TX_TYPE_UNSUPPORTED: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_TX_TYPE_UNSUPPORTED,
97
+ VALIDATOR_RESPONSE_SCHEMA_INVALID: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_SCHEMA_INVALID,
98
+ PENDING_REQUEST_MISSING_TX_DATA: NetworkResultCode.RESULT_CODE_PENDING_REQUEST_MISSING_TX_DATA,
99
+ PROOF_PAYLOAD_MISMATCH: NetworkResultCode.RESULT_CODE_PROOF_PAYLOAD_MISMATCH,
100
+ VALIDATOR_WRITER_KEY_NOT_REGISTERED: NetworkResultCode.RESULT_CODE_VALIDATOR_WRITER_KEY_NOT_REGISTERED,
101
+ VALIDATOR_ADDRESS_MISMATCH: NetworkResultCode.RESULT_CODE_VALIDATOR_ADDRESS_MISMATCH,
102
+ VALIDATOR_NODE_ENTRY_NOT_FOUND: NetworkResultCode.RESULT_CODE_VALIDATOR_NODE_ENTRY_NOT_FOUND,
103
+ VALIDATOR_NODE_NOT_WRITER: NetworkResultCode.RESULT_CODE_VALIDATOR_NODE_NOT_WRITER,
104
+ VALIDATOR_WRITER_KEY_MISMATCH: NetworkResultCode.RESULT_CODE_VALIDATOR_WRITER_KEY_MISMATCH,
105
+ VALIDATOR_TX_OBJECT_INVALID: NetworkResultCode.RESULT_CODE_VALIDATOR_TX_OBJECT_INVALID,
106
+ VALIDATOR_VA_MISSING: NetworkResultCode.RESULT_CODE_VALIDATOR_VA_MISSING,
107
+ TX_INVALID_PAYLOAD: NetworkResultCode.RESULT_CODE_TX_INVALID_PAYLOAD
52
108
  });
53
109
 
54
110
  // Role managment constants
@@ -61,6 +117,7 @@ export const EventType = Object.freeze({
61
117
  WARNING: 'warning',
62
118
  VALIDATOR_CONNECTION_READY: 'validator-connection-ready',
63
119
  VALIDATOR_CONNECTION_TIMEOUT: 'validator-connection-timeout',
120
+ VALIDATOR_HEALTH_CHECK: 'validator-health-check',
64
121
  });
65
122
 
66
123
  // Role managment constants
@@ -82,13 +139,6 @@ export const TRAC_NAMESPACE = 'TracNetwork';
82
139
  export const WHITELIST_SLEEP_INTERVAL = 1_000;
83
140
  export const BALANCE_MIGRATION_SLEEP_INTERVAL = 500;
84
141
 
85
- // Connectivity constants
86
- export const MAX_PEERS = 64;
87
- export const MAX_PARALLEL = 64;
88
- export const MAX_SERVER_CONNECTIONS = Infinity;
89
- export const MAX_CLIENT_CONNECTIONS = Infinity;
90
- export const MAX_WRITERS_FOR_ADMIN_INDEXER_CONNECTION = 10;
91
-
92
142
  // State
93
143
  export const ACK_INTERVAL = 1_000;
94
144
  export const AUTOBASE_VALUE_ENCODING = 'binary';
@@ -115,16 +165,13 @@ export const BOOTSTRAP_HEXSTRING_LENGTH = 64;
115
165
 
116
166
  // Pool constants
117
167
  export const BATCH_SIZE = 10;
118
- export const PROCESS_INTERVAL_MS = 50;
119
-
120
- // Rate limiting constants
121
- export const CLEANUP_INTERVAL_MS = 120_000;
122
- export const CONNECTION_TIMEOUT_MS = 60_000;
123
- export const MAX_TRANSACTIONS_PER_SECOND = 50;
124
168
 
125
169
  // Operation handler constants
126
170
  export const MAX_PARTIAL_TX_PAYLOAD_BYTE_SIZE = 3072;
127
- export const TRANSACTION_POOL_SIZE = 1000;
171
+ export const V1_PROTOCOL_PAYLOAD_MAX_SIZE = 4096;
172
+
173
+ // Transaction Commit Service
174
+ export const TRANSACTION_COMMIT_SERVICE_BUFFER_SIZE = 1500;
128
175
 
129
176
  // Network message constants
130
177
  export const NETWORK_MESSAGE_TYPES = Object.freeze({
@@ -140,3 +187,5 @@ export const NETWORK_MESSAGE_TYPES = Object.freeze({
140
187
  NODE: 'nodeResponse'
141
188
  },
142
189
  });
190
+
191
+ export const NETWORK_CAPABILITIES = Object.freeze(["protocols:v1:legacy"]);
@@ -0,0 +1,40 @@
1
+ import b4a from 'b4a';
2
+ import _ from 'lodash';
3
+
4
+ export const isDeepEqualApplyPayload = (left, right) => {
5
+ if (left === right) return true;
6
+
7
+ const leftIsBuffer = b4a.isBuffer(left);
8
+ const rightIsBuffer = b4a.isBuffer(right);
9
+
10
+ if (leftIsBuffer || rightIsBuffer) {
11
+ return leftIsBuffer && rightIsBuffer && b4a.equals(left, right);
12
+ }
13
+
14
+ const leftIsArray = Array.isArray(left);
15
+ const rightIsArray = Array.isArray(right);
16
+
17
+ if (leftIsArray || rightIsArray) {
18
+ if (!leftIsArray || !rightIsArray) return false;
19
+ if (left.length !== right.length) return false;
20
+
21
+ for (let i = 0; i < left.length; i++) {
22
+ if (!isDeepEqualApplyPayload(left[i], right[i])) return false;
23
+ }
24
+ return true;
25
+ }
26
+
27
+ if (!_.isObject(left) || !_.isObject(right)) return false;
28
+
29
+ const leftKeys = Object.keys(left);
30
+ const rightKeys = Object.keys(right);
31
+
32
+ if (leftKeys.length !== rightKeys.length) return false;
33
+
34
+ for (const key of leftKeys) {
35
+ if (!Object.prototype.hasOwnProperty.call(right, key)) return false;
36
+ if (!isDeepEqualApplyPayload(left[key], right[key])) return false;
37
+ }
38
+
39
+ return true;
40
+ };
@@ -123,6 +123,18 @@ export async function getAllMigrationFiles(migrationDirectory = BALANCE_MIGRATED
123
123
  }
124
124
  }
125
125
 
126
+ export async function ensureCoresStoreDir(config) {
127
+ try {
128
+ // const storesDirectoryStats = await fs.promises.stat(config.storesDirectory);
129
+ // if (!storesDirectoryStats.isDirectory()) {
130
+ // throw new Error(`Stores directory path is not a directory: ${config.storesDirectory}`);
131
+ // }
132
+ await fs.promises.mkdir(config.storesFullPath, { recursive: true });
133
+ } catch (err) {
134
+ throw new Error(`Failed to ensure corestore directory: ${err.message}`);
135
+ }
136
+ }
137
+
126
138
  export async function validateBalanceMigrationData(addresses) {
127
139
  const migrationFiles = await getAllMigrationFiles(BALANCE_MIGRATED_DIR);
128
140
  const addressSet = new Set(addresses.map(a => a.address));
@@ -197,6 +209,7 @@ export default {
197
209
  readAddressesFromWhitelistFile,
198
210
  readBalanceMigrationFile,
199
211
  getAllMigrationFiles,
212
+ ensureCoresStoreDir,
200
213
  validateBalanceMigrationData,
201
214
  validateWhitelistMigrationData,
202
215
  getNextMigrationNumber,
@@ -1,7 +1,8 @@
1
1
  import b4a from "b4a";
2
+ import PeerWallet from "trac-wallet";
2
3
  import {bufferToAddress} from "../core/state/utils/address.js";
3
4
  import { EntryType } from "./constants.js";
4
-
5
+ import { v7 as uuidv7 } from 'uuid';
5
6
  //TODO: change file name or split functions below into multiple files (Remember to update imports and tests accordingly)
6
7
 
7
8
  export function isHexString(string) {
@@ -95,3 +96,11 @@ export function isTransactionRecordPut(entry) {
95
96
  const is64 = entry.key.length === 64;
96
97
  return isPut && isHex && is64;
97
98
  }
99
+
100
+ export function generateUUID() {
101
+ return uuidv7();
102
+ }
103
+
104
+ export function publicKeyToAddress(publicKey, config) {
105
+ return PeerWallet.encodeBech32m(config.addressPrefix, b4a.isBuffer(publicKey) ? publicKey : b4a.from(publicKey, typeof publicKey === 'string' ? 'hex' : undefined));
106
+ }
@@ -0,0 +1,25 @@
1
+ export class Logger {
2
+ #config
3
+ constructor(config) {
4
+ this.#config = config;
5
+ }
6
+
7
+ info(message) {
8
+ console.log("i: " + message);
9
+ }
10
+
11
+ debug(message) {
12
+ if (this.#config.debug) {
13
+ console.debug("d: " + message);
14
+ }
15
+ }
16
+
17
+ error(message) {
18
+ console.error("e: " + message);
19
+ }
20
+
21
+ warn(message) {
22
+ console.warn("w: " + message);
23
+ }
24
+
25
+ }