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/rpc/routes/v1.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ handleHealth,
2
3
  handleBalance,
3
4
  handleTxv,
4
5
  handleFee,
@@ -9,10 +10,11 @@ import {
9
10
  handleTransactionDetails,
10
11
  handleFetchBulkTxPayloads,
11
12
  handleTransactionExtendedDetails,
12
- handleAccountDetails
13
+ handleAccountDetails,
13
14
  } from '../handlers.js';
14
15
 
15
16
  export const v1Routes = [
17
+ { method: 'GET', path: '/health', handler: handleHealth },
16
18
  { method: 'GET', path: '/balance', handler: handleBalance },
17
19
  { method: 'GET', path: '/txv', handler: handleTxv },
18
20
  { method: 'GET', path: '/fee', handler: handleFee },
package/rpc/rpc_server.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { createServer } from "./create_server.js";
2
2
 
3
3
  // Called by msb.mjs file
4
- export function startRpcServer(msbInstance, config ,host, port) {
4
+ export function startRpcServer(msbInstance, config) {
5
5
  const server = createServer(msbInstance, config)
6
6
 
7
- return server.listen(port, host, () => {
8
- console.log(`Running RPC with http at http://${host}:${port}`);
7
+ return server.listen(config.port, config.host, () => {
8
+ console.log(`Running RPC with http at http://${config.host}:${config.port}`);
9
9
  });
10
10
  }
@@ -5,10 +5,24 @@ import {
5
5
  normalizeTransferOperation
6
6
  } from "../src/utils/normalizers.js";
7
7
  import { get_confirmed_tx_info, get_unconfirmed_tx_info } from "../src/utils/cli.js";
8
- import {OperationType} from "../src/utils/constants.js";
8
+ import { OperationType } from "../src/utils/constants.js";
9
+ import { sleep } from "../src/utils/helpers.js";
9
10
  import b4a from "b4a";
10
- import PartialTransaction from "../src/core/network/protocols/shared/validators/PartialTransaction.js";
11
- import PartialTransfer from "../src/core/network/protocols/shared/validators/PartialTransfer.js";
11
+ import { ValidationError, BroadcastError, NotFoundError } from "./utils/helpers.js";
12
+ import PartialTransactionValidator from "../src/core/network/protocols/shared/validators/PartialTransactionValidator.js";
13
+ import PartialTransferValidator from "../src/core/network/protocols/shared/validators/PartialTransferValidator.js";
14
+
15
+ // This was added because V1 is not waiting for signed/unsigned state. So to include reverse compatibility
16
+ // we need to slow down v1 to legacy case.
17
+ const waitForUnconfirmedTx = async (state, txHash, config) => {
18
+ const startedAt = Date.now();
19
+ while ((Date.now() - startedAt) < config.messageValidatorResponseTimeout) {
20
+ const payload = await state.get(txHash);
21
+ if (payload) return true;
22
+ await sleep(100);
23
+ }
24
+ return false;
25
+ };
12
26
 
13
27
  export async function getBalance(msbInstance, address, confirmed) {
14
28
  const state = msbInstance.state;
@@ -46,14 +60,15 @@ export async function getUnconfirmedLength(msbInstance) {
46
60
 
47
61
  export async function broadcastTransaction(msbInstance, config, payload) {
48
62
  if (!payload) {
49
- throw new Error("Transaction payload is required for broadcasting.");
63
+ throw new ValidationError("Transaction payload is required for broadcasting.");
50
64
  }
65
+
51
66
  let normalizedPayload;
52
67
  let isValid = false;
53
68
  let hash;
54
69
 
55
- const partialTransferValidator = new PartialTransfer(msbInstance.state, null , config);
56
- const partialTransactionValidator = new PartialTransaction(msbInstance.state, null , config);
70
+ const partialTransferValidator = new PartialTransferValidator(msbInstance.state, null , config);
71
+ const partialTransactionValidator = new PartialTransactionValidator(msbInstance.state, null , config);
57
72
 
58
73
  if (payload.type === OperationType.TRANSFER) {
59
74
  normalizedPayload = normalizeTransferOperation(payload, config);
@@ -66,19 +81,29 @@ export async function broadcastTransaction(msbInstance, config, payload) {
66
81
  }
67
82
 
68
83
  if (!isValid) {
69
- throw new Error("Invalid transaction payload.");
84
+ throw new ValidationError("Invalid transaction payload.");
70
85
  }
71
86
 
72
87
  const success = await msbInstance.broadcastPartialTransaction(payload);
73
88
 
74
89
  if (!success) {
75
- throw new Error("Failed to broadcast transaction after multiple attempts.");
90
+ throw new BroadcastError("Failed to broadcast transaction after multiple attempts.");
91
+ }
92
+
93
+ const isConfirmed = await waitForUnconfirmedTx(msbInstance.state, hash, config);
94
+ if (!isConfirmed) {
95
+ throw new BroadcastError("Failed to broadcast transaction after multiple attempts.");
76
96
  }
77
97
 
78
98
  const signedLength = msbInstance.state.getSignedLength();
79
99
  const unsignedLength = msbInstance.state.getUnsignedLength();
80
100
 
81
- return { message: "Transaction broadcasted successfully.", signedLength, unsignedLength, tx: hash };
101
+ return {
102
+ message: "Transaction broadcasted successfully.",
103
+ signedLength,
104
+ unsignedLength,
105
+ tx: hash
106
+ };
82
107
  }
83
108
 
84
109
  export async function getTxHashes(msbInstance, start, end) {
@@ -89,7 +114,7 @@ export async function getTxHashes(msbInstance, start, end) {
89
114
  export async function getTxDetails(msbInstance, hash) {
90
115
  const rawPayload = await get_confirmed_tx_info(msbInstance.state, hash);
91
116
  if (!rawPayload) {
92
- return null;
117
+ throw new NotFoundError(`Transaction ${hash} not found.`);
93
118
  }
94
119
 
95
120
  return normalizeDecodedPayloadForJson(rawPayload.decoded, msbInstance.config);
@@ -97,11 +122,11 @@ export async function getTxDetails(msbInstance, hash) {
97
122
 
98
123
  export async function fetchBulkTxPayloads(msbInstance, hashes) {
99
124
  if (!Array.isArray(hashes) || hashes.length === 0) {
100
- throw new Error("Missing hash list.");
125
+ throw new ValidationError("Missing hash list.");
101
126
  }
102
127
 
103
128
  if (hashes.length > 1500) {
104
- throw new Error("Length of input tx hashes exceeded.");
129
+ throw new ValidationError("Length of input tx hashes exceeded.");
105
130
  }
106
131
 
107
132
  const res = { results: [], missing: [] };
@@ -111,7 +136,7 @@ export async function fetchBulkTxPayloads(msbInstance, hashes) {
111
136
 
112
137
  results.forEach((result, index) => {
113
138
  const hash = hashes[index];
114
- if (result === null || result === undefined) {
139
+ if (!result) {
115
140
  res.missing.push(hash);
116
141
  } else {
117
142
  const decodedResult = normalizeDecodedPayloadForJson(result.decoded, msbInstance.config);
@@ -124,32 +149,21 @@ export async function fetchBulkTxPayloads(msbInstance, hashes) {
124
149
 
125
150
  export async function getExtendedTxDetails(msbInstance, hash, confirmed) {
126
151
  const state = msbInstance.state;
152
+ let rawPayload;
127
153
 
128
154
  if (confirmed) {
129
- const rawPayload = await get_confirmed_tx_info(state, hash);
130
- if (!rawPayload) {
131
- throw new Error(`No payload found for tx hash: ${hash}`);
132
- }
133
- const confirmedLength = await state.getTransactionConfirmedLength(hash);
134
- if (confirmedLength === null) {
135
- throw new Error(`No confirmed length found for tx hash: ${hash} in confirmed mode`);
136
- }
137
- const normalizedPayload = normalizeDecodedPayloadForJson(rawPayload.decoded, msbInstance.config);
138
- const feeBuffer = state.getFee();
139
- return {
140
- txDetails: normalizedPayload,
141
- confirmed_length: confirmedLength,
142
- fee: bufferToBigInt(feeBuffer).toString(),
143
- };
155
+ rawPayload = await get_confirmed_tx_info(state, hash);
156
+ } else {
157
+ rawPayload = await get_unconfirmed_tx_info(state, hash);
144
158
  }
145
159
 
146
- const rawPayload = await get_unconfirmed_tx_info(state, hash);
147
160
  if (!rawPayload) {
148
- throw new Error(`No payload found for tx hash: ${hash}`);
161
+ throw new NotFoundError(`No payload found for tx hash: ${hash}`);
149
162
  }
150
163
 
151
164
  const normalizedPayload = normalizeDecodedPayloadForJson(rawPayload.decoded, msbInstance.config);
152
165
  const length = await state.getTransactionConfirmedLength(hash);
166
+
153
167
  if (length === null) {
154
168
  return {
155
169
  txDetails: normalizedPayload,
@@ -164,4 +178,4 @@ export async function getExtendedTxDetails(msbInstance, hash, confirmed) {
164
178
  confirmed_length: length,
165
179
  fee: bufferToBigInt(feeBuffer).toString(),
166
180
  };
167
- }
181
+ }
@@ -1,74 +1,105 @@
1
1
  import b4a from "b4a"
2
2
  import { operationToPayload } from "../../src/utils/applyOperations.js"
3
+ import { isHexString } from "../../src/utils/helpers.js";
4
+
5
+ export class ValidationError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "ValidationError";
9
+ }
10
+ }
11
+
12
+ export class BroadcastError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "BroadcastError";
16
+ }
17
+ }
18
+
19
+ export class NotFoundError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "NotFoundError";
23
+ }
24
+ }
25
+
3
26
  export function decodeBase64Payload(base64) {
4
- let decodedPayloadString
5
- try {
6
- decodedPayloadString = b4a.from(base64, "base64").toString("utf-8")
7
- } catch (err) {
8
- throw new Error("Failed to decode base64 payload.")
9
- }
10
-
11
- try {
12
- return JSON.parse(decodedPayloadString)
13
- } catch (err) {
14
- throw new Error("Decoded payload is not valid JSON.")
15
- }
27
+ let decodedPayloadString
28
+ try {
29
+ decodedPayloadString = b4a.from(base64, "base64").toString("utf-8")
30
+ } catch (err) {
31
+ throw new ValidationError("Failed to decode base64 payload.")
32
+ }
33
+
34
+ try {
35
+ return JSON.parse(decodedPayloadString)
36
+ } catch (err) {
37
+ throw new ValidationError("Decoded payload is not valid JSON.")
38
+ }
16
39
  }
17
40
 
18
41
  export function isBase64(str) {
19
- const base64Regex =
20
- /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
21
- return base64Regex.test(str)
42
+ const base64Regex =
43
+ /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
44
+ return base64Regex.test(str)
22
45
  }
23
46
 
24
47
  export function validatePayloadStructure(payload) {
25
- if (
26
- typeof payload !== "object" ||
27
- payload === null ||
28
- typeof payload.type !== "number" ||
29
- typeof payload.address !== "string" ||
30
- !["txo", "tro"].some((key) => key in payload && typeof payload[key] === "object")
31
- ) {
32
- throw new Error("Invalid payload structure.")
33
- }
48
+ if (
49
+ typeof payload !== "object" ||
50
+ payload === null ||
51
+ typeof payload.type !== "number" ||
52
+ typeof payload.address !== "string" ||
53
+ !["txo", "tro"].some((key) => key in payload && typeof payload[key] === "object")
54
+ ) {
55
+ throw new ValidationError("Invalid payload structure.")
56
+ }
34
57
  }
35
58
 
36
59
  export function sanitizeTransferPayload(payload) {
37
- const operationKey = operationToPayload(payload.type);
60
+ const operationKey = operationToPayload(payload.type);
38
61
 
39
- if (operationKey !== 'tro' && operationKey !== 'txo') {
40
- throw new Error('Payload is not a transfer/transaction operation.');
41
- }
62
+ if (operationKey !== 'tro' && operationKey !== 'txo') {
63
+ throw new ValidationError('Payload is not a transfer/transaction operation.');
64
+ }
42
65
 
43
- const operation = payload[operationKey];
44
- if (payload.address && typeof payload.address === "string") {
45
- payload.address = payload.address.trim()
46
- }
66
+ const operation = payload[operationKey];
67
+ if (payload.address && typeof payload.address === "string") {
68
+ payload.address = payload.address.trim()
69
+ }
47
70
 
48
- if (operation && typeof operation === "object") {
49
- for (const [key, value] of Object.entries(operation)) {
50
- if (typeof value === "string") {
51
- let sanitized = value.trim()
71
+ if (operation && typeof operation === "object") {
72
+ for (const [key, value] of Object.entries(operation)) {
73
+ if (typeof value === "string") {
74
+ let sanitized = value.trim()
52
75
 
53
- // normalize hex-like strings
54
- if (/^[0-9A-F]+$/i.test(sanitized)) {
55
- sanitized = sanitized.toLowerCase()
56
- }
76
+ // normalize hex-like strings
77
+ if (/^[0-9A-F]+$/i.test(sanitized)) {
78
+ sanitized = sanitized.toLowerCase()
79
+ }
57
80
 
58
- payload[operationKey][key] = sanitized
59
- }
60
- }
61
- }
81
+ payload[operationKey][key] = sanitized
82
+ }
83
+ }
84
+ }
62
85
 
63
- return payload
86
+ return payload
64
87
  }
65
88
 
66
89
  export function sanitizeBulkPayloadsRequestBody(body) {
67
- const cleanBody = body
68
- .replace(/^\uFEFF/, '')
69
- .replace(/\r/g, '')
70
- .replace(/\0/g, '')
71
- .trim();
90
+ const cleanBody = body
91
+ .replace(/^\uFEFF/, '')
92
+ .replace(/\r/g, '')
93
+ .replace(/\0/g, '')
94
+ .trim();
72
95
 
73
- return JSON.parse(cleanBody);
96
+ return JSON.parse(cleanBody);
74
97
  }
98
+
99
+ export function isValidTxHash(hash) {
100
+ return isHexString(hash) && hash?.length === 64;
101
+ }
102
+
103
+ export function hasSpacesInUrl(url) {
104
+ return url.includes('%20') || url.includes(' ');
105
+ }
@@ -2,10 +2,10 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
4
  import { fileURLToPath } from 'url';
5
- import { execSync } from 'child_process';
5
+ import { execFileSync } from 'child_process';
6
6
 
7
7
  function generateCJSFromProto(inputPath, outputPath) {
8
- execSync(`protocol-buffers "${inputPath}" -o "${outputPath}"`);
8
+ execFileSync('protocol-buffers', [inputPath, '-o', outputPath]);
9
9
  console.log(`${outputPath} has been generated.`);
10
10
  }
11
11
 
@@ -17,26 +17,51 @@ function transformToUseB4a(outputPath) {
17
17
  console.log(`${outputPath} has been modified to use b4a.`);
18
18
  }
19
19
 
20
+ function generatePbjsModule(pbjsPath, protoRootPath, entryPath, outputPath) {
21
+ execFileSync(pbjsPath, [
22
+ '-t', 'static-module',
23
+ '-w', 'commonjs',
24
+ '--keep-case',
25
+ '-p', protoRootPath,
26
+ '-o', outputPath,
27
+ entryPath
28
+ ]);
29
+ console.log(`${outputPath} has been generated.`);
30
+ }
31
+
32
+ function transformPbjsForBare(outputPath) {
33
+ let content = fs.readFileSync(outputPath, 'utf-8');
34
+ const shim = `if (typeof globalThis !== 'undefined' && typeof globalThis.self === 'undefined') {\n globalThis.self = globalThis;\n}\n`;
35
+ const strictDirective = '"use strict";';
36
+
37
+ if (content.includes(strictDirective)) {
38
+ content = content.replace(strictDirective, `${strictDirective}\n${shim}`);
39
+ } else {
40
+ content = `${strictDirective}\n${shim}${content}`;
41
+ }
42
+
43
+ fs.writeFileSync(outputPath, content, 'utf-8');
44
+ console.log(`${outputPath} has been modified for bare-compatible protobufjs runtime.`);
45
+ }
20
46
 
21
47
  function main() {
22
48
  const directoryName = path.dirname(fileURLToPath(import.meta.url));
23
49
 
24
50
  const inputDir = path.join(directoryName, '../proto');
25
51
  const outputDir = path.join(directoryName, '../src/utils/protobuf');
52
+ const pbjsPath = path.join(directoryName, '../node_modules/.bin/pbjs');
53
+ const applyInputPath = path.join(inputDir, 'applyOperations.proto');
54
+ const applyOutputPath = path.join(outputDir, 'applyOperations.cjs');
55
+ const networkEntryPath = path.join(inputDir, 'network/v1/network_message.proto');
56
+ const generatedNetworkOutputPath = path.join(outputDir, 'networkV1.generated.cjs');
26
57
 
27
58
  fs.mkdirSync(outputDir, { recursive: true });
28
59
 
29
- const files = fs.readdirSync(inputDir).filter(f => f.endsWith('.proto'));
60
+ generateCJSFromProto(applyInputPath, applyOutputPath);
61
+ transformToUseB4a(applyOutputPath);
30
62
 
31
- for (const file of files) {
32
- const name = path.basename(file, '.proto');
33
- const inputPath = path.join(inputDir, file);
34
- const outputPath = path.join(outputDir, `${name}.cjs`);
35
-
36
- generateCJSFromProto(inputPath, outputPath);
37
- transformToUseB4a(outputPath);
38
- }
63
+ generatePbjsModule(pbjsPath, inputDir, networkEntryPath, generatedNetworkOutputPath);
64
+ transformPbjsForBare(generatedNetworkOutputPath);
39
65
  }
40
66
 
41
67
  main();
42
-
@@ -0,0 +1,46 @@
1
+ import { createConfig, ENV } from './env.js';
2
+
3
+ const getArguments = () => {
4
+ const pearApp = typeof Pear !== 'undefined' ? (Pear.app ?? Pear.config) : undefined;
5
+ const runtimeArgs = typeof process !== 'undefined' ? process.argv.slice(2) : [];
6
+ return pearApp?.args ?? runtimeArgs;
7
+ };
8
+
9
+ export const resolveEnvironment = (args = []) => {
10
+ const networkIndex = args.indexOf('--network');
11
+ const network = (networkIndex !== -1 && args[networkIndex + 1]) ? args[networkIndex + 1] : undefined;
12
+
13
+ if (network === ENV.MAINNET) return ENV.MAINNET;
14
+ if (network === ENV.DEVELOPMENT) return ENV.DEVELOPMENT;
15
+ if (network === ENV.TESTNET1 || network === 'testnet') return ENV.TESTNET1;
16
+ return ENV.MAINNET;
17
+ };
18
+
19
+ export const isRpcEnabled = () => {
20
+ const args = getArguments();
21
+ return args.includes('--rpc');
22
+ };
23
+
24
+ export const resolveConfig = () => {
25
+ const args = getArguments();
26
+ const runRpc = isRpcEnabled();
27
+ const selectedEnv = resolveEnvironment(args);
28
+ const storesDirectoryIndex = args.indexOf('--stores-directory');
29
+ const storesDirectory = (storesDirectoryIndex !== -1 && args[storesDirectoryIndex + 1]) ? args[storesDirectoryIndex + 1] : undefined;
30
+ const hostIndex = args.indexOf('--host');
31
+ const host = (hostIndex !== -1 && args[hostIndex + 1]) ? args[hostIndex + 1] : undefined;
32
+ const portIndex = args.indexOf('--port');
33
+ const port = (portIndex !== -1 && args[portIndex + 1]) ? parseInt(args[portIndex + 1], 10) : undefined;
34
+
35
+ const rpc = {
36
+ storesDirectory,
37
+ enableWallet: false,
38
+ enableInteractiveMode: false,
39
+ host,
40
+ port
41
+ };
42
+
43
+ const options = runRpc ? rpc : { storesDirectory };
44
+
45
+ return createConfig(selectedEnv, options);
46
+ };
@@ -1,4 +1,6 @@
1
1
  import b4a from 'b4a'
2
+ import { isDefined } from '../utils/type.js'
3
+ import _ from 'lodash'
2
4
 
3
5
  export class Config {
4
6
  #options
@@ -80,7 +82,7 @@ export class Config {
80
82
  }
81
83
 
82
84
  get isAdminMode() {
83
- return this.#options.storeName === 'admin'
85
+ return _.endsWith(this.storesDirectory, '/admin')
84
86
  }
85
87
 
86
88
  get keyPairPath() {
@@ -97,17 +99,68 @@ export class Config {
97
99
  return this.#config.maxValidators
98
100
  }
99
101
 
102
+ get maxPeers() {
103
+ if (this.#isOverriden('maxPeers')) return this.#options.maxPeers
104
+ return this.#config.maxPeers
105
+ }
106
+
107
+ get maxParallel() {
108
+ if (this.#isOverriden('maxParallel')) return this.#options.maxParallel
109
+ return this.#config.maxParallel
110
+ }
111
+
112
+ get maxServerConnections() {
113
+ if (this.#isOverriden('maxServerConnections')) return this.#options.maxServerConnections
114
+ return this.#config.maxServerConnections
115
+ }
116
+
117
+ get maxClientConnections() {
118
+ if (this.#isOverriden('maxClientConnections')) return this.#options.maxClientConnections
119
+ return this.#config.maxClientConnections
120
+ }
121
+
122
+ get maxWritersForAdminIndexerConnection() {
123
+ if (this.#isOverriden('maxWritersForAdminIndexerConnection')) return this.#options.maxWritersForAdminIndexerConnection
124
+ return this.#config.maxWritersForAdminIndexerConnection
125
+ }
126
+
127
+ get processIntervalMs() {
128
+ if (this.#isOverriden('processIntervalMs')) return this.#options.processIntervalMs
129
+ return this.#config.processIntervalMs
130
+ }
131
+
132
+ get transactionPoolSize() {
133
+ if (this.#isOverriden('transactionPoolSize')) return this.#options.transactionPoolSize
134
+ return this.#config.transactionPoolSize
135
+ }
136
+
100
137
  get networkId() {
101
138
  return this.#config.networkId
102
139
  }
103
140
 
141
+ get host() {
142
+ if (this.#isOverriden('host')) return this.#options.host
143
+ return this.#config.host
144
+ }
145
+
146
+ get port() {
147
+ if (this.#isOverriden('port')) return this.#options.port
148
+ return this.#config.port
149
+ }
150
+
104
151
  get storesDirectory() {
105
- if (this.#isOverriden('storesDirectory')) return this.#options.storesDirectory
106
- return this.#config.storesDirectory
152
+ const storesDirectory = this.#isOverriden('storesDirectory') ?
153
+ this.#options.storesDirectory : this.#config.storesDirectory
154
+
155
+ return _.trimEnd(storesDirectory, '/')
156
+ }
157
+
158
+ get storeName() {
159
+ return this.#config.storeName
107
160
  }
108
161
 
109
162
  get storesFullPath() {
110
- return `${this.storesDirectory}${this.#options.storeName}`
163
+ return `${this.storesDirectory}/${this.storeName}`
111
164
  }
112
165
 
113
166
  get messageThreshold() {
@@ -122,9 +175,49 @@ export class Config {
122
175
  return this.#config.messageValidatorResponseTimeout
123
176
  }
124
177
 
178
+ get rateLimitCleanupIntervalMs() {
179
+ if (this.#isOverriden('rateLimitCleanupIntervalMs')) return this.#options.rateLimitCleanupIntervalMs
180
+ return this.#config.rateLimitCleanupIntervalMs
181
+ }
182
+
183
+ get rateLimitConnectionTimeoutMs() {
184
+ if (this.#isOverriden('rateLimitConnectionTimeoutMs')) return this.#options.rateLimitConnectionTimeoutMs
185
+ return this.#config.rateLimitConnectionTimeoutMs
186
+ }
187
+
188
+ get rateLimitMaxTransactionsPerSecond() {
189
+ if (this.#isOverriden('rateLimitMaxTransactionsPerSecond')) return this.#options.rateLimitMaxTransactionsPerSecond
190
+ return this.#config.rateLimitMaxTransactionsPerSecond
191
+ }
192
+
193
+ get pendingRequestTimeout() {
194
+ return this.#config.pendingRequestTimeout
195
+ }
196
+
197
+ get txCommitTimeout() {
198
+ return this.#config.txCommitTimeout
199
+ }
200
+
201
+ get txPoolSize() {
202
+ return this.#config.txPoolSize
203
+ }
204
+
205
+ get validatorHealthCheckInterval() {
206
+ if (this.#isOverriden('validatorHealthCheckInterval')) return this.#options.validatorHealthCheckInterval
207
+ return this.#config.validatorHealthCheckInterval
208
+ }
209
+
210
+ get maxPendingRequestsInPendingRequestsService() {
211
+ return this.#config.maxPendingRequestsInPendingRequestsService
212
+ }
213
+
214
+ get debug() {
215
+ return this.#config.debug
216
+ }
217
+
125
218
  // Most of these properties are boolean
126
219
  #isOverriden(prop) {
127
- return this.#options.hasOwnProperty(prop)
220
+ return this.#options.hasOwnProperty(prop) && isDefined(this.#options[prop])
128
221
  }
129
222
 
130
223
  #validate(options, config) {
@@ -134,4 +227,5 @@ export class Config {
134
227
  );
135
228
  }
136
229
  }
230
+
137
231
  }