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
package/rpc/handlers.mjs CHANGED
@@ -1,47 +1,54 @@
1
1
  import { decodeBase64Payload, isBase64, sanitizeBulkPayloadsRequestBody, sanitizeTransferPayload, validatePayloadStructure } from "./utils/helpers.mjs"
2
- import { MAX_SIGNED_LENGTH } from "./constants.mjs";
2
+ import { MAX_SIGNED_LENGTH, ZERO_WK } from "./constants.mjs";
3
+ import { buildRequestUrl } from "./utils/url.mjs";
3
4
  import { isHexString } from "../src/utils/helpers.js";
5
+ import {
6
+ getBalance,
7
+ getTxv,
8
+ getFee,
9
+ getConfirmedLength,
10
+ getUnconfirmedLength,
11
+ broadcastTransaction,
12
+ getTxHashes,
13
+ getTxDetails,
14
+ fetchBulkTxPayloads,
15
+ getExtendedTxDetails
16
+ } from "./rpc_services.js";
17
+ import { bufferToBigInt, licenseBufferToBigInt } from "../src/utils/amountSerialization.js";
18
+ import { isAddressValid } from "../src/core/state/utils/address.js";
19
+ import { getConfirmedParameter } from "./utils/confirmedParameter.mjs";
4
20
 
5
21
  export async function handleBalance({ req, respond, msbInstance }) {
6
- const [path, queryString] = req.url.split("?");
7
- const parts = path.split("/").filter(Boolean);
22
+ const url = buildRequestUrl(req);
23
+ const parts = url.pathname.split("/").filter(Boolean);
8
24
  const address = parts[2];
9
25
 
10
- let confirmed = true; // default
11
- if (queryString) {
12
- const params = new URLSearchParams(queryString);
13
- if (params.has("confirmed")) {
14
- confirmed = params.get("confirmed") === "true";
15
- }
16
- }
26
+ const confirmedParam = getConfirmedParameter(url);
27
+ const confirmed = confirmedParam === null ? false : confirmedParam; // invalid -> fallback to unconfirmed
17
28
 
29
+ // TODO: VALIDATION?
18
30
  if (!address) {
19
31
  respond(400, { error: 'Wallet address is required' });
20
32
  return;
21
33
  }
22
-
23
- const commandString =`/get_balance ${address} ${confirmed}`;
24
- const nodeInfo = await msbInstance.handleCommand(commandString);
34
+
35
+ const nodeInfo = await getBalance(msbInstance, address, confirmed);
25
36
  const balance = nodeInfo?.balance || 0;
26
37
  respond(200, { address, balance });
27
38
  }
28
39
 
29
40
  export async function handleTxv({ msbInstance, respond }) {
30
- const commandString = '/get_txv';
31
- const txvRaw = await msbInstance.handleCommand(commandString);
32
- const txv = txvRaw.toString('hex');
41
+ const txv = await getTxv(msbInstance);
33
42
  respond(200, { txv });
34
43
  }
35
44
 
36
45
  export async function handleFee({ msbInstance, respond }) {
37
- const commandString = '/get_fee';
38
- const fee = await msbInstance.handleCommand(commandString);
46
+ const fee = await getFee(msbInstance);
39
47
  respond(200, { fee });
40
48
  }
41
49
 
42
50
  export async function handleConfirmedLength({ msbInstance, respond }) {
43
- const commandString = '/confirmed_length';
44
- const confirmed_length = await msbInstance.handleCommand(commandString);
51
+ const confirmed_length = await getConfirmedLength(msbInstance);
45
52
  respond(200, { confirmed_length });
46
53
  }
47
54
 
@@ -65,20 +72,20 @@ export async function handleBroadcastTransaction({ msbInstance, respond, req })
65
72
  const decodedPayload = decodeBase64Payload(payload);
66
73
  validatePayloadStructure(decodedPayload);
67
74
  const sanitizedPayload = sanitizeTransferPayload(decodedPayload);
68
- const result = await msbInstance.handleCommand('/broadcast_transaction', null, sanitizedPayload);
75
+ const result = await broadcastTransaction(msbInstance, sanitizedPayload);
69
76
  respond(200, { result });
