trac-msb 0.2.17 → 0.2.19

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 (365) hide show
  1. package/README.md +62 -2
  2. package/SECURITY.md +9 -6
  3. package/cli/commandHandler.js +272 -0
  4. package/cli/handlers.js +210 -0
  5. package/cli/interactive.js +182 -0
  6. package/msb.mjs +7 -8
  7. package/package.json +33 -11
  8. package/rpc/create_server.js +44 -44
  9. package/rpc/handlers.js +43 -56
  10. package/rpc/routes/index.js +8 -8
  11. package/rpc/rpc_server.js +6 -2
  12. package/rpc/utils/helpers.js +46 -66
  13. package/scripts/pear-runner.js +170 -0
  14. package/src/config/args.js +60 -13
  15. package/src/config/config.js +163 -14
  16. package/src/config/env.js +57 -25
  17. package/src/core/network/Network.js +121 -56
  18. package/src/core/network/protocols/LegacyProtocol.js +21 -11
  19. package/src/core/network/protocols/NetworkMessages.js +38 -17
  20. package/src/core/network/protocols/ProtocolInterface.js +15 -3
  21. package/src/core/network/protocols/ProtocolSession.js +144 -17
  22. package/src/core/network/protocols/V1Protocol.js +37 -18
  23. package/src/core/network/protocols/connectionPolicies.js +174 -0
  24. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +26 -20
  25. package/src/core/network/protocols/{shared/handlers/base/BaseOperationHandler.js → legacy/handlers/BaseStateOperationHandler.js} +25 -14
  26. package/src/core/network/protocols/legacy/handlers/{GetRequestHandler.js → LegacyGetRequestHandler.js} +9 -9
  27. package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +23 -0
  28. package/src/core/network/protocols/{shared/handlers/RoleOperationHandler.js → legacy/handlers/LegacyRoleOperationHandler.js} +19 -12
  29. package/src/core/network/protocols/{shared/handlers/SubnetworkOperationHandler.js → legacy/handlers/LegacySubnetworkOperationHandler.js} +29 -18
  30. package/src/core/network/protocols/{shared/handlers/TransferOperationHandler.js → legacy/handlers/LegacyTransferOperationHandler.js} +18 -12
  31. package/src/core/network/protocols/legacy/validators/base/BaseResponse.js +7 -10
  32. package/src/core/network/protocols/shared/validators/{PartialBootstrapDeployment.js → PartialBootstrapDeploymentValidator.js} +9 -4
  33. package/src/core/network/protocols/shared/validators/{base/PartialOperation.js → PartialOperationValidator.js} +53 -31
  34. package/src/core/network/protocols/shared/validators/{PartialRoleAccess.js → PartialRoleAccessValidator.js} +51 -17
  35. package/src/core/network/protocols/shared/validators/{PartialTransaction.js → PartialTransactionValidator.js} +21 -7
  36. package/src/core/network/protocols/shared/validators/{PartialTransfer.js → PartialTransferValidator.js} +32 -14
  37. package/src/core/network/protocols/v1/NetworkMessageRouter.js +91 -7
  38. package/src/core/network/protocols/v1/V1ProtocolError.js +27 -0
  39. package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +66 -0
  40. package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +272 -0
  41. package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +88 -0
  42. package/src/core/network/protocols/v1/handlers/broadcastTransaction/BroadcastTransactionErrorMapper.js +84 -0
  43. package/src/core/network/protocols/v1/handlers/broadcastTransaction/BroadcastTransactionOperationStrategies.js +128 -0
  44. package/src/core/network/protocols/v1/handlers/broadcastTransaction/BroadcastTransactionPayloadUtils.js +26 -0
  45. package/src/core/network/protocols/v1/validators/V1BaseOperation.js +212 -0
  46. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +28 -0
  47. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +260 -0
  48. package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +15 -0
  49. package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +17 -0
  50. package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +210 -0
  51. package/src/core/network/services/ConnectionManager.js +149 -96
  52. package/src/core/network/services/MessageOrchestrator.js +197 -37
  53. package/src/core/network/services/PendingRequestService.js +175 -0
  54. package/src/core/network/services/TransactionCommitService.js +149 -0
  55. package/src/core/network/services/TransactionPoolService.js +129 -18
  56. package/src/core/network/services/TransactionRateLimiterService.js +57 -35
  57. package/src/core/network/services/ValidatorHealthCheckService.js +127 -0
  58. package/src/core/network/services/ValidatorObserverService.js +400 -121
  59. package/src/core/state/State.js +259 -156
  60. package/src/core/state/utils/address.js +4 -9
  61. package/src/core/state/utils/balance.js +7 -7
  62. package/src/core/state/utils/roles.js +1 -1
  63. package/src/index.js +320 -278
  64. package/src/messages/network/v1/NetworkMessageBuilder.js +71 -100
  65. package/src/messages/network/v1/NetworkMessageDirector.js +16 -50
  66. package/src/messages/network/v1/networkMessageFactory.js +1 -1
  67. package/src/messages/state/ApplyStateMessageBuilder.js +6 -6
  68. package/src/messages/state/applyStateMessageFactory.js +1 -1
  69. package/src/utils/Scheduler.js +0 -8
  70. package/src/utils/buffer.js +1 -1
  71. package/src/utils/check.js +12 -13
  72. package/src/utils/constants.js +78 -5
  73. package/src/utils/deepEqualApplyPayload.js +40 -0
  74. package/src/utils/errors.js +20 -0
  75. package/src/utils/fileUtils.js +12 -3
  76. package/src/utils/helpers.js +10 -1
  77. package/src/utils/logger.js +25 -0
  78. package/src/utils/migrationUtils.js +3 -3
  79. package/src/utils/normalizers.js +38 -0
  80. package/src/utils/protobuf/networkV1.generated.cjs +2460 -0
  81. package/src/utils/protobuf/operationHelpers.js +24 -3
  82. package/.dockerignore +0 -16
  83. package/.github/workflows/acceptance-tests.yml +0 -38
  84. package/.github/workflows/lint-pr-title.yml +0 -26
  85. package/.github/workflows/publish.yml +0 -33
  86. package/.github/workflows/unit-tests.yml +0 -34
  87. package/CODE_OF_CONDUCT.md +0 -128
  88. package/docker-compose.yml +0 -17
  89. package/dockerfile +0 -41
  90. package/docs/fee_distribution.md +0 -89
  91. package/docs/trac_network_http_api.openapi.yaml +0 -889
  92. package/migration/.gitkeep +0 -0
  93. package/proto/applyOperations.proto +0 -117
  94. package/proto/network.proto +0 -74
  95. package/rpc/rpc_services.js +0 -163
  96. package/scripts/generate-protobufs.js +0 -42
  97. package/src/core/network/identity/NetworkWalletFactory.js +0 -77
  98. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +0 -37
  99. package/src/utils/cli.js +0 -130
  100. package/src/utils/cliCommands.js +0 -280
  101. package/src/utils/protobuf/network.cjs +0 -840
  102. package/tests/acceptance/v1/account/account.test.mjs +0 -123
  103. package/tests/acceptance/v1/balance/balance.test.mjs +0 -54
  104. package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +0 -116
  105. package/tests/acceptance/v1/confirmed-length/confirmed-length.test.mjs +0 -19
  106. package/tests/acceptance/v1/fee/fee.test.mjs +0 -11
  107. package/tests/acceptance/v1/health/health.test.mjs +0 -33
  108. package/tests/acceptance/v1/rpc.test.mjs +0 -95
  109. package/tests/acceptance/v1/tx/tx.test.mjs +0 -111
  110. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +0 -213
  111. package/tests/acceptance/v1/tx-hashes/tx-hashes.test.mjs +0 -72
  112. package/tests/acceptance/v1/tx-payloads-bulk/tx-payloads-bulk.test.mjs +0 -27
  113. package/tests/acceptance/v1/txv/txv.test.mjs +0 -11
  114. package/tests/acceptance/v1/unconfirmed-length/unconfirmed-length.test.mjs +0 -11
  115. package/tests/fixtures/apply.fixtures.js +0 -77
  116. package/tests/fixtures/assembleMessage.fixtures.js +0 -41
  117. package/tests/fixtures/check.fixtures.js +0 -428
  118. package/tests/fixtures/networkV1.fixtures.js +0 -85
  119. package/tests/fixtures/protobuf.fixtures.js +0 -388
  120. package/tests/helpers/StateNetworkFactory.js +0 -155
  121. package/tests/helpers/address.js +0 -6
  122. package/tests/helpers/autobaseTestHelpers.js +0 -419
  123. package/tests/helpers/config.js +0 -4
  124. package/tests/helpers/createTestSignature.js +0 -13
  125. package/tests/helpers/regexHelper.js +0 -3
  126. package/tests/helpers/setupApplyTests.js +0 -587
  127. package/tests/helpers/transactionPayloads.mjs +0 -92
  128. package/tests/helpers/wrapper.js +0 -28
  129. package/tests/unit/messages/messages.test.js +0 -12
  130. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +0 -276
  131. package/tests/unit/messages/network/NetworkMessageDirector.test.js +0 -201
  132. package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +0 -525
  133. package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +0 -237
  134. package/tests/unit/network/ConnectionManager.test.js +0 -191
  135. package/tests/unit/network/NetworkWalletFactory.test.js +0 -156
  136. package/tests/unit/network/networkModule.test.js +0 -10
  137. package/tests/unit/state/State.test.js +0 -80
  138. package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +0 -42
  139. package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +0 -276
  140. package/tests/unit/state/apply/addAdmin/adminEntryEncodingFailureScenario.js +0 -30
  141. package/tests/unit/state/apply/addAdmin/adminEntryExistsScenario.js +0 -78
  142. package/tests/unit/state/apply/addAdmin/nodeEntryInitializationFailureScenario.js +0 -30
  143. package/tests/unit/state/apply/addAdmin/nonBootstrapNodeScenario.js +0 -68
  144. package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +0 -159
  145. package/tests/unit/state/apply/addIndexer/addIndexerHappyPathScenario.js +0 -39
  146. package/tests/unit/state/apply/addIndexer/addIndexerMultipleIndexersInTheNetworkScenario.js +0 -167
  147. package/tests/unit/state/apply/addIndexer/addIndexerPretenderAlreadyIndexerScenario.js +0 -21
  148. package/tests/unit/state/apply/addIndexer/addIndexerPretenderNotWriterScenario.js +0 -21
  149. package/tests/unit/state/apply/addIndexer/addIndexerRemoveAndReAddScenario.js +0 -186
  150. package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +0 -443
  151. package/tests/unit/state/apply/addIndexer/addIndexerWriterKeyAlreadyRegisteredScenario.js +0 -32
  152. package/tests/unit/state/apply/addIndexer/state.apply.addIndexer.test.js +0 -297
  153. package/tests/unit/state/apply/addWriter/addWriterHappyPathScenario.js +0 -41
  154. package/tests/unit/state/apply/addWriter/addWriterInvalidValidatorSignatureScenario.js +0 -32
  155. package/tests/unit/state/apply/addWriter/addWriterNewWkScenario.js +0 -149
  156. package/tests/unit/state/apply/addWriter/addWriterRequesterAlreadyWriterScenario.js +0 -21
  157. package/tests/unit/state/apply/addWriter/addWriterRequesterBalanceInsufficientScenario.js +0 -21
  158. package/tests/unit/state/apply/addWriter/addWriterRequesterEntryDecodeFailureScenario.js +0 -19
  159. package/tests/unit/state/apply/addWriter/addWriterRequesterEntryMissingScenario.js +0 -19
  160. package/tests/unit/state/apply/addWriter/addWriterRequesterIndexerScenario.js +0 -21
  161. package/tests/unit/state/apply/addWriter/addWriterRequesterNotWhitelistedScenario.js +0 -21
  162. package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +0 -766
  163. package/tests/unit/state/apply/addWriter/addWriterStakeBalanceUpdateFailureScenario.js +0 -50
  164. package/tests/unit/state/apply/addWriter/addWriterStakeInsufficientBalanceScenario.js +0 -29
  165. package/tests/unit/state/apply/addWriter/addWriterStakeInvalidBalanceScenario.js +0 -29
  166. package/tests/unit/state/apply/addWriter/addWriterStakeInvalidEntryScenario.js +0 -21
  167. package/tests/unit/state/apply/addWriter/addWriterStakeStakedBalanceFailureScenario.js +0 -37
  168. package/tests/unit/state/apply/addWriter/addWriterStakeSubtractFailureScenario.js +0 -42
  169. package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +0 -106
  170. package/tests/unit/state/apply/addWriter/addWriterWriterKeyMismatchScenario.js +0 -54
  171. package/tests/unit/state/apply/addWriter/addWriterWriterKeyOwnershipScenario.js +0 -54
  172. package/tests/unit/state/apply/addWriter/addWriterZeroWriterKeyScenario.js +0 -29
  173. package/tests/unit/state/apply/addWriter/state.apply.addWriter.test.js +0 -309
  174. package/tests/unit/state/apply/adminRecovery/adminRecoveryHappyPathScenario.js +0 -30
  175. package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +0 -881
  176. package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +0 -435
  177. package/tests/unit/state/apply/appendWhitelist/appendWhitelistBanAndReapplyScenario.js +0 -78
  178. package/tests/unit/state/apply/appendWhitelist/appendWhitelistExistingReaderHappyPathScenario.js +0 -98
  179. package/tests/unit/state/apply/appendWhitelist/appendWhitelistFeeAfterDisableScenario.js +0 -66
  180. package/tests/unit/state/apply/appendWhitelist/appendWhitelistHappyPathScenario.js +0 -55
  181. package/tests/unit/state/apply/appendWhitelist/appendWhitelistInsufficientAdminBalanceScenario.js +0 -103
  182. package/tests/unit/state/apply/appendWhitelist/appendWhitelistNodeAlreadyWhitelistedScenario.js +0 -60
  183. package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +0 -189
  184. package/tests/unit/state/apply/appendWhitelist/state.apply.appendWhitelist.test.js +0 -220
  185. package/tests/unit/state/apply/balanceInitialization/balanceInitializationHappyPathScenario.js +0 -82
  186. package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +0 -111
  187. package/tests/unit/state/apply/balanceInitialization/invalidAmountScenario.js +0 -45
  188. package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +0 -82
  189. package/tests/unit/state/apply/balanceInitialization/state.apply.balanceInitialization.test.js +0 -189
  190. package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +0 -156
  191. package/tests/unit/state/apply/banValidator/banValidatorHappyPathScenario.js +0 -36
  192. package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +0 -531
  193. package/tests/unit/state/apply/banValidator/banValidatorSequentialBansScenario.js +0 -74
  194. package/tests/unit/state/apply/banValidator/banValidatorTargetDecodeFailureScenario.js +0 -19
  195. package/tests/unit/state/apply/banValidator/banValidatorTargetIndexerScenario.js +0 -32
  196. package/tests/unit/state/apply/banValidator/banValidatorTargetNodeEntryMissingScenario.js +0 -19
  197. package/tests/unit/state/apply/banValidator/banValidatorTargetRoleUpdateFailureScenario.js +0 -19
  198. package/tests/unit/state/apply/banValidator/banValidatorWhitelistedNonWriterScenario.js +0 -38
  199. package/tests/unit/state/apply/banValidator/banValidatorWhitelistedZeroBalanceScenario.js +0 -91
  200. package/tests/unit/state/apply/banValidator/banValidatorWithdrawFailureScenario.js +0 -19
  201. package/tests/unit/state/apply/banValidator/state.apply.banValidator.test.js +0 -266
  202. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +0 -143
  203. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentHappyPathScenario.js +0 -26
  204. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentIncompleteOperationScenario.js +0 -94
  205. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentInvalidDeploymentEntryScenario.js +0 -37
  206. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentMultipleBootstrapScenario.js +0 -86
  207. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +0 -347
  208. package/tests/unit/state/apply/bootstrapDeployment/invalidValidatorNodeEntryScenario.js +0 -57
  209. package/tests/unit/state/apply/bootstrapDeployment/state.apply.bootstrapDeployment.test.js +0 -429
  210. package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +0 -120
  211. package/tests/unit/state/apply/common/access-control/adminEntryDecodeFailureScenario.js +0 -130
  212. package/tests/unit/state/apply/common/access-control/adminEntryExistsScenario.js +0 -93
  213. package/tests/unit/state/apply/common/access-control/adminEntryMissingScenario.js +0 -108
  214. package/tests/unit/state/apply/common/access-control/adminOnlyGuardScenario.js +0 -126
  215. package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +0 -121
  216. package/tests/unit/state/apply/common/access-control/roleAccessOperationValidationScenario.js +0 -50
  217. package/tests/unit/state/apply/common/adminControlOperationValidationScenario.js +0 -56
  218. package/tests/unit/state/apply/common/balances/adminEntryUpdateFailureScenario.js +0 -52
  219. package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +0 -198
  220. package/tests/unit/state/apply/common/balances/feeDecodeFailureScenario.js +0 -52
  221. package/tests/unit/state/apply/common/balances/requesterBalanceDecodeFailureScenario.js +0 -15
  222. package/tests/unit/state/apply/common/balances/requesterBalanceFeeApplicationFailureScenario.js +0 -11
  223. package/tests/unit/state/apply/common/balances/requesterBalanceInsufficientScenario.js +0 -15
  224. package/tests/unit/state/apply/common/balances/requesterBalanceUpdateFailureScenario.js +0 -11
  225. package/tests/unit/state/apply/common/balances/validatorEntryRewardFailureScenario.js +0 -11
  226. package/tests/unit/state/apply/common/balances/validatorEntryUpdateFailureScenario.js +0 -11
  227. package/tests/unit/state/apply/common/balances/validatorNodeEntryDecodeFailureScenario.js +0 -40
  228. package/tests/unit/state/apply/common/base/OperationValidationScenarioBase.js +0 -114
  229. package/tests/unit/state/apply/common/commonScenarioHelper.js +0 -103
  230. package/tests/unit/state/apply/common/indexer/indexerNodeEntryDecodeFailureScenario.js +0 -36
  231. package/tests/unit/state/apply/common/indexer/indexerNodeEntryMissingScenario.js +0 -36
  232. package/tests/unit/state/apply/common/indexer/indexerRoleUpdateFailureScenario.js +0 -29
  233. package/tests/unit/state/apply/common/indexer/indexerSequenceStateInvalidScenario.js +0 -66
  234. package/tests/unit/state/apply/common/invalidMessageComponentValidationScenario.js +0 -84
  235. package/tests/unit/state/apply/common/nodeEntryInitializationFailureScenario.js +0 -47
  236. package/tests/unit/state/apply/common/operationAlreadyAppliedScenario.js +0 -85
  237. package/tests/unit/state/apply/common/payload-structure/addressWithInvalidPublicKeyScenario.js +0 -52
  238. package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +0 -54
  239. package/tests/unit/state/apply/common/payload-structure/invalidAddressValidationScenario.js +0 -73
  240. package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +0 -71
  241. package/tests/unit/state/apply/common/payload-structure/invalidPayloadValidationScenario.js +0 -31
  242. package/tests/unit/state/apply/common/payload-structure/invalidSignatureValidationScenario.js +0 -142
  243. package/tests/unit/state/apply/common/payload-structure/partialOperationValidationScenario.js +0 -87
  244. package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +0 -71
  245. package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +0 -73
  246. package/tests/unit/state/apply/common/requester/requesterNodeEntryMissingScenario.js +0 -36
  247. package/tests/unit/state/apply/common/requesterAddressValidationScenario.js +0 -44
  248. package/tests/unit/state/apply/common/requesterPublicKeyValidationScenario.js +0 -25
  249. package/tests/unit/state/apply/common/transactionValidityMismatchScenario.js +0 -98
  250. package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +0 -202
  251. package/tests/unit/state/apply/common/validatorConsistency/validatorEntryDecodeFailureScenario.js +0 -17
  252. package/tests/unit/state/apply/common/validatorConsistency/validatorEntryMissingScenario.js +0 -44
  253. package/tests/unit/state/apply/common/validatorConsistency/validatorInactiveScenario.js +0 -19
  254. package/tests/unit/state/apply/common/validatorConsistency/validatorWriterKeyMismatchScenario.js +0 -18
  255. package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +0 -315
  256. package/tests/unit/state/apply/common/validatorEntryValidation/validatorEntryInvalidBalanceScenario.js +0 -18
  257. package/tests/unit/state/apply/common/writerKeyExistsValidationScenario.js +0 -43
  258. package/tests/unit/state/apply/disableInitialization/disableInitializationAlreadyDisabledScenario.js +0 -53
  259. package/tests/unit/state/apply/disableInitialization/disableInitializationHappyPathScenario.js +0 -24
  260. package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +0 -204
  261. package/tests/unit/state/apply/disableInitialization/state.apply.disableInitialization.test.js +0 -161
  262. package/tests/unit/state/apply/missing-tests.md +0 -18
  263. package/tests/unit/state/apply/removeIndexer/removeIndexerHappyPathScenario.js +0 -58
  264. package/tests/unit/state/apply/removeIndexer/removeIndexerReAddAndRemoveAgainScenario.js +0 -98
  265. package/tests/unit/state/apply/removeIndexer/removeIndexerRemoveMultipleIndexersScenario.js +0 -167
  266. package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +0 -429
  267. package/tests/unit/state/apply/removeIndexer/removeIndexerTargetNotIndexerScenario.js +0 -22
  268. package/tests/unit/state/apply/removeIndexer/removeIndexerWriterKeyMissingScenario.js +0 -20
  269. package/tests/unit/state/apply/removeIndexer/state.apply.removeIndexer.test.js +0 -291
  270. package/tests/unit/state/apply/removeWriter/removeWriterAndAddWriterAgainScenario.js +0 -87
  271. package/tests/unit/state/apply/removeWriter/removeWriterHappyPathScenario.js +0 -38
  272. package/tests/unit/state/apply/removeWriter/removeWriterInvalidValidatorSignatureScenario.js +0 -32
  273. package/tests/unit/state/apply/removeWriter/removeWriterRequesterBalanceInsufficientScenario.js +0 -21
  274. package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryDecodeFailureScenario.js +0 -19
  275. package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryMissingScenario.js +0 -19
  276. package/tests/unit/state/apply/removeWriter/removeWriterRequesterIndexerScenario.js +0 -21
  277. package/tests/unit/state/apply/removeWriter/removeWriterRequesterNotWriterScenario.js +0 -21
  278. package/tests/unit/state/apply/removeWriter/removeWriterRequesterRoleUpdateFailureScenario.js +0 -19
  279. package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +0 -348
  280. package/tests/unit/state/apply/removeWriter/removeWriterThroughWriterValidatorScenario.js +0 -113
  281. package/tests/unit/state/apply/removeWriter/removeWriterUnstakeFailureScenario.js +0 -33
  282. package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyMismatchScenario.js +0 -21
  283. package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyOwnershipScenario.js +0 -26
  284. package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyRegistryMissingScenario.js +0 -22
  285. package/tests/unit/state/apply/removeWriter/state.apply.removeWriter.test.js +0 -307
  286. package/tests/unit/state/apply/state.apply.test.js +0 -24
  287. package/tests/unit/state/apply/transfer/state.apply.transfer.test.js +0 -819
  288. package/tests/unit/state/apply/transfer/transferContractSchemaValidationScenario.js +0 -22
  289. package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +0 -146
  290. package/tests/unit/state/apply/transfer/transferDoubleSpendSameBatchScenario.js +0 -63
  291. package/tests/unit/state/apply/transfer/transferDoubleSpendSingleValidatorScenario.js +0 -67
  292. package/tests/unit/state/apply/transfer/transferExistingRecipientAmountScenario.js +0 -31
  293. package/tests/unit/state/apply/transfer/transferExistingRecipientZeroAmountScenario.js +0 -31
  294. package/tests/unit/state/apply/transfer/transferHandlerGuardScenarios.js +0 -22
  295. package/tests/unit/state/apply/transfer/transferHappyPathScenario.js +0 -8
  296. package/tests/unit/state/apply/transfer/transferInvalidIncomingDataScenario.js +0 -66
  297. package/tests/unit/state/apply/transfer/transferNewRecipientAmountScenario.js +0 -31
  298. package/tests/unit/state/apply/transfer/transferNewRecipientZeroAmountScenario.js +0 -31
  299. package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +0 -1170
  300. package/tests/unit/state/apply/transfer/transferSelfTransferAmountScenario.js +0 -38
  301. package/tests/unit/state/apply/transfer/transferSelfTransferZeroAmountScenario.js +0 -38
  302. package/tests/unit/state/apply/transfer/transferValidatorRecipientAmountScenario.js +0 -38
  303. package/tests/unit/state/apply/transfer/transferValidatorRecipientZeroAmountScenario.js +0 -38
  304. package/tests/unit/state/apply/txOperation/state.apply.txOperation.test.js +0 -318
  305. package/tests/unit/state/apply/txOperation/txOperationBootstrapNotRegisteredScenario.js +0 -70
  306. package/tests/unit/state/apply/txOperation/txOperationDifferentValidatorCreatorHappyPathScenario.js +0 -23
  307. package/tests/unit/state/apply/txOperation/txOperationInvalidDeploymentEntryScenario.js +0 -48
  308. package/tests/unit/state/apply/txOperation/txOperationInvalidFeeAmountScenario.js +0 -39
  309. package/tests/unit/state/apply/txOperation/txOperationInvalidSubnetCreatorAddressScenario.js +0 -46
  310. package/tests/unit/state/apply/txOperation/txOperationRequesterCreatorHappyPathScenario.js +0 -21
  311. package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +0 -432
  312. package/tests/unit/state/apply/txOperation/txOperationStandardHappyPathScenario.js +0 -21
  313. package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddCreatorBalanceFailureScenario.js +0 -26
  314. package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBalanceFailureScenario.js +0 -25
  315. package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBonusFailureScenario.js +0 -27
  316. package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeCreatorEntryScenario.js +0 -18
  317. package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeRequesterEntryScenario.js +0 -17
  318. package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeValidatorEntryScenario.js +0 -31
  319. package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardBypassScenario.js +0 -49
  320. package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +0 -93
  321. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInsufficientRequesterBalanceScenario.js +0 -28
  322. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidCreatorBalanceScenario.js +0 -29
  323. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterBalanceScenario.js +0 -28
  324. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterEntryScenario.js +0 -17
  325. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidValidatorBalanceScenario.js +0 -33
  326. package/tests/unit/state/apply/txOperation/txOperationTransferFeeMissingCreatorEntryScenario.js +0 -18
  327. package/tests/unit/state/apply/txOperation/txOperationTransferFeeSubtractFailureScenario.js +0 -25
  328. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateCreatorBalanceFailureScenario.js +0 -26
  329. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateFailureScenario.js +0 -25
  330. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBalanceFailureScenario.js +0 -26
  331. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBonusFailureScenario.js +0 -27
  332. package/tests/unit/state/apply/txOperation/txOperationValidatorCreatorHappyPathScenario.js +0 -21
  333. package/tests/unit/state/stateModule.test.js +0 -23
  334. package/tests/unit/state/stateTestUtils.js +0 -32
  335. package/tests/unit/state/utils/address.test.js +0 -63
  336. package/tests/unit/state/utils/adminEntry.test.js +0 -52
  337. package/tests/unit/state/utils/balance.test.js +0 -320
  338. package/tests/unit/state/utils/lengthEntry.test.js +0 -54
  339. package/tests/unit/state/utils/nodeEntry.test.js +0 -298
  340. package/tests/unit/state/utils/roles.test.js +0 -44
  341. package/tests/unit/unit.test.js +0 -14
  342. package/tests/unit/utils/amountSerialization/amountSerialization.test.js +0 -237
  343. package/tests/unit/utils/buffer/buffer.test.js +0 -251
  344. package/tests/unit/utils/check/adminControlOperation.test.js +0 -156
  345. package/tests/unit/utils/check/balanceInitializationOperation.test.js +0 -54
  346. package/tests/unit/utils/check/bootstrapDeploymentOperation.test.js +0 -104
  347. package/tests/unit/utils/check/check.test.js +0 -17
  348. package/tests/unit/utils/check/common.test.js +0 -419
  349. package/tests/unit/utils/check/coreAdminOperation.test.js +0 -95
  350. package/tests/unit/utils/check/roleAccessOperation.test.js +0 -255
  351. package/tests/unit/utils/check/transactionOperation.test.js +0 -107
  352. package/tests/unit/utils/check/transferOperation.test.js +0 -102
  353. package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +0 -95
  354. package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +0 -149
  355. package/tests/unit/utils/helpers/helpers.test.js +0 -11
  356. package/tests/unit/utils/helpers/isHexString.test.js +0 -12
  357. package/tests/unit/utils/helpers/normalizeHex.test.js +0 -82
  358. package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +0 -100
  359. package/tests/unit/utils/normalizers/normalizers.test.js +0 -469
  360. package/tests/unit/utils/protobuf/applyOperations.test.js +0 -91
  361. package/tests/unit/utils/protobuf/operationHelpers.test.js +0 -305
  362. package/tests/unit/utils/protobuf/protobuf.test.js +0 -12
  363. package/tests/unit/utils/type/type.test.js +0 -25
  364. package/tests/unit/utils/utils.test.js +0 -20
  365. package/whitelist/.gitkeep +0 -0
