trac-msb 0.2.4 → 0.2.6

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 (239) hide show
  1. package/.dockerignore +16 -0
  2. package/.github/workflows/acceptance-tests.yml +7 -0
  3. package/.github/workflows/publish.yml +40 -0
  4. package/.github/workflows/{CI.yml → unit-tests.yml} +1 -1
  5. package/README.md +175 -50
  6. package/docker-compose.yml +16 -0
  7. package/dockerfile +41 -0
  8. package/docs/fee_distribution.md +89 -0
  9. package/msb.mjs +12 -14
  10. package/package.json +8 -4
  11. package/rpc/constants.mjs +4 -1
  12. package/rpc/handlers.mjs +109 -66
  13. package/rpc/routes/v1.mjs +3 -1
  14. package/rpc/rpc_services.js +126 -0
  15. package/rpc/utils/confirmedParameter.mjs +17 -0
  16. package/rpc/utils/url.mjs +38 -0
  17. package/src/core/network/Network.js +27 -10
  18. package/src/core/network/identity/NetworkWalletFactory.js +78 -0
  19. package/src/core/network/services/ConnectionManager.js +2 -2
  20. package/src/core/network/services/ValidatorObserverService.js +7 -4
  21. package/src/core/state/State.js +28 -22
  22. package/src/index.js +197 -385
  23. package/src/utils/cliCommands.js +280 -0
  24. package/src/utils/constants.js +3 -1
  25. package/tests/acceptance/v1/account/account.test.mjs +123 -0
  26. package/tests/acceptance/v1/balance/balance.test.mjs +55 -0
  27. package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +111 -0
  28. package/tests/acceptance/v1/confirmed-length/confirmed-length.test.mjs +19 -0
  29. package/tests/acceptance/v1/fee/fee.test.mjs +11 -0
  30. package/tests/acceptance/v1/rpc.test.mjs +62 -291
  31. package/tests/acceptance/v1/tx/tx.test.mjs +98 -0
  32. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +195 -0
  33. package/tests/acceptance/v1/tx-hashes/tx-hashes.test.mjs +72 -0
  34. package/tests/acceptance/v1/tx-payloads-bulk/tx-payloads-bulk.test.mjs +27 -0
  35. package/tests/acceptance/v1/txv/txv.test.mjs +11 -0
  36. package/tests/acceptance/v1/unconfirmed-length/unconfirmed-length.test.mjs +11 -0
  37. package/tests/helpers/StateNetworkFactory.js +157 -0
  38. package/tests/helpers/autobaseTestHelpers.js +369 -0
  39. package/tests/helpers/createTestSignature.js +12 -0
  40. package/tests/helpers/transactionPayloads.mjs +78 -0
  41. package/tests/unit/network/NetworkWalletFactory.test.js +156 -0
  42. package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +38 -0
  43. package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +273 -0
  44. package/tests/unit/state/apply/addAdmin/adminEntryEncodingFailureScenario.js +30 -0
  45. package/tests/unit/state/apply/addAdmin/adminEntryExistsScenario.js +78 -0
  46. package/tests/unit/state/apply/addAdmin/nodeEntryInitializationFailureScenario.js +30 -0
  47. package/tests/unit/state/apply/addAdmin/nonBootstrapNodeScenario.js +68 -0
  48. package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +155 -0
  49. package/tests/unit/state/apply/addIndexer/addIndexerHappyPathScenario.js +39 -0
  50. package/tests/unit/state/apply/addIndexer/addIndexerMultipleIndexersInTheNetworkScenario.js +167 -0
  51. package/tests/unit/state/apply/addIndexer/addIndexerPretenderAlreadyIndexerScenario.js +21 -0
  52. package/tests/unit/state/apply/addIndexer/addIndexerPretenderNotWriterScenario.js +21 -0
  53. package/tests/unit/state/apply/addIndexer/addIndexerRemoveAndReAddScenario.js +186 -0
  54. package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +445 -0
  55. package/tests/unit/state/apply/addIndexer/addIndexerWriterKeyAlreadyRegisteredScenario.js +32 -0
  56. package/tests/unit/state/apply/addIndexer/state.apply.addIndexer.test.js +297 -0
  57. package/tests/unit/state/apply/addWriter/addWriterHappyPathScenario.js +41 -0
  58. package/tests/unit/state/apply/addWriter/addWriterInvalidValidatorSignatureScenario.js +32 -0
  59. package/tests/unit/state/apply/addWriter/addWriterNewWkScenario.js +149 -0
  60. package/tests/unit/state/apply/addWriter/addWriterRequesterAlreadyWriterScenario.js +21 -0
  61. package/tests/unit/state/apply/addWriter/addWriterRequesterBalanceInsufficientScenario.js +21 -0
  62. package/tests/unit/state/apply/addWriter/addWriterRequesterEntryDecodeFailureScenario.js +19 -0
  63. package/tests/unit/state/apply/addWriter/addWriterRequesterEntryMissingScenario.js +19 -0
  64. package/tests/unit/state/apply/addWriter/addWriterRequesterIndexerScenario.js +21 -0
  65. package/tests/unit/state/apply/addWriter/addWriterRequesterNotWhitelistedScenario.js +21 -0
  66. package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +757 -0
  67. package/tests/unit/state/apply/addWriter/addWriterStakeBalanceUpdateFailureScenario.js +50 -0
  68. package/tests/unit/state/apply/addWriter/addWriterStakeInsufficientBalanceScenario.js +29 -0
  69. package/tests/unit/state/apply/addWriter/addWriterStakeInvalidBalanceScenario.js +29 -0
  70. package/tests/unit/state/apply/addWriter/addWriterStakeInvalidEntryScenario.js +21 -0
  71. package/tests/unit/state/apply/addWriter/addWriterStakeStakedBalanceFailureScenario.js +37 -0
  72. package/tests/unit/state/apply/addWriter/addWriterStakeSubtractFailureScenario.js +42 -0
  73. package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +105 -0
  74. package/tests/unit/state/apply/addWriter/addWriterWriterKeyMismatchScenario.js +54 -0
  75. package/tests/unit/state/apply/addWriter/addWriterWriterKeyOwnershipScenario.js +54 -0
  76. package/tests/unit/state/apply/addWriter/addWriterZeroWriterKeyScenario.js +29 -0
  77. package/tests/unit/state/apply/addWriter/state.apply.addWriter.test.js +309 -0
  78. package/tests/unit/state/apply/adminRecovery/adminRecoveryHappyPathScenario.js +30 -0
  79. package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +866 -0
  80. package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +439 -0
  81. package/tests/unit/state/apply/appendWhitelist/appendWhitelistBanAndReapplyScenario.js +78 -0
  82. package/tests/unit/state/apply/appendWhitelist/appendWhitelistExistingReaderHappyPathScenario.js +98 -0
  83. package/tests/unit/state/apply/appendWhitelist/appendWhitelistFeeAfterDisableScenario.js +66 -0
  84. package/tests/unit/state/apply/appendWhitelist/appendWhitelistHappyPathScenario.js +55 -0
  85. package/tests/unit/state/apply/appendWhitelist/appendWhitelistInsufficientAdminBalanceScenario.js +103 -0
  86. package/tests/unit/state/apply/appendWhitelist/appendWhitelistNodeAlreadyWhitelistedScenario.js +60 -0
  87. package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +191 -0
  88. package/tests/unit/state/apply/appendWhitelist/state.apply.appendWhitelist.test.js +220 -0
  89. package/tests/unit/state/apply/balanceInitialization/balanceInitializationHappyPathScenario.js +82 -0
  90. package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +106 -0
  91. package/tests/unit/state/apply/balanceInitialization/invalidAmountScenario.js +45 -0
  92. package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +81 -0
  93. package/tests/unit/state/apply/balanceInitialization/state.apply.balanceInitialization.test.js +189 -0
  94. package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +155 -0
  95. package/tests/unit/state/apply/banValidator/banValidatorHappyPathScenario.js +36 -0
  96. package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +534 -0
  97. package/tests/unit/state/apply/banValidator/banValidatorSequentialBansScenario.js +74 -0
  98. package/tests/unit/state/apply/banValidator/banValidatorTargetDecodeFailureScenario.js +19 -0
  99. package/tests/unit/state/apply/banValidator/banValidatorTargetIndexerScenario.js +32 -0
  100. package/tests/unit/state/apply/banValidator/banValidatorTargetNodeEntryMissingScenario.js +19 -0
  101. package/tests/unit/state/apply/banValidator/banValidatorTargetRoleUpdateFailureScenario.js +19 -0
  102. package/tests/unit/state/apply/banValidator/banValidatorWhitelistedNonWriterScenario.js +38 -0
  103. package/tests/unit/state/apply/banValidator/banValidatorWhitelistedZeroBalanceScenario.js +91 -0
  104. package/tests/unit/state/apply/banValidator/banValidatorWithdrawFailureScenario.js +19 -0
  105. package/tests/unit/state/apply/banValidator/state.apply.banValidator.test.js +266 -0
  106. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +142 -0
  107. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentHappyPathScenario.js +26 -0
  108. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentIncompleteOperationScenario.js +94 -0
  109. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentInvalidDeploymentEntryScenario.js +37 -0
  110. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentMultipleBootstrapScenario.js +86 -0
  111. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +344 -0
  112. package/tests/unit/state/apply/bootstrapDeployment/invalidValidatorNodeEntryScenario.js +57 -0
  113. package/tests/unit/state/apply/bootstrapDeployment/state.apply.bootstrapDeployment.test.js +429 -0
  114. package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +119 -0
  115. package/tests/unit/state/apply/common/access-control/adminEntryDecodeFailureScenario.js +130 -0
  116. package/tests/unit/state/apply/common/access-control/adminEntryExistsScenario.js +93 -0
  117. package/tests/unit/state/apply/common/access-control/adminEntryMissingScenario.js +108 -0
  118. package/tests/unit/state/apply/common/access-control/adminOnlyGuardScenario.js +126 -0
  119. package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +120 -0
  120. package/tests/unit/state/apply/common/access-control/roleAccessOperationValidationScenario.js +50 -0
  121. package/tests/unit/state/apply/common/adminControlOperationValidationScenario.js +56 -0
  122. package/tests/unit/state/apply/common/balances/adminEntryUpdateFailureScenario.js +52 -0
  123. package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +197 -0
  124. package/tests/unit/state/apply/common/balances/feeDecodeFailureScenario.js +52 -0
  125. package/tests/unit/state/apply/common/balances/requesterBalanceDecodeFailureScenario.js +15 -0
  126. package/tests/unit/state/apply/common/balances/requesterBalanceFeeApplicationFailureScenario.js +11 -0
  127. package/tests/unit/state/apply/common/balances/requesterBalanceInsufficientScenario.js +15 -0
  128. package/tests/unit/state/apply/common/balances/requesterBalanceUpdateFailureScenario.js +11 -0
  129. package/tests/unit/state/apply/common/balances/validatorEntryRewardFailureScenario.js +11 -0
  130. package/tests/unit/state/apply/common/balances/validatorEntryUpdateFailureScenario.js +11 -0
  131. package/tests/unit/state/apply/common/balances/validatorNodeEntryDecodeFailureScenario.js +40 -0
  132. package/tests/unit/state/apply/common/base/OperationValidationScenarioBase.js +114 -0
  133. package/tests/unit/state/apply/common/commonScenarioHelper.js +103 -0
  134. package/tests/unit/state/apply/common/indexer/indexerNodeEntryDecodeFailureScenario.js +36 -0
  135. package/tests/unit/state/apply/common/indexer/indexerNodeEntryMissingScenario.js +36 -0
  136. package/tests/unit/state/apply/common/indexer/indexerRoleUpdateFailureScenario.js +29 -0
  137. package/tests/unit/state/apply/common/indexer/indexerSequenceStateInvalidScenario.js +66 -0
  138. package/tests/unit/state/apply/common/invalidMessageComponentValidationScenario.js +84 -0
  139. package/tests/unit/state/apply/common/nodeEntryInitializationFailureScenario.js +47 -0
  140. package/tests/unit/state/apply/common/operationAlreadyAppliedScenario.js +85 -0
  141. package/tests/unit/state/apply/common/payload-structure/addressWithInvalidPublicKeyScenario.js +52 -0
  142. package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +49 -0
  143. package/tests/unit/state/apply/common/payload-structure/invalidAddressValidationScenario.js +73 -0
  144. package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +71 -0
  145. package/tests/unit/state/apply/common/payload-structure/invalidPayloadValidationScenario.js +31 -0
  146. package/tests/unit/state/apply/common/payload-structure/invalidSignatureValidationScenario.js +142 -0
  147. package/tests/unit/state/apply/common/payload-structure/partialOperationValidationScenario.js +87 -0
  148. package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +70 -0
  149. package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +72 -0
  150. package/tests/unit/state/apply/common/requester/requesterNodeEntryMissingScenario.js +36 -0
  151. package/tests/unit/state/apply/common/requesterAddressValidationScenario.js +44 -0
  152. package/tests/unit/state/apply/common/requesterPublicKeyValidationScenario.js +25 -0
  153. package/tests/unit/state/apply/common/transactionValidityMismatchScenario.js +98 -0
  154. package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +201 -0
  155. package/tests/unit/state/apply/common/validatorConsistency/validatorEntryDecodeFailureScenario.js +17 -0
  156. package/tests/unit/state/apply/common/validatorConsistency/validatorEntryMissingScenario.js +44 -0
  157. package/tests/unit/state/apply/common/validatorConsistency/validatorInactiveScenario.js +19 -0
  158. package/tests/unit/state/apply/common/validatorConsistency/validatorWriterKeyMismatchScenario.js +18 -0
  159. package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +314 -0
  160. package/tests/unit/state/apply/common/validatorEntryValidation/validatorEntryInvalidBalanceScenario.js +18 -0
  161. package/tests/unit/state/apply/common/writerKeyExistsValidationScenario.js +43 -0
  162. package/tests/unit/state/apply/disableInitialization/disableInitializationAlreadyDisabledScenario.js +53 -0
  163. package/tests/unit/state/apply/disableInitialization/disableInitializationHappyPathScenario.js +24 -0
  164. package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +197 -0
  165. package/tests/unit/state/apply/disableInitialization/state.apply.disableInitialization.test.js +161 -0
  166. package/tests/unit/state/apply/missing-tests.md +18 -0
  167. package/tests/unit/state/apply/removeIndexer/removeIndexerHappyPathScenario.js +58 -0
  168. package/tests/unit/state/apply/removeIndexer/removeIndexerReAddAndRemoveAgainScenario.js +98 -0
  169. package/tests/unit/state/apply/removeIndexer/removeIndexerRemoveMultipleIndexersScenario.js +167 -0
  170. package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +428 -0
  171. package/tests/unit/state/apply/removeIndexer/removeIndexerTargetNotIndexerScenario.js +22 -0
  172. package/tests/unit/state/apply/removeIndexer/removeIndexerWriterKeyMissingScenario.js +20 -0
  173. package/tests/unit/state/apply/removeIndexer/state.apply.removeIndexer.test.js +291 -0
  174. package/tests/unit/state/apply/removeWriter/removeWriterAndAddWriterAgainScenario.js +87 -0
  175. package/tests/unit/state/apply/removeWriter/removeWriterHappyPathScenario.js +38 -0
  176. package/tests/unit/state/apply/removeWriter/removeWriterInvalidValidatorSignatureScenario.js +32 -0
  177. package/tests/unit/state/apply/removeWriter/removeWriterRequesterBalanceInsufficientScenario.js +21 -0
  178. package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryDecodeFailureScenario.js +19 -0
  179. package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryMissingScenario.js +19 -0
  180. package/tests/unit/state/apply/removeWriter/removeWriterRequesterIndexerScenario.js +21 -0
  181. package/tests/unit/state/apply/removeWriter/removeWriterRequesterNotWriterScenario.js +21 -0
  182. package/tests/unit/state/apply/removeWriter/removeWriterRequesterRoleUpdateFailureScenario.js +19 -0
  183. package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +344 -0
  184. package/tests/unit/state/apply/removeWriter/removeWriterThroughWriterValidatorScenario.js +113 -0
  185. package/tests/unit/state/apply/removeWriter/removeWriterUnstakeFailureScenario.js +33 -0
  186. package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyMismatchScenario.js +21 -0
  187. package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyOwnershipScenario.js +26 -0
  188. package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyRegistryMissingScenario.js +22 -0
  189. package/tests/unit/state/apply/removeWriter/state.apply.removeWriter.test.js +307 -0
  190. package/tests/unit/state/apply/state.apply.test.js +24 -0
  191. package/tests/unit/state/apply/transfer/state.apply.transfer.test.js +819 -0
  192. package/tests/unit/state/apply/transfer/transferContractSchemaValidationScenario.js +22 -0
  193. package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +137 -0
  194. package/tests/unit/state/apply/transfer/transferDoubleSpendSameBatchScenario.js +63 -0
  195. package/tests/unit/state/apply/transfer/transferDoubleSpendSingleValidatorScenario.js +67 -0
  196. package/tests/unit/state/apply/transfer/transferExistingRecipientAmountScenario.js +31 -0
  197. package/tests/unit/state/apply/transfer/transferExistingRecipientZeroAmountScenario.js +31 -0
  198. package/tests/unit/state/apply/transfer/transferHandlerGuardScenarios.js +22 -0
  199. package/tests/unit/state/apply/transfer/transferHappyPathScenario.js +8 -0
  200. package/tests/unit/state/apply/transfer/transferInvalidIncomingDataScenario.js +66 -0
  201. package/tests/unit/state/apply/transfer/transferNewRecipientAmountScenario.js +31 -0
  202. package/tests/unit/state/apply/transfer/transferNewRecipientZeroAmountScenario.js +31 -0
  203. package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +1167 -0
  204. package/tests/unit/state/apply/transfer/transferSelfTransferAmountScenario.js +38 -0
  205. package/tests/unit/state/apply/transfer/transferSelfTransferZeroAmountScenario.js +38 -0
  206. package/tests/unit/state/apply/transfer/transferValidatorRecipientAmountScenario.js +38 -0
  207. package/tests/unit/state/apply/transfer/transferValidatorRecipientZeroAmountScenario.js +38 -0
  208. package/tests/unit/state/apply/txOperation/state.apply.txOperation.test.js +318 -0
  209. package/tests/unit/state/apply/txOperation/txOperationBootstrapNotRegisteredScenario.js +70 -0
  210. package/tests/unit/state/apply/txOperation/txOperationDifferentValidatorCreatorHappyPathScenario.js +23 -0
  211. package/tests/unit/state/apply/txOperation/txOperationInvalidDeploymentEntryScenario.js +48 -0
  212. package/tests/unit/state/apply/txOperation/txOperationInvalidFeeAmountScenario.js +39 -0
  213. package/tests/unit/state/apply/txOperation/txOperationInvalidSubnetCreatorAddressScenario.js +46 -0
  214. package/tests/unit/state/apply/txOperation/txOperationRequesterCreatorHappyPathScenario.js +21 -0
  215. package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +429 -0
  216. package/tests/unit/state/apply/txOperation/txOperationStandardHappyPathScenario.js +21 -0
  217. package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddCreatorBalanceFailureScenario.js +26 -0
  218. package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBalanceFailureScenario.js +25 -0
  219. package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBonusFailureScenario.js +27 -0
  220. package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeCreatorEntryScenario.js +18 -0
  221. package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeRequesterEntryScenario.js +17 -0
  222. package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeValidatorEntryScenario.js +31 -0
  223. package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardBypassScenario.js +49 -0
  224. package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +92 -0
  225. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInsufficientRequesterBalanceScenario.js +28 -0
  226. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidCreatorBalanceScenario.js +29 -0
  227. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterBalanceScenario.js +28 -0
  228. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterEntryScenario.js +17 -0
  229. package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidValidatorBalanceScenario.js +33 -0
  230. package/tests/unit/state/apply/txOperation/txOperationTransferFeeMissingCreatorEntryScenario.js +18 -0
  231. package/tests/unit/state/apply/txOperation/txOperationTransferFeeSubtractFailureScenario.js +25 -0
  232. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateCreatorBalanceFailureScenario.js +26 -0
  233. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateFailureScenario.js +25 -0
  234. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBalanceFailureScenario.js +26 -0
  235. package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBonusFailureScenario.js +27 -0
  236. package/tests/unit/state/apply/txOperation/txOperationValidatorCreatorHappyPathScenario.js +21 -0
  237. package/tests/unit/state/stateModule.test.js +1 -0
  238. package/tests/unit/state/stateTestUtils.js +5 -1
  239. package/.env +0 -3