70
77
  } catch (error) {
71
78
  let code = error instanceof SyntaxError ? 400 : 500;
72
79
  let errorMsg = code === 400 ? 'Invalid JSON payload.' : 'An error occurred processing the transaction.'
73
80
 
74
- if(error.message.includes("Failed to broadcast transaction after multiple attempts.")){
81
+ if (error.message.includes("Failed to broadcast transaction after multiple attempts.")) {
75
82
  code = 429;
76
83
  errorMsg = "Failed to broadcast transaction after multiple attempts."
77
84
  }
78
-
85
+
79
86
  console.error('Error in handleBroadcastTransaction:', error);
80
87
  // Use 400 for client errors (like bad JSON), 500 for server/command errors
81
- respond(code, { error: errorMsg});
88
+ respond(code, { error: errorMsg });
82
89
  }
83
90
  });
84
91
 
@@ -89,8 +96,10 @@ export async function handleBroadcastTransaction({ msbInstance, respond, req })
89
96
  }
90
97
 
91
98
  export async function handleTxHashes({ msbInstance, respond, req }) {
92
- const startSignedLengthStr = req.url.split('/')[3];
93
- const endSignedLengthStr = req.url.split('/')[4];
99
+ const url = buildRequestUrl(req);
100
+ const pathParts = url.pathname.split('/').filter(Boolean);
101
+ const startSignedLengthStr = pathParts[2];
102
+ const endSignedLengthStr = pathParts[3];
94
103
 
95
104
  const startSignedLength = parseInt(startSignedLengthStr);
96
105
  const endSignedLength = parseInt(endSignedLengthStr);
@@ -116,27 +125,24 @@ export async function handleTxHashes({ msbInstance, respond, req }) {
116
125
  }
117
126
 
118
127
  // 4. Get current confirmed length
119
- const currentConfirmedLength = await msbInstance.handleCommand('/confirmed_length');
128
+ const currentConfirmedLength = await getConfirmedLength(msbInstance);
120
129
 
121
130
  // 5. Adjust the end index to not exceed the confirmed length.
122
131
  const adjustedEndLength = Math.min(endSignedLength, currentConfirmedLength)
123
-
132
+
124
133
  // 6. Fetch txs hashes for the adjusted range, assuming the command takes start and end index.
125
- const commandString = `/get_txs_hashes ${startSignedLength} ${adjustedEndLength}`;
126
- const { hashes } = await msbInstance.handleCommand(commandString);
134
+ const { hashes } = await getTxHashes(msbInstance, startSignedLength, adjustedEndLength);
127
135
  respond(200, { hashes });
128
136
  }
129
137
 
130
138
  export async function handleUnconfirmedLength({ msbInstance, respond }) {
131
- const commandString = '/unconfirmed_length';
132
- const unconfirmed_length = await msbInstance.handleCommand(commandString);
139
+ const unconfirmed_length = await getUnconfirmedLength(msbInstance);
133
140
  respond(200, { unconfirmed_length });
134
141
  }
135
142
 
136
143
  export async function handleTransactionDetails({ msbInstance, respond, req }) {
137
144
  const hash = req.url.split('/')[3];
138
- const commandString = `/get_tx_details ${hash}`;
139
- const txDetails = await msbInstance.handleCommand(commandString);
145
+ const txDetails = await getTxDetails(msbInstance, hash);
140
146
  respond(txDetails === null ? 404 : 200 , { txDetails });
141
147
  }
142
148
 
@@ -150,9 +156,9 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
150
156
  if (headersSent) return; // Stop processing if response has started/errored
151
157
 
152
158
  bytesRead += chunk.length;