@@ -1,11 +1,24 @@
1
- import { sleep } from '../../../utils/helpers.js';
1
+ import { generateUUID, publicKeyToAddress } from '../../../utils/helpers.js';
2
2
  import { operationToPayload } from '../../../utils/applyOperations.js';
3
+ import { networkMessageFactory } from "../../../messages/network/v1/networkMessageFactory.js";
4
+ import { NETWORK_CAPABILITIES, ResultCode } from "../../../utils/constants.js";
5
+ import {
6
+ unsafeEncodeApplyOperation
7
+ } from "../../../utils/protobuf/operationHelpers.js";
8
+ import { normalizeMessageByOperationType } from "../../../utils/normalizers.js";
9
+ import { resultToValidatorAction, SENDER_ACTION } from "../protocols/connectionPolicies.js";
10
+ import { ConnectionManagerError } from './ConnectionManager.js';
3
11
  /**
4
12
  * MessageOrchestrator coordinates message submission, retry, and validator management.
5
13
  * It works with ConnectionManager and ledger state to ensure reliable message delivery.
6
14
  */
7
15
  class MessageOrchestrator {
8
16
  #config;
17
+ #wallet;
18
+ #idempotentSuccessCodes = new Set([
19
+ ResultCode.TX_ALREADY_EXISTS,
20
+ ResultCode.OPERATION_ALREADY_COMPLETED,
21
+ ]);
9
22
  /**
10
23
  * Attempts to send a message to validators with retries and state checks.
11
24
  * @param {ConnectionManager} connectionManager - The connection manager instance
@@ -16,57 +29,204 @@ class MessageOrchestrator {
16
29
  this.connectionManager = connectionManager;
17
30
  this.state = state;
18
31
  this.#config = config;
32
+ this.#wallet = null;
33
+ }
34
+
35
+ setWallet(wallet) {
36
+ this.#wallet = wallet;
37
+ }
38
+
39
+ /**
40
+ * Picks a validator for an outgoing message while avoiding requester self-validation.
41
+ *
42
+ * ValidatorObserverService already prevents connecting to the local node itself.
43
+ * This method handles a different case: for a given message, we avoid selecting
44
+ * a validator whose address equals `message.address` (requester), because
45
+ * validator-side checks reject that flow.
46
+ *
47
+ * @param {object} [message] Outgoing operation payload.
48
+ * @param {string} [message.address] Requester address (bech32m).
49
+ * @returns {string|null} Selected validator public key hex, or null when unavailable.
50
+ */
51
+ #pickValidatorForMessage(message) {
52
+ const requesterAddress = message?.address;
53
+ if (!requesterAddress || typeof this.connectionManager.connectedValidators !== 'function') {
54
+ return this.connectionManager.pickRandomConnectedValidator();
55
+ }
56
+
57
+ const connected = this.connectionManager.connectedValidators();
58
+ if (!Array.isArray(connected) || connected.length === 0) {
59
+ return null;
60
+ }
61
+
62
+ const eligible = connected.filter((publicKey) => {
63
+ return publicKeyToAddress(publicKey, this.#config) !== requesterAddress;
64
+ });
65
+
66
+ const pool = eligible.length > 0 ? eligible : connected;
67
+ if (typeof this.connectionManager.pickRandomValidator === 'function') {
68
+ return this.connectionManager.pickRandomValidator(pool);
69
+ }
70
+
71
+ const index = Math.floor(Math.random() * pool.length);
72
+ return pool[index] ?? null;
19
73
  }