@@ -1,17 +1,30 @@
1
- import request from "supertest"
2
1
  import { createServer } from "../../../rpc/create_server.mjs"
3
2
  import { initTemporaryDirectory } from '../../helpers/setupApplyTests.js'
4
- import { testKeyPair1, testKeyPair2 } from '../../fixtures/apply.fixtures.js'
5
- import { randomBytes, setupMsbAdmin, setupMsbWriter, fundPeer, removeTemporaryDirectory } from "../../helpers/setupApplyTests.js"
6
- import { $TNK } from "../../../src/core/state/utils/balance.js"
7
- import tracCrypto from 'trac-crypto-api';
8
- import b4a from 'b4a'
3
+ import { testKeyPair1, testKeyPair2, testKeyPair3 } from '../../fixtures/apply.fixtures.js'
4
+ import { randomBytes, setupMsbAdmin, setupMsbWriter, removeTemporaryDirectory, setupMsbPeer, tryToSyncWriters, waitForNodeState } from "../../helpers/setupApplyTests.js"
5
+ import { registerAccountTests } from "./account/account.test.mjs"
6
+ import { registerBalanceTests } from "./balance/balance.test.mjs"
7
+ import { registerBroadcastTransactionTests } from "./broadcast-transaction/broadcast-transaction.test.mjs"
8
+ import { registerConfirmedLengthTests } from "./confirmed-length/confirmed-length.test.mjs"
9
+ import { registerFeeTests } from "./fee/fee.test.mjs"
10
+ import { registerTxHashesTests } from "./tx-hashes/tx-hashes.test.mjs"
11
+ import { registerTxPayloadsBulkTests } from "./tx-payloads-bulk/tx-payloads-bulk.test.mjs"
12
+ import { registerTxDetailsTests } from "./tx-details/tx-details.test.mjs"
13
+ import { registerTxTests } from "./tx/tx.test.mjs"
14
+ import { registerTxvTests } from "./txv/txv.test.mjs"
15
+ import { registerUnconfirmedLengthTests } from "./unconfirmed-length/unconfirmed-length.test.mjs"
9
16
 