153
- if (bytesRead > limitBytes) {
159
+ if (bytesRead > limitBytes) {
154
160
  respond(413, { error: 'Request body too large.' });
155
- headersSent = true;
161
+ headersSent = true;
156
162
  req.destroy(); // Stop receiving data (GOOD PRACTICE)
157
163
  return;
158
164
  }
@@ -164,16 +170,16 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
164
170
 
165
171
 
166
172
  try {
167
- if (body === null || body === ''){
173
+ if (body === null || body === '') {
168
174
  return respond(400, { error: 'Missing payload.' });
169
175
  }
170
176
 
171
177
  const sanitizedPayload = sanitizeBulkPayloadsRequestBody(body);
172
178
 
173
- if (sanitizedPayload === null){
179
+ if (sanitizedPayload === null) {
174
180
  return respond(400, { error: 'Invalid payload.' });
175
181
  }
176
-
182
+
177
183
  const { hashes } = sanitizedPayload;
178
184
 
179
185
  if (!Array.isArray(hashes) || hashes.length === 0) {
@@ -186,11 +192,11 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
186
192
 
187
193
  const uniqueHashes = [...new Set(hashes)];
188
194
 
189
- const commandResult = await msbInstance.handleCommand( `/get_tx_payloads_bulk`, null, uniqueHashes)
195
+ const commandResult = await fetchBulkTxPayloads(msbInstance, uniqueHashes);
190
196
 
191
197
  const responseString = JSON.stringify(commandResult);
192
198
  if (Buffer.byteLength(responseString, 'utf8') > 2_000_000) {
193
- return respond(413, { error: 'Response too large. Reduce number of hashes.'});
199
+ return respond(413, { error: 'Response too large. Reduce number of hashes.' });
194
200
  }
195
201
 
196
202
  return respond(200, commandResult);
@@ -209,38 +215,26 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
209
215
  }
210
216
 
211
217
  export async function handleTransactionExtendedDetails({ msbInstance, respond, req }) {
212
- const [path, queryString] = req.url.split("?");
213
- const pathParts = path.split('/');
214
- const hash = pathParts[4];
215
-
218
+ const url = buildRequestUrl(req);
219
+ const pathParts = url.pathname.split('/').filter(Boolean);
220
+ const hash = pathParts[3];
221
+
216
222
  if (!hash) {
217
223
  return respond(400, { error: "Transaction hash is required" });
218
224
  }
219
-
225
+
220
226
  if (isHexString(hash) === false || hash.length !== 64) {
221
227
  return respond(400, { error: "Invalid transaction hash format" });
222
228
  }
223
- let confirmed = true; // default
224
- if (queryString) {
225
- const params = new URLSearchParams(queryString);
226
- if (params.has("confirmed")) {
227
- const confirmedParam = params.get("confirmed");
228
- if (confirmedParam !== "true" && confirmedParam !== "false") {
229
- return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
230
- }
231
- confirmed = confirmedParam === "true";
232
- }
229
+
230
+ const confirmed = getConfirmedParameter(url);
231
+ if (confirmed === null) {
232
+ return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
233
233
  }
234
-
234
+
235
235
  try {
236
- let txDetails;
237
- const commandString = `/get_extended_tx_details ${hash} ${confirmed}`;
238
- txDetails = await msbInstance.handleCommand(commandString);
239
- if (txDetails === null) {
240
- respond(404, { error: `No payload found for tx hash: ${hash}` });
241
- } else {
242
- respond(200, txDetails);
243
- }
236
+ const details = await getExtendedTxDetails(msbInstance, hash, confirmed);
237
+ respond(200, details);
244
238
  } catch (error) {
245
239
  if (error.message?.includes('No payload found for tx hash')) {
246
240
  respond(404, { error: error.message });
@@ -249,4 +243,53 @@ export async function handleTransactionExtendedDetails({ msbInstance, respond, r
249
243
  respond(500, { error: 'An error occurred processing the request.' });
250
244
  }
251
245
  }
252
- }
246
+ }
247
+
248
+ export async function handleAccountDetails({ msbInstance, respond, req }) {
249
+ const url = buildRequestUrl(req);
250
+ const address = url.pathname.split('/').filter(Boolean)[2];
251
+
252
+ if (!address) {
253
+ return respond(400, { error: "Account address is required" });
254
+ }
255
+
256
+ const confirmed = getConfirmedParameter(url);
257
+ if (confirmed === null) {
258
+ return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
259
+ }
260
+
261
+ if (!isAddressValid(address)) {
262
+ return respond(400, { error: "Invalid account address format" });
263
+ }
264
+
265
+ const defaultAccountState = {
266
+ address,
267
+ writingKey: ZERO_WK.toString('hex'),
268
+ isWhitelisted: false,
269
+ isValidator: false,
270
+ isIndexer: false,
271
+ license: null,
272
+ balance: '0',
273
+ stakedBalance: '0',
274
+ };
275
+
276
+ const nodeEntry = confirmed
277
+ ? await msbInstance.state.getNodeEntry(address)
278
+ : await msbInstance.state.getNodeEntryUnsigned(address);
279
+ if (!nodeEntry) {
280
+ return respond(200, defaultAccountState);
281
+ }
282
+
283
+ const licenseValue = licenseBufferToBigInt(nodeEntry.license);
284
+
285
+ return respond(200, {
286
+ ...defaultAccountState,
287
+ writingKey: nodeEntry.wk.toString('hex'),
288
+ isWhitelisted: nodeEntry.isWhitelisted,
289
+ isValidator: nodeEntry.isWriter,
290
+ isIndexer: nodeEntry.isIndexer,
291
+ license: licenseValue === 0n ? null : licenseValue.toString(),
292
+ balance: bufferToBigInt(nodeEntry.balance).toString(),
293
+ stakedBalance: bufferToBigInt(nodeEntry.stakedBalance).toString(),
294
+ });
295
+ }
package/rpc/routes/v1.mjs CHANGED
@@ -8,7 +8,8 @@ import {
8
8
  handleUnconfirmedLength,
9
9
  handleTransactionDetails,
10
10
  handleFetchBulkTxPayloads,
11
- handleTransactionExtendedDetails
11
+ handleTransactionExtendedDetails,
12
+ handleAccountDetails
12
13
  } from '../handlers.mjs';
13
14
 
14
15
  export const v1Routes = [
@@ -22,4 +23,5 @@ export const v1Routes = [
22
23
  { method: 'GET', path: '/tx', handler: handleTransactionDetails },
23
24
  { method: 'POST', path: '/tx-payloads-bulk', handler: handleFetchBulkTxPayloads },
24
25
  { method: 'GET', path: '/tx/details', handler: handleTransactionExtendedDetails },
26
+ { method: 'GET', path: '/account', handler: handleAccountDetails },
25
27
  ];
@@ -0,0 +1,126 @@
1
+ import { bufferToBigInt } from "../src/utils/amountSerialization.js";
2
+ import { normalizeDecodedPayloadForJson } from "../src/utils/normalizers.js";
3
+ import { get_confirmed_tx_info, get_unconfirmed_tx_info } from "../src/utils/cli.js";
4
+
5
+ export async function getBalance(msbInstance, address, confirmed) {
6
+ const state = msbInstance.state;
7
+ const useUnconfirmed = confirmed === false;
8
+
9
+ const nodeEntry = useUnconfirmed
10
+ ? await state.getNodeEntryUnsigned(address)
11
+ : await state.getNodeEntry(address);
12
+
13
+ if (!nodeEntry) return undefined;
14
+
15
+ return {
16
+ address,
17
+ balance: bufferToBigInt(nodeEntry.balance).toString(),
18
+ };
19
+ }
20
+
21
+ export async function getTxv(msbInstance) {
22
+ const txv = await msbInstance.state.getIndexerSequenceState();
23
+ return txv.toString("hex");
24
+ }
25
+
26
+ export async function getFee(msbInstance) {
27
+ const feeBuffer = msbInstance.state.getFee();
28
+ return bufferToBigInt(feeBuffer).toString();
29
+ }
30
+
31
+ export async function getConfirmedLength(msbInstance) {
32
+ return msbInstance.state.getSignedLength();
33
+ }
34
+
35
+ export async function getUnconfirmedLength(msbInstance) {
36
+ return msbInstance.state.getUnsignedLength();
37
+ }
38
+
39
+ export async function broadcastTransaction(msbInstance, payload) {
40
+ return msbInstance.broadcastTransactionCommand(payload);
41
+ }
42
+
43
+ export async function getTxHashes(msbInstance, start, end) {
44
+ const hashes = await msbInstance.state.confirmedTransactionsBetween(start, end);
45
+ return { hashes };
46
+ }
47
+
48
+ export async function getTxDetails(msbInstance, hash) {
49
+ const rawPayload = await get_confirmed_tx_info(msbInstance.state, hash);
50
+ if (!rawPayload) {
51
+ return null;
52
+ }
53
+
54
+ return normalizeDecodedPayloadForJson(rawPayload.decoded);
55
+ }
56
+
57
+ export async function fetchBulkTxPayloads(msbInstance, hashes) {
58
+ if (!Array.isArray(hashes) || hashes.length === 0) {
59
+ throw new Error("Missing hash list.");
60
+ }
61
+
62
+ if (hashes.length > 1500) {
63
+ throw new Error("Length of input tx hashes exceeded.");
64
+ }
65
+
66
+ const res = { results: [], missing: [] };
67
+
68
+ const promises = hashes.map((hash) => get_confirmed_tx_info(msbInstance.state, hash));
69
+ const results = await Promise.all(promises);
70
+
71
+ results.forEach((result, index) => {
72
+ const hash = hashes[index];
73
+ if (result === null || result === undefined) {
74
+ res.missing.push(hash);
75
+ } else {
76
+ const decodedResult = normalizeDecodedPayloadForJson(result.decoded);
77
+ res.results.push({ hash, payload: decodedResult });
78
+ }
79
+ });
80
+
81
+ return res;
82
+ }
83
+
84
+ export async function getExtendedTxDetails(msbInstance, hash, confirmed) {
85
+ const state = msbInstance.state;
86
+
87
+ if (confirmed) {
88
+ const rawPayload = await get_confirmed_tx_info(state, hash);
89
+ if (!rawPayload) {
90
+ throw new Error(`No payload found for tx hash: ${hash}`);
91
+ }
92
+ const confirmedLength = await state.getTransactionConfirmedLength(hash);
93
+ if (confirmedLength === null) {
94
+ throw new Error(`No confirmed length found for tx hash: ${hash} in confirmed mode`);
95
+ }
96
+ const normalizedPayload = normalizeDecodedPayloadForJson(rawPayload.decoded, true);
97
+ const feeBuffer = state.getFee();
98
+ return {
99
+ txDetails: normalizedPayload,
100
+ confirmed_length: confirmedLength,
101
+ fee: bufferToBigInt(feeBuffer).toString(),
102
+ };
103
+ }
104
+
105
+ const rawPayload = await get_unconfirmed_tx_info(state, hash);
106
+ if (!rawPayload) {
107
+ throw new Error(`No payload found for tx hash: ${hash}`);
108
+ }
109
+
110
+ const normalizedPayload = normalizeDecodedPayloadForJson(rawPayload.decoded, true);
111
+ const length = await state.getTransactionConfirmedLength(hash);
112
+ if (length === null) {
113
+ return {
114
+ txDetails: normalizedPayload,
115
+ confirmed_length: 0,
116
+ fee: "0",
117
+ };
118
+ }
119
+
120
+ const feeBuffer = state.getFee();
121
+ return {
122
+ txDetails: normalizedPayload,
123
+ confirmed_length: length,
124
+ fee: bufferToBigInt(feeBuffer).toString(),
125
+ };
126
+ }
@@ -0,0 +1,17 @@
1
+ export function getConfirmedParameter(url, { defaultValue = true } = {}) {
2
+ const confirmedParam = url.searchParams.get("confirmed");
3
+
4
+ if (confirmedParam === null) {
5
+ return defaultValue;
6
+ }
7
+
8
+ if (confirmedParam === "true") {
9
+ return true;
10
+ }
11
+
12
+ if (confirmedParam === "false") {
13
+ return false;
14
+ }
15
+
16
+ return null;
17
+ }
@@ -0,0 +1,38 @@
1
+ const HOST_HEADER_PATTERN = /^[\[\]A-Za-z0-9:.-]+(:\d{1,5})?$/;
2
+
3
+ const isValidHostHeader = (hostHeader) => {
4
+ if (!hostHeader) return false;
5
+ if (hostHeader.length > 255) return false;
6
+ const trimmed = hostHeader.trim();
7
+
8
+ if (/[\/\\\s]/.test(trimmed)) return false;
9
+
10
+ return HOST_HEADER_PATTERN.test(trimmed);
11
+ };
12
+
13
+ const socketAddressToHost = (address, port) => {
14
+ if (!address) return null;
15
+ const needsBrackets = address.includes(':') && !address.startsWith('[');
16
+ const host = needsBrackets ? `[${address}]` : address;
17
+ return port ? `${host}:${port}` : host;
18
+ };
19
+
20
+ const detectProtocol = (req) => {
21
+ const forwardedProto = req?.headers?.['x-forwarded-proto'];
22
+ if (forwardedProto === 'https') return 'https';
23
+ if (forwardedProto === 'http') return 'http';
24
+
25
+ if (req?.socket?.encrypted) return 'https';
26
+
27
+ return 'http';
28
+ };
29
+
30
+ export const buildRequestUrl = (req) => {
31
+ const safeHost = isValidHostHeader(req?.headers?.host)
32
+ ? req.headers.host.trim()
33
+ : socketAddressToHost(req?.socket?.localAddress, req?.socket?.localPort);
34
+
35
+ const protocol = detectProtocol(req);
36
+ const base = safeHost ? `${protocol}://${safeHost}` : `${protocol}://localhost`;
37
+ return new URL(req?.url || '/', base);
38
+ };
@@ -17,7 +17,7 @@ import {
17
17
  DHT_BOOTSTRAPS
18
18
  } from '../../utils/constants.js';
19
19
  import ConnectionManager from './services/ConnectionManager.js';
20
-
20
+ import NetworkWalletFactory from './identity/NetworkWalletFactory.js';
21
21
  const wakeup = new w();
22
22
 
23
23
  class Network extends ReadyResource {
@@ -29,15 +29,18 @@ class Network extends ReadyResource {
29
29
  #transactionPoolService;
30
30
  #validatorObserverService;
31
31
  #validatorConnectionManager;
32
+ #options;
33
+ #identityProvider = null;
32
34
 
33
35
  constructor(state, channel, address = null, options = {}) {
34
36
  super();
37
+ this.#options = options;
35
38
  this.#enable_wallet = options.enable_wallet !== false;
36
39
  this.#channel = channel;
37
40
  this.#transactionPoolService = new TransactionPoolService(state, address, options);
38
- this.#validatorObserverService = new ValidatorObserverService(this, state, address, options)
41
+ this.#validatorObserverService = new ValidatorObserverService(this, state, address, options);
39
42
  this.#networkMessages = new NetworkMessages(this, options);
40
- this.#validatorConnectionManager = new ConnectionManager({ maxValidators: options.max_validators })
43
+ this.#validatorConnectionManager = new ConnectionManager({ maxValidators: options.max_validators });
41
44
  this.admin_stream = null;
42
45
  this.admin = null;
43
46
  this.validator = null;
@@ -90,6 +93,7 @@ class Network extends ReadyResource {
90
93
  ) {
91
94
  if (!this.#swarm) {
92
95
  const keyPair = await this.initializeNetworkingKeyPair(store, wallet);
96
+ const wrappedWallet = this.#getNetworkWalletWrapper(wallet, keyPair);
93
97
  this.#swarm = new Hyperswarm({
94
98
  keyPair,
95
99
  bootstrap: this.#dht_bootstrap,
@@ -100,7 +104,7 @@ class Network extends ReadyResource {
100
104
  });
101
105
 
102
106
  console.log(`Channel: ${b4a.toString(this.#channel)}`);
103
- this.#networkMessages.initializeMessageRouter(state, wallet);
107
+ this.#networkMessages.initializeMessageRouter(state, wrappedWallet);
104
108
 
105
109
  this.#swarm.on('connection', async (connection) => {
106
110
  const { message_channel, message } = await this.#networkMessages.setupProtomuxMessages(connection);
@@ -120,7 +124,7 @@ class Network extends ReadyResource {
120
124
  this.custom_stream = null;
121
125
  this.custom_node = null;
122
126
  }
123
- try{ message_channel.close() }catch(e){}
127
+ try { message_channel.close() } catch (e) { }
124
128
 
125
129
  });
126
130
 
@@ -129,7 +133,7 @@ class Network extends ReadyResource {
129
133
  error && error.message && (
130
134
  error.message.includes('connection reset by peer') ||
131
135
  error.message.includes('Duplicate connection') ||
132
- error.message.includes('connection timed out') )
136
+ error.message.includes('connection timed out'))
133
137
  ) {
134
138
  // TODO: decide if we want to handle this error in a specific way. It generates a lot of logs.
135
139
  return;
@@ -168,11 +172,11 @@ class Network extends ReadyResource {
168
172
  cnt += 1;
169
173
  }
170
174
  }
171
-
175
+
172
176
  if (this.#swarm.peers.has(publicKey)) {
173
177
  let stream;
174
178
  const peerInfo = this.#swarm.peers.get(publicKey)
175
- stream = this.#swarm._allConnections.get(peerInfo.publicKey)
179
+ stream = this.#swarm._allConnections.get(peerInfo.publicKey)
176
180
  if (stream !== undefined && stream.messenger !== undefined) {
177
181
  if (type === 'validator') {
178
182
  this.#validatorConnectionManager.addValidator(b4a.from(publicKey, 'hex'), stream)
@@ -183,8 +187,8 @@ class Network extends ReadyResource {
183
187
  }
184
188
 
185
189
  async isConnected(publicKey) {
186
- return this.#swarm.peers.has(publicKey) &&
187
- this.#swarm.peers.get(publicKey).connectedTime != -1
190
+ return this.#swarm.peers.has(publicKey) &&
191
+ this.#swarm.peers.get(publicKey).connectedTime != -1
188
192
  }
189
193
 
190
194
  async #sendRequestByType(stream, type) {
@@ -239,6 +243,19 @@ class Network extends ReadyResource {
239
243
  console.log(e)
240
244
  }
241
245
  }
246
+
247
+ #getNetworkWalletWrapper(wallet, keyPair) {
248
+ if (!this.#identityProvider) {
249
+ this.#identityProvider = NetworkWalletFactory.provide({
250
+ enableWallet: this.#enable_wallet,
251
+ wallet,
252
+ keyPair,
253
+ networkPrefix: this.#options?.networkPrefix
254
+ });
255
+ }
256
+ return this.#identityProvider;
257
+ }
258
+
242
259
  }
243
260
 
244
261
  export default Network;
@@ -0,0 +1,78 @@
1
+ import PeerWallet from 'trac-wallet';
2
+ import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js';
3
+ import b4a from 'b4a';
4
+
5
+ export class NetworkWalletFactory {
6
+ static provide(options = {}) {
7
+ const {
8
+ enableWallet = true,
9
+ wallet,
10
+ keyPair,
11
+ networkPrefix = TRAC_NETWORK_MSB_MAINNET_PREFIX
12
+ } = options;
13
+
14
+ if (enableWallet) {
15
+ if (!wallet) {
16
+ throw new Error('NetworkingWalletFactory: wallet instance is required when wallet is enabled');
17
+ }
18
+ return wallet;
19
+ }
20
+
21
+ if (!keyPair) {
22
+ throw new Error('NetworkingWalletFactory: keyPair must be provided when wallet is disabled');
23
+ }
24
+
25
+ return new EphemeralWallet(keyPair, networkPrefix);
26
+ }
27
+ }
28
+
29
+ // TODO: Once Wallet class in trac-wallet exposes a constructor/factory that accepts an existing keyPair
30
+ // (e.g. Wallet.fromKeyPair({ publicKey, secretKey }, networkPrefix)), replace EphemeralWallet
31
+ // with a thin wrapper around that functionality instead of duplicating signing/verification logic.
32
+ class EphemeralWallet {
33
+ #publicKey;
34
+ #secretKey;
35
+ #address;
36
+
37
+ constructor(keyPair, networkPrefix = TRAC_NETWORK_MSB_MAINNET_PREFIX) {
38
+
39
+ if (!keyPair?.publicKey || !keyPair?.secretKey) {
40
+ throw new Error('NetworkIdentityProvider: keyPair with publicKey and secretKey is required');
41
+ }
42
+ this.#assertBuffer(keyPair.publicKey);
43
+ this.#assertBuffer(keyPair.secretKey);
44
+
45
+ const address = PeerWallet.encodeBech32m(networkPrefix, keyPair.publicKey);
46
+ if (!address) {
47
+ throw new Error('NetworkIdentityProvider: failed to derive address from networking key pair');
48
+ }
49
+
50
+ this.#publicKey = keyPair.publicKey;
51
+ this.#secretKey = keyPair.secretKey;
52
+ this.#address = address;
53
+ }
54
+
55
+ get publicKey() {
56
+ return this.#publicKey;
57
+ }
58
+
59
+ get address() {
60
+ return this.#address;
61
+ }
62
+
63
+ sign(message) {
64
+ return PeerWallet.sign(message, this.#secretKey);
65
+ }
66
+
67
+ verify(signature, message, publicKey = this.#publicKey) {
68
+ return PeerWallet.verify(signature, message, publicKey);
69
+ }
70
+
71
+ #assertBuffer(value) {
72
+ if (!b4a.isBuffer(value)) {
73
+ throw new Error(`NetworkIdentityProvider: value must be a Buffer`);
74
+ }
75
+ }
76
+ }
77
+
78
+ export default NetworkWalletFactory;