20
74
 
21
75
  /**
22
76
  * Sends a message to a single randomly selected connected validator.
23
77
  * @param {object} message - The message object to be sent
78
+ * @param retries - The current retry count for this message
24
79
  * @returns {Promise<boolean>} - true if successful, false otherwise
25
80
  */
26
- async send(message) {
27
- const startTime = Date.now();
28
- while (Date.now() - startTime < this.#config.messageValidatorResponseTimeout) {
29
- const validator = this.connectionManager.pickRandomConnectedValidator();
30
- if (!validator) return false;
31
-
32
- const success = await this.#attemptSendMessage(validator, message);
33
- if (success) {
34
- return true;
35
- }
81
+ async send(message, retries = 0) {
82
+ if (retries > this.#config.maxRetries) {
83
+ console.warn(`MessageOrchestrator: Max retries reached for message ${JSON.stringify(message)}. Aborting send.`);
84
+ return false;
36
85
  }
37
- return false;
38
- }
39
86
 
40
- async #attemptSendMessage(validator, message) {
41
- let attempts = 0;
42
- const deductedTxType = operationToPayload(message.type);
43
- while (attempts <= this.#config.maxRetries) {
44
- this.connectionManager.sendSingleMessage(message, validator);
45
-
46
- const appeared = await this.waitForUnsignedState(message[deductedTxType].tx, this.#config.messageValidatorRetryDelay);
47
- if (appeared) {
48
- this.incrementSentCount(validator);
49
- if (this.shouldRemove(validator)) {
50
- this.connectionManager.remove(validator);
51
- }
52
- return true;
87
+ const validatorPublicKey = this.#pickValidatorForMessage(message);
88
+ if (!validatorPublicKey) return false;
89
+ console.log("Sending message to validator:", publicKeyToAddress(validatorPublicKey, this.#config));
90
+
91
+ /* NOTE: Since the retry logic for Legacy is handled here, and is very unique to the protocol,
92
+ * it was decided to not change MessageOrchestrator send method in the refactor to make protocols transparent.
93
+ * As the Legacy protocol is going to be deprecated soon, it was decided to keep the retry logic
94
+ * here instead of abstracting it in the protocol implementation.
95
+ * If we were to abstract it, we would need to add protocol-specific logic in the ProtocolSession
96
+ * or ProtocolInterface, which would make them less clean and more coupled with the specifics of the protocols.
97
+ * The parts to be refactored in the future are marked with TODO comments.
98
+ */
99
+
100
+ // TODO: After Legacy is deprecated, we don't need to check preferred protocol here.
101
+ const validatorConnection = this.connectionManager.getConnection(validatorPublicKey);
102
+ const preferredProtocol = validatorConnection.protocolSession.preferredProtocol;
103
+ let success = false;
104
+ if (preferredProtocol === validatorConnection.protocolSession.supportedProtocols.LEGACY) {
105
+
106
+ try {
107
+ success = await this.#attemptSendMessageForLegacy(validatorPublicKey, message);
108
+ } catch {
109
+ success = await this.send(message, retries + 1);
110
+ }
111
+ if (!success) {
112
+ // Remove validator and retry
113
+ this.connectionManager.remove(validatorPublicKey);
114
+ success = await this.send(message, retries + 1);
53
115
  }
54
- attempts++;
116
+ } else if (preferredProtocol === validatorConnection.protocolSession.supportedProtocols.V1) {
117
+ // TODO: This is probably better placed inside the V1 protocol definition.
118
+ // Both protocols should receive a 'canonical' message and solve the encodings internally
119
+ // Refactor
120
+ const normalizedMessage = normalizeMessageByOperationType(message, this.#config)
121
+ const encodedTransaction = unsafeEncodeApplyOperation(normalizedMessage)
122
+ const v1Message = await networkMessageFactory(this.#wallet, this.#config)
123
+ .buildBroadcastTransactionRequest(
124
+ generateUUID(),
125
+ encodedTransaction,
126
+ NETWORK_CAPABILITIES
127
+ );
128
+
129
+ await this.connectionManager.sendSingleMessage(v1Message, validatorPublicKey)
130
+ .then(
131
+ async (resultCode) => {
132
+ if (await this.#isIdempotentSuccess(resultCode, message)) {
133
+ success = true;
134
+ return;
135
+ }
136
+
137
+ // TODO: When we will deprecate the legacy protocol, we should refactor this scope, to propagate domain-error with result code.
138
+ const action = resultToValidatorAction(resultCode);
139
+ switch (action) {
140
+ case SENDER_ACTION.SUCCESS:
141
+ success = true;
142
+ //TODO: Create a function for action below, and replace it also in legacy flow.
143
+ this.incrementSentCount(validatorPublicKey);
144
+ if (this.shouldRemove(validatorPublicKey)) {
145
+ this.connectionManager.remove(validatorPublicKey);
146
+ }
147
+ break;
148
+ case SENDER_ACTION.ROTATE:
149
+ this.connectionManager.remove(validatorPublicKey);
150
+ break;
151
+ case SENDER_ACTION.NO_ROTATE:
152
+ // ignore
153
+ break;
154
+ default:
155
+ this.connectionManager.remove(validatorPublicKey);
156
+ console.warn(
157
+ `MessageOrchestrator: Unrecognized action from connectionPolicies: ${action}.
158
+ ResultCode was: ${resultCode}. Removing validator ${publicKeyToAddress(validatorPublicKey, this.#config)}`
159
+ );
160
+ break;
161
+ }
162
+ }
163
+ )
164
+ .catch(
165
+ async (err) => {
166
+ if (err instanceof ConnectionManagerError) {
167
+ success = await this.send(message, retries + 1);
168
+ console.warn(`MessageOrchestrator: Connection Error: ${err.message}`);
169
+ } else {
170
+ this.connectionManager.remove(validatorPublicKey);
171
+ success = await this.send(message, retries + 1);
172
+ }
173
+ }
174
+ )
175
+
55
176
  }
177
+ return success;
178
+ }
56
179
 
57
- // If all retries fail, remove validator from pool
58
- this.connectionManager.remove(validator);
59
- return false;
180
+ /**
181
+ * Determines whether a non-OK result code should be treated as a success due to idempotency.
182
+ *
183
+ * This handles the retry scenario where a requester did not receive or accept the response
184
+ * from the first validator that committed the transaction. On retry, a second validator may
185
+ * return TX_ALREADY_EXISTS or OPERATION_ALREADY_COMPLETED because the tx was already
186
+ * processed. Those codes are not errors — they confirm the operation succeeded.
187
+ *
188
+ * To safely treat these as success, we check whether the local unsigned state for the tx
189
+ * has been committed. This ensures we only acknowledge idempotent success when the state
190
+ * change is actually observable locally, not just because the result code matched.
191
+ */
192
+ async #isIdempotentSuccess(resultCode, message) {
193
+ if (!this.#idempotentSuccessCodes.has(resultCode)) return false;
194
+
195
+ const txHash = this.#extractTxHash(message);
196
+ if (!txHash) return false;
197
+
198
+ // A short wait covers the race where a first validator committed the tx
199
+ // and a retried validator only observes it as "already exists/completed".
200
+ const timeout = this.#config.messageValidatorResponseTimeout ?? 2000;
201
+ return await this.waitForUnsignedState(txHash, timeout);
60
202
  }
61
203
 
62
204
  async waitForUnsignedState(txHash, timeout) {
63
- // Polls state for the transaction hash for up to timeout ms
64
- const start = Date.now();
65
- let entry = null;
66
- while (Date.now() - start < timeout) {
67
- await sleep(200);
68
- entry = await this.state.get(txHash)
69
- if (entry) return true;
205
+ return this.state.waitForUnsigned(txHash, timeout);
206
+ }
207
+
208
+ #extractTxHash(message) {
209
+ if (!message || !Number.isInteger(message.type)) return null;
210
+
211
+ const payloadKey = operationToPayload(message.type);
212
+ const txHash = message?.[payloadKey]?.tx;
213
+ return typeof txHash === 'string' && txHash.length > 0 ? txHash : null;
214
+ }
215
+
216
+ // TODO: Delete this function after legacy protocol is deprecated
217
+ async #attemptSendMessageForLegacy(validatorPublicKey, message) {
218
+ const deductedTxType = operationToPayload(message.type);
219
+ await this.connectionManager.sendSingleMessage(message, validatorPublicKey);
220
+ const appeared = await this.state.waitForUnsigned(
221
+ message[deductedTxType].tx,
222
+ this.#config.messageValidatorResponseTimeout
223
+ );
224
+ if (appeared) {
225
+ this.incrementSentCount(validatorPublicKey);
226
+ if (this.shouldRemove(validatorPublicKey)) {
227
+ this.connectionManager.remove(validatorPublicKey);
228
+ }
229
+ return true;
70
230
  }
71
231
  return false;
72
232
  }
@@ -0,0 +1,175 @@
1
+ import {NetworkOperationType, ResultCode} from '../../../utils/constants.js';
2
+ import {isHexString, publicKeyToAddress} from '../../../utils/helpers.js';
3
+ import {V1ProtocolError} from "../protocols/v1/V1ProtocolError.js";
4
+ import b4a from 'b4a';
5
+
6
+ const PEER_PUBLIC_KEY_HEX_LENGTH = 64;
7
+
8
+ export class PendingRequestServiceTimeoutError extends Error {
9
+ constructor(requestId, peerAddress, timeoutMs) {
10
+ super(`Pending request ${requestId} to peer ${peerAddress} timed out after ${timeoutMs} ms.`);
11
+ this.name = this.constructor.name;
12
+ }
13
+ }
14
+
15
+ export default class PendingRequestService {
16
+ #pendingRequests;
17
+ #requestMessageTypes = [NetworkOperationType.LIVENESS_REQUEST, NetworkOperationType.BROADCAST_TRANSACTION_REQUEST];
18
+ #config;
19
+
20
+ constructor(config) {
21
+ this.#pendingRequests = new Map(); // Map<id, pendingRequestEntry>
22
+ this.#config = config;
23
+ }
24
+
25
+ has(id) {
26
+ return this.#pendingRequests.has(id);
27
+ }
28
+
29
+ isProbePending(peerPubKeyHex) {
30
+ for (const [, entry] of this.#pendingRequests) {
31
+ if (entry.requestedTo === peerPubKeyHex && entry.requestType === NetworkOperationType.LIVENESS_REQUEST) {
32
+ return true;
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+
38
+ #validateRegisterInput(peerPubKeyHex, message) {
39
+ if (!isHexString(peerPubKeyHex) || peerPubKeyHex.length !== PEER_PUBLIC_KEY_HEX_LENGTH) {
40
+ throw new Error('Invalid peer public key. Expected 32-byte hex string.');
41
+ }
42
+
43
+ if (!message || typeof message !== 'object') {
44
+ throw new Error('Pending request message must be an object.');
45
+ }
46
+
47
+ if (typeof message.id !== 'string' || message.id.length === 0) {
48
+ throw new Error('Pending request ID must be a non-empty string.');
49
+ }
50
+
51
+ if (!this.#requestMessageTypes.includes(message.type)) {
52
+ throw new Error('Unsupported pending request type.');
53
+ }
54
+ }
55
+
56
+ /*
57
+ @returns {Promise}
58
+ */
59
+ registerPendingRequest(peerPubKeyHex, message) {
60
+ this.#validateRegisterInput(peerPubKeyHex, message);
61
+ const id = message.id;
62
+ const peerAddress = publicKeyToAddress(peerPubKeyHex, this.#config);
63
+ if (this.#pendingRequests.size >= this.#config.maxPendingRequestsInPendingRequestsService) {
64
+ throw new Error('Maximum number of pending requests reached.');
65
+ }
66
+
67
+ if (this.#pendingRequests.has(id)) {
68
+ throw new Error(`Pending request with ID ${id} from peer ${peerPubKeyHex} already exists.`);
69
+ }
70
+
71
+ const entry = {
72
+ id: id,
73
+ requestType: message.type,
74
+ requestTxData: this.#extractRequestTxData(message),
75
+ requestedTo: peerPubKeyHex,
76
+ timeoutId: null,
77
+ resolve: null,
78
+ reject: null,
79
+ }
80
+
81
+ const promise = new Promise((resolve, reject) => {
82
+ entry.resolve = resolve;
83
+ entry.reject = reject;
84
+ });
85
+
86
+ entry.timeoutId = setTimeout(() => {
87
+ this.rejectPendingRequest(
88
+ id,
89
+ new PendingRequestServiceTimeoutError(
90
+ id,
91
+ peerAddress,
92
+ this.#config.pendingRequestTimeout
93
+ )
94
+ );
95
+
96
+ }, this.#config.pendingRequestTimeout);
97
+
98
+ this.#pendingRequests.set(id, entry);
99
+ return promise;
100
+ }
101
+
102
+ #extractRequestTxData(message) {
103
+ if (message.type !== NetworkOperationType.BROADCAST_TRANSACTION_REQUEST) return null;
104
+ const txData = message.broadcast_transaction_request?.data;
105
+ return b4a.isBuffer(txData) ? txData : null;
106
+ }
107
+
108
+ getAndDeletePendingRequest(id) {
109
+ const entry = this.#pendingRequests.get(id);
110
+ if (!entry) return null;
111
+
112
+ clearTimeout(entry.timeoutId);
113
+ this.#pendingRequests.delete(id);
114
+ return entry;
115
+ }
116
+
117
+ getPendingRequest(id) {
118
+ const entry = this.#pendingRequests.get(id);
119
+ if (!entry) return null;
120
+ return entry;
121
+ }
122
+
123
+ // for now, we are resolving only resultCode, but we can extend it in the future if needed...
124
+ resolvePendingRequest(id, resultCode = ResultCode.OK) {
125
+ const entry = this.getAndDeletePendingRequest(id);
126
+ if (!entry) return false;
127
+ entry.resolve(resultCode);
128
+ return true;
129
+ }
130
+
131
+ rejectPendingRequest(id, error) {
132
+ const entry = this.getAndDeletePendingRequest(id);
133
+ if (!entry) return false;
134
+ entry.reject(error instanceof Error ? error : new Error(error?.message ?? 'Unexpected error'));
135
+ return true;
136
+ }
137
+
138
+ rejectPendingRequestsForPeer(peerPubKeyHex, error) {
139
+ const idsToReject = [];
140
+ for (const [id, entry] of this.#pendingRequests) {
141
+ if (entry.requestedTo === peerPubKeyHex) idsToReject.push(id);
142
+ }
143
+
144
+ for (const id of idsToReject) {
145
+ this.rejectPendingRequest(id, error);
146
+ }
147
+
148
+ return idsToReject.length;
149
+ }
150
+
151
+ stopPendingRequestTimeout(id) {
152
+ const entry = this.#pendingRequests.get(id);
153
+ if (!entry) return false;
154
+
155
+ clearTimeout(entry.timeoutId);
156
+ entry.timeoutId = null;
157
+ return true;
158
+ }
159
+
160
+ close() {
161
+ for (const [id, entry] of this.#pendingRequests) {
162
+ clearTimeout(entry.timeoutId);
163
+ try {
164
+ entry.reject(
165
+ new V1ProtocolError(
166
+ ResultCode.UNEXPECTED_ERROR,
167
+ `Pending request ${id} cancelled (shutdown).`)
168
+ );
169
+ } catch (error) {
170
+ console.error(`PendingRequestService.close: failed to reject pending request ${id}:`, error);
171
+ }
172
+ }
173
+ this.#pendingRequests.clear();
174
+ }
175
+ }
@@ -0,0 +1,149 @@
1
+ import {isHexString} from '../../../utils/helpers.js';
2
+ import {TRANSACTION_COMMIT_SERVICE_BUFFER_SIZE} from '../../../utils/constants.js';
3
+
4
+ const TX_HASH_HEX_STRING_LENGTH = 64;
5
+
6
+ export class PendingCommitInvalidTxHashError extends Error {
7
+ constructor(txHash) {
8
+ super(`Invalid txHash format: ${txHash}`);
9
+ }
10
+ }
11
+
12
+ export class PendingCommitBufferFullError extends Error {
13
+ constructor(limit) {
14
+ super(`Maximum number of pending commits reached (limit=${limit}).`);
15
+ }
16
+ }
17
+
18
+ export class PendingCommitAlreadyExistsError extends Error {
19
+ constructor(txHash) {
20
+ super(`Pending commit for txHash ${txHash} already exists.`);
21
+ }
22
+ }
23
+
24
+ export class PendingCommitTimeoutError extends Error {
25
+ constructor(txHash, timeoutMs) {
26
+ super(`Pending commit for txHash ${txHash} timed out after ${timeoutMs} ms.`);
27
+ }
28
+ }
29
+
30
+ export class PendingCommitCancelledError extends Error {
31
+ constructor(txHash) {
32
+ super(`Pending commit ${txHash} cancelled (shutdown).`);
33
+ }
34
+ }
35
+
36
+ export class PendingCommitUnexpectedError extends Error {
37
+ constructor(message = 'Unexpected commit error') {
38
+ super(message);
39
+ }
40
+ }
41
+
42
+ class TransactionCommitService {
43
+ #pendingCommits;
44
+ #config;
45
+
46
+ constructor(config) {
47
+ this.#pendingCommits = new Map(); // Map<txHash, pendingCommitEntry>
48
+ this.#config = config;
49
+ }
50
+
51
+ #assertTxHash(txHash) {
52
+ if (!isHexString(txHash) || txHash.length !== TX_HASH_HEX_STRING_LENGTH) {
53
+ throw new PendingCommitInvalidTxHashError(txHash);
54
+ }
55
+ }
56
+
57
+ has(txHash) {
58
+ this.#assertTxHash(txHash);
59
+ return this.#pendingCommits.has(txHash);
60
+ }
61
+
62
+ /*
63
+ @returns {Promise}
64
+ */
65
+ registerPendingCommit(txHash) {
66
+ this.#assertTxHash(txHash);
67
+
68
+ if (this.#pendingCommits.size >= TRANSACTION_COMMIT_SERVICE_BUFFER_SIZE) {
69
+ throw new PendingCommitBufferFullError(TRANSACTION_COMMIT_SERVICE_BUFFER_SIZE);
70
+ }
71
+
72
+ if (this.#pendingCommits.has(txHash)) {
73
+ throw new PendingCommitAlreadyExistsError(txHash);
74
+ }
75
+
76
+ const timeoutMs = this.#config.txCommitTimeout;
77
+
78
+ const entry = {
79
+ txHash,
80
+ timeoutMs,
81
+ timeoutId: null,
82
+ resolve: null,
83
+ reject: null,
84
+ };
85
+
86
+ const promise = new Promise((resolve, reject) => {
87
+ entry.resolve = resolve;
88
+ entry.reject = reject;
89
+ });
90
+
91
+ entry.timeoutId = setTimeout(() => {
92
+ this.rejectPendingCommit(
93
+ txHash,
94
+ new PendingCommitTimeoutError(txHash, timeoutMs)
95
+ );
96
+ }, timeoutMs);
97
+
98
+ this.#pendingCommits.set(txHash, entry);
99
+ return promise;
100
+ }
101
+
102
+ getAndDeletePendingCommit(txHash) {
103
+ this.#assertTxHash(txHash);
104
+ const entry = this.#pendingCommits.get(txHash);
105
+ if (!entry) return null;
106
+
107
+ clearTimeout(entry.timeoutId);
108
+ this.#pendingCommits.delete(txHash);
109
+ return entry;
110
+ }
111
+
112
+ resolvePendingCommit(txHash, receipt = null) {
113
+ this.#assertTxHash(txHash);
114
+ const entry = this.getAndDeletePendingCommit(txHash);
115
+ if (!entry) return false;
116
+ entry.resolve(receipt);
117
+ return true;
118
+ }
119
+
120
+ rejectPendingCommit(txHash, error) {
121
+ this.#assertTxHash(txHash);
122
+ const entry = this.getAndDeletePendingCommit(txHash);
123
+ if (!entry) return false;
124
+
125
+ entry.reject(
126
+ error instanceof Error
127
+ ? error
128
+ : new PendingCommitUnexpectedError('Unexpected commit error')
129
+ );
130
+
131
+ return true;
132
+ }
133
+
134
+ close() {
135
+ for (const [txHash, entry] of this.#pendingCommits) {
136
+ clearTimeout(entry.timeoutId);
137
+ try {
138
+ entry.reject(
139
+ new PendingCommitCancelledError(txHash)
140
+ );
141
+ } catch (error) {
142
+ console.error(`TransactionCommitService.close: failed to reject pending commit ${txHash}:`, error);
143
+ }
144
+ }
145
+ this.#pendingCommits.clear();
146
+ }
147
+ }
148
+
149
+ export default TransactionCommitService;