10
- let msb
11
- let server
12
- let wallet
13
17
  let toClose
14
18
  let tmpDirectory
19
+ const additionalPeers = []
20
+
21
+ const testContext = {
22
+ writerMsb: null,
23
+ rpcMsb: null,
24
+ server: null,
25
+ wallet: null,
26
+ adminWallet: null,
27
+ }
15
28
 
16
29
  const setupNetwork = async () => {
17
30
  tmpDirectory = await initTemporaryDirectory()
@@ -28,296 +41,54 @@ const setupNetwork = async () => {
28
41
  store_name: '/admin'
29
42
  }
30
43
 
31
- const peer = await setupMsbAdmin(testKeyPair1, tmpDirectory, rpcOpts)
32
- const writer = await setupMsbWriter(peer, 'writer', testKeyPair2, tmpDirectory, peer.options);
33
- return { writer, peer }
44
+ const admin = await setupMsbAdmin(testKeyPair1, tmpDirectory, rpcOpts)
45
+ const writer = await setupMsbWriter(admin, 'writer', testKeyPair2, tmpDirectory, admin.options);
46
+ additionalPeers.push(writer)
47
+ const reader = await setupMsbPeer('peer-2', testKeyPair3, tmpDirectory, { ...admin.options, enable_wallet: false });
48
+ additionalPeers.push(reader)
49
+ await tryToSyncWriters(admin, writer, reader)
50
+ await waitForNodeState(reader, writer.wallet.address, {
51
+ wk: writer.msb.state.writingKey,
52
+ isWhitelisted: true,
53
+ isWriter: true,
54
+ isIndexer: false,
55
+ })
56
+ return { writer, admin, reader }
34
57
  }
35
58
 
36
59
  beforeAll(async () => {
37
- const { peer, writer } = await setupNetwork()
38
- msb = writer.msb
39
- wallet = msb.wallet
40
- server = createServer(msb)
41
- toClose = peer.msb
60
+ const { admin, writer, reader } = await setupNetwork()
61
+ const server = createServer(reader.msb)
62
+ toClose = admin.msb
63
+ Object.assign(testContext, {
64
+ writerMsb: writer.msb,
65
+ rpcMsb: reader.msb,
66
+ server,
67
+ wallet: writer.msb.wallet,
68
+ adminWallet: admin.wallet,
69
+ })
42
70
  })
43
71
 
44
72
  afterAll(async () => {
45
- await Promise.all([msb?.close(), toClose?.close()])
73
+ const peersToClose = [...new Set(additionalPeers.map(peer => peer?.msb).filter(Boolean))]
74
+ await Promise.all([
75
+ toClose?.close(),
76
+ ...peersToClose.map(instance => instance.close())
77
+ ])
46
78
  await removeTemporaryDirectory(tmpDirectory)
47
79
  })
48
80
 
49
81
  // The order here is important since the OPs change the network state. We wont boot up an instance before each because the tests are to verify rpc structure and the decision is to spare ci resources.
50
82
  describe("API acceptance tests", () => {
51
- it("GET /v1/confirmed-length", async () => {
52
- const res = await request(server).get("/v1/confirmed-length")
53
- expect(res.statusCode).toBe(200)
54
- expect(res.body).toEqual({ confirmed_length: expect.any(Number) })
55
- })
56
-
57
- it("GET /v1/unconfirmed-length", async () => {
58
- const res = await request(server).get("/v1/unconfirmed-length")
59
- expect(res.statusCode).toBe(200)
60
- expect(res.body).toEqual({ unconfirmed_length: expect.any(Number) })
61
- })
62
-
63
- it("GET /v1/txv", async () => {
64
- const res = await request(server).get("/v1/txv")
65
- expect(res.statusCode).toBe(200)
66
- expect(res.body).toEqual({ txv: expect.stringMatching(/^[a-z0-9]{64}$/) })
67
- })
68
-
69
- it("GET /v1/fee", async () => {
70
- const res = await request(server).get("/v1/fee")
71
- expect(res.statusCode).toBe(200)
72
- expect(res.body).toEqual({ fee: expect.stringMatching(/^-?\d+(\.\d+)?$/) })
73
- })
74
-
75
- describe('GET /v1/tx-hashes', () => {
76
- it("< 1000", async () => {
77
- const res = await request(server).get("/v1/tx-hashes/1/1001")
78
- expect(res.statusCode).toBe(200)
79
- expect(res.body).toEqual({
80
- hashes: expect.arrayContaining([
81
- expect.objectContaining({
82
- hash: expect.any(String),
83
- confirmed_length: expect.any(Number),
84
- })
85
- ])
86
- })
87
- })
88
-
89
- it("> 1000", async () => {
90
- const res = await request(server).get("/v1/tx-hashes/1/1002")
91
- expect(res.statusCode).toBe(400)
92
- })
93
- })
94
-
95
- it("GET /v1/balance", async () => {
96
- const res = await request(server).get(`/v1/balance/${wallet.address}`)
97
- expect(res.statusCode).toBe(200)
98
- expect(res.body).toEqual({ address: wallet.address, balance: "9670000000000000000" })
99
- })
100
-
101
- it("GET /v1/balance unconfirmed", async () => {
102
- const res = await request(server).get(`/v1/balance/${wallet.address}?confirmed=false`)
103
- expect(res.statusCode).toBe(200)
104
- expect(res.body).toEqual({ address: wallet.address, balance: "9670000000000000000" })
105
- })
106
-
107
- it("POST /v1/broadcast-transaction", async () => {
108
- const txData = await tracCrypto.transaction.preBuild(
109
- wallet.address,
110
- wallet.address,
111
- b4a.toString($TNK(1n), 'hex'),
112
- b4a.toString(await msb.state.getIndexerSequenceState(), 'hex')
113
- );
114
-
115
- const payload = tracCrypto.transaction.build(txData, b4a.from(wallet.secretKey, 'hex'));
116
- const res = await request(server)
117
- .post("/v1/broadcast-transaction")
118
- .set("Accept", "application/json")
119
- .send(JSON.stringify({ payload }))
120
-
121
- expect(res.statusCode).toBe(200)
122
- expect(res.body).toMatchObject({
123
- result: {
124
- message: "Transaction broadcasted successfully.",
125
- signedLength: expect.any(Number),
126
- unsignedLength: expect.any(Number),
127
- tx: expect.any(String)
128
- }
129
- })
130
- })
131
-
132
- it("POST /v1/tx-payloads-bulk", async () => {
133
- const result = await msb.state.confirmedTransactionsBetween(0, 40) // This is just an arbitrary range that will most likely contain valid
134
- const hashes = result.map(({ hash }) => hash)
135
-
136
- const payload = { hashes }
137
-
138
- const res = await request(server)
139
- .post("/v1/tx-payloads-bulk")
140
- .set("Accept", "application/json")
141
- .send(JSON.stringify(payload))
142
-
143
- expect(res.statusCode).toBe(200)
144
- expect(res.body).toMatchObject({
145
- results: expect.arrayOf(
146
- expect.objectContaining({
147
- hash: expect.any(String),
148
- })
149
- ),
150
- missing: []
151
- })
152
- })
153
-
154
- describe('GET /v1/tx/details', () => {
155
-
156
- it("positive case - should return 200 for valid already broadcasted hash confirmed and unconfirmed", async () => {
157
- const txData = await tracCrypto.transaction.preBuild(
158
- wallet.address,
159
- wallet.address,
160
- b4a.toString($TNK(1n), 'hex'),
161
- b4a.toString(await msb.state.getIndexerSequenceState(), 'hex')
162
- );
163
-
164
- const payload = tracCrypto.transaction.build(txData, b4a.from(wallet.secretKey, 'hex'));
165
- const broadcastRes = await request(server)
166
- .post("/v1/broadcast-transaction")
167
- .set("Accept", "application/json")
168
- .send(JSON.stringify({ payload }));
169
- expect(broadcastRes.statusCode).toBe(200);
170
-
171
- const resConfirmed = await request(server)
172
- .get(`/v1/tx/details/${txData.hash.toString('hex')}?confirmed=true`);
173
- expect(resConfirmed.statusCode).toBe(200);
174
-
175
- expect(resConfirmed.body).toMatchObject({
176
- txDetails: expect.any(Object),
177
- confirmed_length: expect.any(Number),
178
- fee: expect.any(String)
179
- })
180
-
181
- const resUnconfirmed = await request(server)
182
- .get(`/v1/tx/details/${txData.hash.toString('hex')}?confirmed=false`);
183
- expect(resUnconfirmed.statusCode).toBe(200);
184
-
185
- expect(resUnconfirmed.body).toMatchObject({
186
- txDetails: expect.any(Object),
187
- confirmed_length: expect.any(Number),
188
- fee: expect.any(String)
189
- })
190
- });
191
-
192
- it("should handle null confirmed_length for unconfirmed transaction", async () => {
193
- const txData = await tracCrypto.transaction.preBuild(
194
- wallet.address,
195
- wallet.address,
196
- b4a.toString($TNK(1n), 'hex'),
197
- b4a.toString(await msb.state.getIndexerSequenceState(), 'hex')
198
- );
199
-
200
- const payload = tracCrypto.transaction.build(txData, b4a.from(wallet.secretKey, 'hex'));
201
-
202
- const originalGetConfirmedLength = msb.state.getTransactionConfirmedLength;
203
- msb.state.getTransactionConfirmedLength = async () => null;
204
-
205
- try {
206
- const broadcastRes = await request(server)
207
- .post("/v1/broadcast-transaction")
208
- .set("Accept", "application/json")
209
- .send(JSON.stringify({ payload }));
210
- expect(broadcastRes.statusCode).toBe(200);
211
-
212
- const res = await request(server)
213
- .get(`/v1/tx/details/${txData.hash.toString('hex')}?confirmed=false`);
214
- expect(res.statusCode).toBe(200);
215
-
216
- expect(res.body).toMatchObject({
217
- txDetails: expect.any(Object),
218
- confirmed_length: 0,
219
- fee: '0'
220
- });
221
- } finally {
222
- msb.state.getTransactionConfirmedLength = originalGetConfirmedLength;
223
- }
224
- });
225
-
226
- it("should return 404 for non-existent transaction hash", async () => {
227
- const nonExistentHash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c";
228
- const res = await request(server)
229
- .get(`/v1/tx/details/${nonExistentHash}`);
230
-
231
- expect(res.statusCode).toBe(404);
232
- expect(res.body).toEqual({
233
- error: `No payload found for tx hash: ${nonExistentHash}`
234
- });
235
- });
236
-
237
- it("should return 400 for invalid hash format (too short)", async () => {
238
- const invalidHash = '0'.repeat(63);
239
- const res = await request(server)
240
- .get(`/v1/tx/details/${invalidHash}`);
241
-
242
- expect(res.statusCode).toBe(400);
243
- expect(res.body).toEqual({
244
- error: "Invalid transaction hash format"
245
- });
246
- });
247
-
248
- it("should return 400 for invalid hash format (non-hex)", async () => {
249
- const invalidHash = 'Z'.repeat(64);
250
- const res = await request(server)
251
- .get(`/v1/tx/details/${invalidHash}`);
252
-
253
- expect(res.statusCode).toBe(400);
254
- expect(res.body).toEqual({
255
- error: "Invalid transaction hash format"
256
- });
257
- });
258
-
259
- it("should return 400 for invalid confirmed parameter", async () => {
260
- const hash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c";
261
-
262
- const res = await request(server)
263
- .get(`/v1/tx/details/${hash}?confirmed=invalid`);
264
-
265
- expect(res.statusCode).toBe(400);
266
- expect(res.body).toEqual({
267
- error: 'Parameter "confirmed" must be exactly "true" or "false"'
268
- });
269
- });
270
-
271
- it("should return 400 for invalid confirmed parameter case (UPPERCASE)", async () => {
272
- const hash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c";
273
- const res = await request(server).get(`/v1/tx/details/${hash}?confirmed=TRUE`);
274
- expect(res.statusCode).toBe(400);
275
- expect(res.body).toEqual({
276
- error: 'Parameter "confirmed" must be exactly "true" or "false"'
277
- });
278
- });
279
-
280
- it("should return 400 when no hash provided", async () => {
281
- const res = await request(server)
282
- .get('/v1/tx/details');
283
-
284
- expect(res.statusCode).toBe(400);
285
- expect(res.body).toEqual({
286
- error: "Transaction hash is required"
287
- });
288
- });
289
-
290
- it("should return 400 for hash with invalid characters", async () => {
291
- const invalidHash = '0b4d1c1dac48$af13212f6166017399457476a0b644850875b7f4b79df6ff89c';
292
- const res = await request(server)
293
- .get(`/v1/tx/details/${invalidHash}`);
294
-
295
- expect(res.statusCode).toBe(400);
296
- expect(res.body).toEqual({
297
- error: "Invalid transaction hash format"
298
- });
299
- });
300
-
301
- it("should return 400 for hash with special characters", async () => {
302
- const invalidHash = '!@#$%^&*'.repeat(8);
303
- const res = await request(server)
304
- .get(`/v1/tx/details/${invalidHash}`);
305
-
306
- expect(res.statusCode).toBe(400);
307
- expect(res.body).toEqual({
308
- error: "Invalid transaction hash format"
309
- });
310
- });
311
-
312
- it("should return 400 for hash with spaces", async () => {
313
- const invalidHash = '0b4d1c1dac48af13212f616601d7399457476a0b644850875b7 4b79df6ff89c';
314
- const res = await request(server)
315
- .get(`/v1/tx/details/${invalidHash}`);
316
-
317
- expect(res.statusCode).toBe(400);
318
- expect(res.body).toEqual({
319
- error: "Invalid transaction hash format"
320
- });
321
- });
322
- })
323
- })
83
+ registerConfirmedLengthTests(testContext)
84
+ registerUnconfirmedLengthTests(testContext)
85
+ registerTxvTests(testContext)
86
+ registerFeeTests(testContext)
87
+ registerTxHashesTests(testContext)
88
+ registerTxTests(testContext)
89
+ registerBalanceTests(testContext)
90
+ registerBroadcastTransactionTests(testContext)
91
+ registerTxPayloadsBulkTests(testContext)
92
+ registerTxDetailsTests(testContext)
93
+ registerAccountTests(testContext)
94
+ })
@@ -0,0 +1,98 @@
1
+ import request from "supertest"
2
+ import { buildRpcSelfTransferPayload } from "../../../helpers/transactionPayloads.mjs"
3
+
4
+ export const registerTxTests = (context) => {
5
+ describe("GET /v1/tx/:hash", () => {
6
+ it("returns tx details for a broadcasted transaction", async () => {
7
+ const { payload, txHashHex } = await buildRpcSelfTransferPayload(
8
+ context.wallet,
9
+ context.rpcMsb.state,
10
+ 1n
11
+ );
12
+ const broadcastRes = await request(context.server)
13
+ .post("/v1/broadcast-transaction")
14
+ .set("Accept", "application/json")
15
+ .send(JSON.stringify({ payload }))
16
+ expect(broadcastRes.statusCode).toBe(200)
17
+
18
+ const res = await request(context.server).get(`/v1/tx/${txHashHex}`)
19
+ expect(res.statusCode).toBe(200)
20
+ expect(res.body).toMatchObject({ txDetails: expect.any(Object) })
21
+ })
22
+
23
+ it("returns 404 for non-existent transaction hash", async () => {
24
+ const nonExistentHash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c"
25
+ const res = await request(context.server).get(`/v1/tx/${nonExistentHash}`)
26
+ expect(res.statusCode).toBe(404)
27
+ expect(res.body).toEqual({ txDetails: null })
28
+ })
29
+
30
+ // TODO: adjust implementation to cover tests below
31
+ it.skip("returns 400 for invalid hash format (too short)", async () => {
32
+ const invalidHash = '0'.repeat(63)
33
+ const res = await request(context.server).get(`/v1/tx/${invalidHash}`)
34
+ expect(res.statusCode).toBe(400)
35
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
36
+ })
37
+
38
+ it.skip("returns 400 for invalid hash format (non-hex)", async () => {
39
+ const invalidHash = 'Z'.repeat(64)
40
+ const res = await request(context.server).get(`/v1/tx/${invalidHash}`)
41
+ expect(res.statusCode).toBe(400)
42
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
43
+ })
44
+
45
+ it.skip("returns 400 when no hash provided", async () => {
46
+ const res = await request(context.server).get('/v1/tx')
47
+ expect(res.statusCode).toBe(400)
48
+ expect(res.body).toEqual({ error: "Transaction hash is required" })
49
+ })
50
+
51
+ it.skip("returns 400 for hash with invalid characters", async () => {
52
+ const invalidHash = '0b4d1c1dac48$af13212f6166017399457476a0b644850875b7f4b79df6ff89c'
53
+ const res = await request(context.server).get(`/v1/tx/${invalidHash}`)
54
+ expect(res.statusCode).toBe(400)
55
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
56
+ })
57
+
58
+ it.skip("returns 400 for hash with special characters", async () => {
59
+ const invalidHash = '!@#$%^&*'.repeat(8)
60
+ const res = await request(context.server).get(`/v1/tx/${invalidHash}`)
61
+ expect(res.statusCode).toBe(400)
62
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
63
+ })
64
+
65
+ it.skip("returns 400 for hash with spaces", async () => {
66
+ const invalidHash = '0b4d1c1dac48af13212f616601d7399457476a0b644850875b7 4b79df6ff89c'
67
+ const res = await request(context.server).get(`/v1/tx/${invalidHash}`)
68
+ expect(res.statusCode).toBe(400)
69
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
70
+ })
71
+
72
+ it.skip("returns 400 for hash with 0x prefix", async () => {
73
+ const hash = "0x" + "0".repeat(62)
74
+ const res = await request(context.server).get(`/v1/tx/${hash}`)
75
+ expect(res.statusCode).toBe(400)
76
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
77
+ })
78
+
79
+ it.skip("returns 400 for odd-length hex", async () => {
80
+ const hash = "a".repeat(63)
81
+ const res = await request(context.server).get(`/v1/tx/${hash}`)
82
+ expect(res.statusCode).toBe(400)
83
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
84
+ })
85
+
86
+ it.skip("accepts uppercase hex", async () => {
87
+ const hash = "A".repeat(64)
88
+ const res = await request(context.server).get(`/v1/tx/${hash}`)
89
+ expect(res.statusCode).toBe(200)
90
+ })
91
+
92
+ it.skip("returns 400 for trailing space hash", async () => {
93
+ const hash = `${"a".repeat(64)} `
94
+ const res = await request(context.server).get(`/v1/tx/${hash}`)
95
+ expect(res.statusCode).toBe(400)
96
+ })
97
+ })
98
+ }
@@ -0,0 +1,195 @@
1
+ import request from "supertest"
2
+ import { buildRpcSelfTransferPayload } from "../../../helpers/transactionPayloads.mjs"
3
+
4
+ export const registerTxDetailsTests = (context) => {
5
+ describe("GET /v1/tx/details", () => {
6
+ it("returns 200 for broadcasted hash (confirmed and unconfirmed)", async () => {
7
+ const { payload, txHashHex } = await buildRpcSelfTransferPayload(
8
+ context.wallet,
9
+ context.rpcMsb.state,
10
+ 1n
11
+ );
12
+ const broadcastRes = await request(context.server)
13
+ .post("/v1/broadcast-transaction")
14
+ .set("Accept", "application/json")
15
+ .send(JSON.stringify({ payload }))
16
+ expect(broadcastRes.statusCode).toBe(200)
17
+
18
+ const resConfirmed = await request(context.server)
19
+ .get(`/v1/tx/details/${txHashHex}?confirmed=true`)
20
+ expect(resConfirmed.statusCode).toBe(200)
21
+
22
+ expect(resConfirmed.body).toMatchObject({
23
+ txDetails: expect.any(Object),
24
+ confirmed_length: expect.any(Number),
25
+ fee: expect.any(String)
26
+ })
27
+
28
+ const resUnconfirmed = await request(context.server)
29
+ .get(`/v1/tx/details/${txHashHex}?confirmed=false`)
30
+ expect(resUnconfirmed.statusCode).toBe(200)
31
+
32
+ expect(resUnconfirmed.body).toMatchObject({
33
+ txDetails: expect.any(Object),
34
+ confirmed_length: expect.any(Number),
35
+ fee: expect.any(String)
36
+ })
37
+ })
38
+
39
+ it("handles null confirmed_length for unconfirmed transaction", async () => {
40
+ const { payload, txHashHex } = await buildRpcSelfTransferPayload(
41
+ context.wallet,
42
+ context.rpcMsb.state,
43
+ 1n
44
+ );
45
+
46
+ const originalGetConfirmedLength = context.rpcMsb.state.getTransactionConfirmedLength
47
+ context.rpcMsb.state.getTransactionConfirmedLength = async () => null
48
+
49
+ try {
50
+ const broadcastRes = await request(context.server)
51
+ .post("/v1/broadcast-transaction")
52
+ .set("Accept", "application/json")
53
+ .send(JSON.stringify({ payload }))
54
+ expect(broadcastRes.statusCode).toBe(200)
55
+
56
+ const res = await request(context.server)
57
+ .get(`/v1/tx/details/${txHashHex}?confirmed=false`)
58
+ expect(res.statusCode).toBe(200)
59
+
60
+ expect(res.body).toMatchObject({
61
+ txDetails: expect.any(Object),
62
+ confirmed_length: 0,
63
+ fee: '0'
64
+ })
65
+ } finally {
66
+ context.rpcMsb.state.getTransactionConfirmedLength = originalGetConfirmedLength
67
+ }
68
+ })
69
+
70
+ it("returns 404 for non-existent transaction hash", async () => {
71
+ const nonExistentHash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c"
72
+ const res = await request(context.server)
73
+ .get(`/v1/tx/details/${nonExistentHash}`)
74
+
75
+ expect(res.statusCode).toBe(404)
76
+ expect(res.body).toEqual({
77
+ error: `No payload found for tx hash: ${nonExistentHash}`
78
+ })
79
+ })
80
+
81
+ it("returns 400 for invalid hash format (too short)", async () => {
82
+ const invalidHash = '0'.repeat(63)
83
+ const res = await request(context.server)
84
+ .get(`/v1/tx/details/${invalidHash}`)
85
+
86
+ expect(res.statusCode).toBe(400)
87
+ expect(res.body).toEqual({
88
+ error: "Invalid transaction hash format"
89
+ })
90
+ })
91
+
92
+ it("returns 400 for invalid hash format (non-hex)", async () => {
93
+ const invalidHash = 'Z'.repeat(64)
94
+ const res = await request(context.server)
95
+ .get(`/v1/tx/details/${invalidHash}`)
96
+
97
+ expect(res.statusCode).toBe(400)
98
+ expect(res.body).toEqual({
99
+ error: "Invalid transaction hash format"
100
+ })
101
+ })
102
+
103
+ it("returns 400 for invalid confirmed parameter", async () => {
104
+ const hash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c"
105
+
106
+ const res = await request(context.server)
107
+ .get(`/v1/tx/details/${hash}?confirmed=invalid`)
108
+
109
+ expect(res.statusCode).toBe(400)
110
+ expect(res.body).toEqual({
111
+ error: 'Parameter "confirmed" must be exactly "true" or "false"'
112
+ })
113
+ })
114
+
115
+ it("returns 400 for invalid confirmed parameter case (UPPERCASE)", async () => {
116
+ const hash = "0b4d1c1dac48af13212f616601d7399457476a0b644850875b7f4b79df6ff89c"
117
+ const res = await request(context.server).get(`/v1/tx/details/${hash}?confirmed=TRUE`)
118
+ expect(res.statusCode).toBe(400)
119
+ expect(res.body).toEqual({
120
+ error: 'Parameter "confirmed" must be exactly "true" or "false"'
121
+ })
122
+ })
123
+
124
+ it("returns 400 when no hash provided", async () => {
125
+ const res = await request(context.server)
126
+ .get('/v1/tx/details')
127
+
128
+ expect(res.statusCode).toBe(400)
129
+ expect(res.body).toEqual({
130
+ error: "Transaction hash is required"
131
+ })
132
+ })
133
+
134
+ it("returns 400 for hash with invalid characters", async () => {
135
+ const invalidHash = '0b4d1c1dac48$af13212f6166017399457476a0b644850875b7f4b79df6ff89c'
136
+ const res = await request(context.server)
137
+ .get(`/v1/tx/details/${invalidHash}`)
138
+
139
+ expect(res.statusCode).toBe(400)
140
+ expect(res.body).toEqual({
141
+ error: "Invalid transaction hash format"
142
+ })
143
+ })
144
+
145
+ it("returns 400 for hash with special characters", async () => {
146
+ const invalidHash = '!@#$%^&*'.repeat(8)
147
+ const res = await request(context.server)
148
+ .get(`/v1/tx/details/${invalidHash}`)
149
+
150
+ expect(res.statusCode).toBe(400)
151
+ expect(res.body).toEqual({
152
+ error: "Invalid transaction hash format"
153
+ })
154
+ })
155
+
156
+ it("returns 400 for hash with spaces", async () => {
157
+ const invalidHash = '0b4d1c1dac48af13212f616601d7399457476a0b644850875b7 4b79df6ff89c'
158
+ const res = await request(context.server)
159
+ .get(`/v1/tx/details/${invalidHash}`)
160
+
161
+ expect(res.statusCode).toBe(400)
162
+ expect(res.body).toEqual({
163
+ error: "Invalid transaction hash format"
164
+ })
165
+ })
166
+
167
+ it("returns 400 for hash with 0x prefix", async () => {
168
+ const hash = "0x" + "0".repeat(62)
169
+ const res = await request(context.server).get(`/v1/tx/details/${hash}`)
170
+ expect(res.statusCode).toBe(400)
171
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
172
+ })
173
+
174
+ it("returns 400 for odd-length hex", async () => {
175
+ const hash = "a".repeat(63)
176
+ const res = await request(context.server).get(`/v1/tx/details/${hash}`)
177
+ expect(res.statusCode).toBe(400)
178
+ expect(res.body).toEqual({ error: "Invalid transaction hash format" })
179
+ })
180
+
181
+ // TODOadjust implementation to cover tests below
182
+ it.skip("accepts uppercase hex", async () => {
183
+ const hash = "A".repeat(64)
184
+ const res = await request(context.server).get(`/v1/tx/details/${hash}?confirmed=false`)
185
+ expect([200]).toContain(res.statusCode)
186
+ expect(res.statusCode).toBe(200)
187
+ })
188
+
189
+ it.skip("returns 400 for trailing space hash", async () => {
190
+ const hash = `${"a".repeat(64)} `
191
+ const res = await request(context.server).get(`/v1/tx/details/${hash}`)
192
+ expect(res.statusCode).toBe(400)
193
+ })
194
+ })
195
+ }