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
@@ -0,0 +1,82 @@
1
+ import { test } from 'brittle';
2
+ import b4a from 'b4a';
3
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
4
+ import nodeEntryUtils from '../../../../../src/core/state/utils/nodeEntry.js';
5
+ import { toTerm } from '../../../../../src/core/state/utils/balance.js';
6
+ import { ZERO_WK } from '../../../../../src/utils/buffer.js';
7
+ import { safeDecodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
8
+ import setupBalanceInitializationScenario, {
9
+ buildBalanceInitializationPayload
10
+ } from './balanceInitializationScenarioHelpers.js';
11
+
12
+ export default function balanceInitializationHappyPathScenario() {
13
+ test('State.apply balanceInitialization credits reader nodes with requested balances - happy path', async t => {
14
+ const recipientCount = 2;
15
+ const networkContext = await setupBalanceInitializationScenario(t, recipientCount);
16
+ const adminNode = networkContext.adminBootstrap;
17
+ const readerNodes = networkContext.peers.slice(1);
18
+ const recipients = [];
19
+
20
+ for (let i = 0; i < recipientCount; i++) {
21
+ const peer = readerNodes[i];
22
+ recipients.push({
23
+ peer,
24
+ balance: i === 0 ? toTerm(25n) : toTerm(75n)
25
+ });
26
+ }
27
+
28
+ const appliedTxHashes = [];
29
+
30
+ for (const recipient of recipients) {
31
+ const payload = await buildBalanceInitializationPayload(
32
+ networkContext,
33
+ recipient.peer.wallet.address,
34
+ recipient.balance
35
+ );
36
+ const decodedPayload = safeDecodeApplyOperation(payload);
37
+ appliedTxHashes.push(decodedPayload.bio.tx.toString('hex'));
38
+
39
+ await adminNode.base.append(payload);
40
+ await adminNode.base.update();
41
+ await eventFlush();
42
+ }
43
+
44
+ for (const recipient of recipients) {
45
+ await assertRecipientBalance(
46
+ t,
47
+ adminNode.base,
48
+ recipient.peer.wallet.address,
49
+ recipient.balance
50
+ );
51
+ }
52
+
53
+ for (const txHashHex of appliedTxHashes) {
54
+ const txEntry = await adminNode.base.view.get(txHashHex);
55
+ t.ok(txEntry, 'operation hash stored to prevent replays');
56
+ }
57
+
58
+ await networkContext.sync();
59
+
60
+ for (const recipient of recipients) {
61
+ await assertRecipientBalance(
62
+ t,
63
+ recipient.peer.base,
64
+ recipient.peer.wallet.address,
65
+ recipient.balance
66
+ );
67
+ }
68
+ });
69
+ }
70
+
71
+ async function assertRecipientBalance(t, base, address, expectedBalance) {
72
+ const nodeEntryRecord = await base.view.get(address);
73
+ t.ok(nodeEntryRecord, 'recipient node entry exists');
74
+
75
+ const decodedEntry = nodeEntryUtils.decode(nodeEntryRecord.value);
76
+ t.ok(decodedEntry, 'recipient node entry decodes');
77
+ t.is(decodedEntry.isWriter, false, 'recipient not flagged as writer');
78
+ t.is(decodedEntry.isIndexer, false, 'recipient not flagged as indexer');
79
+ t.ok(b4a.equals(decodedEntry.wk, ZERO_WK), 'recipient writing key remains unset');
80
+ t.ok(b4a.equals(decodedEntry.balance, expectedBalance), 'recipient balance is set');
81
+ t.ok(b4a.equals(decodedEntry.stakedBalance, toTerm(0n)), 'recipient staked balance is zero');
82
+ }
@@ -0,0 +1,106 @@
1
+ import b4a from 'b4a';
2
+ import { setupStateNetwork } from '../../../../helpers/StateNetworkFactory.js';
3
+ import {
4
+ seedBootstrapIndexer,
5
+ defaultOpenHyperbeeView,
6
+ deriveIndexerSequenceState,
7
+ eventFlush
8
+ } from '../../../../helpers/autobaseTestHelpers.js';
9
+ import CompleteStateMessageOperations from '../../../../../src/messages/completeStateMessages/CompleteStateMessageOperations.js';
10
+ import { AUTOBASE_VALUE_ENCODING } from '../../../../../src/utils/constants.js';
11
+ import { toTerm } from '../../../../../src/core/state/utils/balance.js';
12
+ import { safeDecodeApplyOperation, safeEncodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
13
+ import { buildAddAdminRequesterPayload } from '../addAdmin/addAdminScenarioHelpers.js';
14
+
15
+ export async function setupBalanceInitializationScenario(t, { recipientCount = 2 } = {}) {
16
+ const context = await setupStateNetwork({
17
+ nodes: Math.max(recipientCount + 1, 2),
18
+ valueEncoding: AUTOBASE_VALUE_ENCODING,
19
+ open: defaultOpenHyperbeeView
20
+ });
21
+
22
+ seedBootstrapIndexer(context);
23
+
24
+ t.teardown(async () => {
25
+ await context.teardown();
26
+ });
27
+
28
+ await bootstrapAdmin(context);
29
+ return context;
30
+ }
31
+
32
+ async function bootstrapAdmin(context) {
33
+ const adminNode = context.adminBootstrap;
34
+ const addAdminPayload = await buildAddAdminRequesterPayload(context);
35
+
36
+ await adminNode.base.append(addAdminPayload);
37
+ await adminNode.base.update();
38
+ await eventFlush();
39
+ }
40
+
41
+ export async function buildBalanceInitializationPayload(context, recipientAddress, balanceBuffer) {
42
+ const adminNode = context.adminBootstrap;
43
+ const txValidity = await deriveIndexerSequenceState(adminNode.base);
44
+ const messages = await CompleteStateMessageOperations.assembleBalanceInitializationMessages(
45
+ adminNode.wallet,
46
+ txValidity,
47
+ [[recipientAddress, balanceBuffer]]
48
+ );
49
+ return messages[0];
50
+ }
51
+
52
+ export async function buildBalanceInitializationPayloadWithTxValidity({
53
+ context,
54
+ validPayload,
55
+ mutatedTxValidity
56
+ }) {
57
+ const decoded = safeDecodeApplyOperation(validPayload);
58
+ if (!decoded?.bio?.ia || !decoded?.bio?.am) {
59
+ return validPayload;
60
+ }
61
+
62
+ const adminNode = context.adminBootstrap;
63
+ const messages = await CompleteStateMessageOperations.assembleBalanceInitializationMessages(
64
+ adminNode.wallet,
65
+ mutatedTxValidity,
66
+ [[decoded.bio.ia, decoded.bio.am]]
67
+ );
68
+ return messages[0];
69
+ }
70
+
71
+ export async function buildDefaultBalanceInitializationPayload(context) {
72
+ const recipientPeer = selectRecipientPeer(context);
73
+ return buildBalanceInitializationPayload(context, recipientPeer.wallet.address, toTerm(25n));
74
+ }
75
+
76
+ export async function assertBalanceInitializationFailureState(t, context, { skipSync = false } = {}) {
77
+ const adminNode = context.adminBootstrap;
78
+ const recipientPeer = selectRecipientPeer(context);
79
+
80
+ await assertRecipientAbsent(t, adminNode.base, recipientPeer.wallet.address);
81
+
82
+ if (!skipSync) {
83
+ await context.sync();
84
+ await assertRecipientAbsent(t, recipientPeer.base, recipientPeer.wallet.address);
85
+ }
86
+ }
87
+
88
+ export function mutateBalanceInitializationPayloadForInvalidSchema(t, payload) {
89
+ const decodedPayload = safeDecodeApplyOperation(payload);
90
+ t.ok(decodedPayload, 'fixtures decode');
91
+
92
+ decodedPayload.bio.tx = b4a.alloc(decodedPayload.bio.tx.length);
93
+ return safeEncodeApplyOperation(decodedPayload);
94
+ }
95
+
96
+ function selectRecipientPeer(context, offset = 0) {
97
+ const readerPeers = context.peers.slice(1);
98
+ return readerPeers[Math.min(offset, readerPeers.length - 1)];
99
+ }
100
+
101
+ async function assertRecipientAbsent(t, base, address) {
102
+ const nodeEntryRecord = await base.view.get(address);
103
+ t.is(nodeEntryRecord, null, 'recipient node entry remains absent');
104
+ }
105
+
106
+ export default setupBalanceInitializationScenario;
@@ -0,0 +1,45 @@
1
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
2
+ import OperationValidationScenarioBase from '../common/base/OperationValidationScenarioBase.js';
3
+ import { safeDecodeApplyOperation, safeEncodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
4
+ import setupBalanceInitializationScenario, {
5
+ buildDefaultBalanceInitializationPayload,
6
+ assertBalanceInitializationFailureState
7
+ } from './balanceInitializationScenarioHelpers.js';
8
+
9
+ export default function balanceInitializationInvalidAmountScenario() {
10
+ new OperationValidationScenarioBase({
11
+ title: 'State.apply balanceInitialization aborts when amount buffer is malformed',
12
+ setupScenario: setupBalanceInitializationScenario,
13
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
14
+ mutatePayload: truncateAmountBuffer,
15
+ applyInvalidPayload: bypassSchemaAndApply,
16
+ assertStateUnchanged: assertBalanceInitializationFailureState,
17
+ expectedLogs: ['Invalid balance.']
18
+ }).performScenario();
19
+ }
20
+
21
+ function truncateAmountBuffer(t, payload) {
22
+ const decodedPayload = safeDecodeApplyOperation(payload);
23
+ t.ok(decodedPayload, 'fixtures decode');
24
+
25
+ decodedPayload.bio.am = decodedPayload.bio.am.slice(0, 8);
26
+ return safeEncodeApplyOperation(decodedPayload);
27
+ }
28
+
29
+ async function bypassSchemaAndApply(context, invalidPayload) {
30
+ const adminNode = context.adminBootstrap;
31
+ if (!adminNode) {
32
+ throw new Error('Invalid amount scenario requires admin bootstrap context.');
33
+ }
34
+
35
+ const originalValidator = adminNode.state.check.validateBalanceInitialization;
36
+ adminNode.state.check.validateBalanceInitialization = () => true;
37
+
38
+ try {
39
+ await adminNode.base.append(invalidPayload);
40
+ await adminNode.base.update();
41
+ await eventFlush();
42
+ } finally {
43
+ adminNode.state.check.validateBalanceInitialization = originalValidator;
44
+ }
45
+ }
@@ -0,0 +1,81 @@
1
+ import b4a from 'b4a';
2
+ import OperationValidationScenarioBase from '../common/base/OperationValidationScenarioBase.js';
3
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
4
+ import { safeDecodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
5
+ import addressUtils from '../../../../../src/core/state/utils/address.js';
6
+ import setupBalanceInitializationScenario, {
7
+ buildDefaultBalanceInitializationPayload,
8
+ assertBalanceInitializationFailureState
9
+ } from './balanceInitializationScenarioHelpers.js';
10
+
11
+ export default function balanceInitializationNodeEntryBalanceUpdateFailureScenario() {
12
+ new OperationValidationScenarioBase({
13
+ title: 'State.apply balanceInitialization aborts when updating existing node entry balance fails',
14
+ setupScenario: setupBalanceInitializationScenario,
15
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
16
+ mutatePayload: (_t, payload) => payload,
17
+ applyInvalidPayload: applyWithCorruptExistingNodeEntry,
18
+ assertStateUnchanged: (t, context) =>
19
+ assertBalanceInitializationFailureState(t, context, { skipSync: true }),
20
+ expectedLogs: ['Failed to set node entry balance.']
21
+ }).performScenario();
22
+ }
23
+
24
+ async function applyWithCorruptExistingNodeEntry(context, payload, _t, validPayload) {
25
+ const node = context.adminBootstrap;
26
+ if (!node?.base) {
27
+ throw new Error('Balance update failure scenario requires an admin bootstrap node.');
28
+ }
29
+
30
+ const decoded = safeDecodeApplyOperation(validPayload ?? payload);
31
+ const targetAddressBuffer = decoded?.bio?.ia;
32
+ const targetAddressString = targetAddressBuffer
33
+ ? addressUtils.bufferToAddress(targetAddressBuffer)
34
+ : null;
35
+ if (!targetAddressString || !targetAddressBuffer) {
36
+ throw new Error('Failed to resolve recipient address for balance update failure scenario.');
37
+ }
38
+
39
+ const originalApply = node.base._handlers.apply;
40
+ node.base._handlers.apply = async function patchedApply(nodes, view, baseCtx) {
41
+ const originalBatch = view.batch;
42
+ view.batch = function patchedBatch(...args) {
43
+ const batch = originalBatch.apply(this, args);
44
+ if (!batch?.get) return batch;
45
+
46
+ const originalGet = batch.get.bind(batch);
47
+ batch.get = async key => {
48
+ if (isTargetKey(key, targetAddressString, targetAddressBuffer)) {
49
+ return { key, value: b4a.alloc(1) };
50
+ }
51
+ return originalGet(key);
52
+ };
53
+
54
+ return batch;
55
+ };
56
+
57
+ try {
58
+ return await originalApply.call(this, nodes, view, baseCtx);
59
+ } finally {
60
+ view.batch = originalBatch;
61
+ }
62
+ };
63
+
64
+ try {
65
+ await node.base.append(payload);
66
+ await node.base.update();
67
+ await eventFlush();
68
+ } finally {
69
+ node.base._handlers.apply = originalApply;
70
+ }
71
+ }
72
+
73
+ function isTargetKey(key, targetAddressString, targetAddressBuffer) {
74
+ if (typeof key === 'string') {
75
+ return key === targetAddressString;
76
+ }
77
+ if (b4a.isBuffer(key) && targetAddressBuffer) {
78
+ return b4a.equals(key, targetAddressBuffer);
79
+ }
80
+ return false;
81
+ }
@@ -0,0 +1,189 @@
1
+ import setupBalanceInitializationScenario, {
2
+ buildDefaultBalanceInitializationPayload,
3
+ assertBalanceInitializationFailureState,
4
+ mutateBalanceInitializationPayloadForInvalidSchema,
5
+ buildBalanceInitializationPayloadWithTxValidity
6
+ } from './balanceInitializationScenarioHelpers.js';
7
+ import balanceInitializationHappyPathScenario from './balanceInitializationHappyPathScenario.js';
8
+ import balanceInitializationInvalidAmountScenario from './invalidAmountScenario.js';
9
+ import balanceInitializationNodeEntryBalanceUpdateFailureScenario from './nodeEntryBalanceUpdateFailureScenario.js';
10
+ import RequesterAddressValidationScenario from '../common/requesterAddressValidationScenario.js';
11
+ import InvalidPayloadValidationScenario from '../common/payload-structure/invalidPayloadValidationScenario.js';
12
+ import InvalidAddressValidationScenario from '../common/payload-structure/invalidAddressValidationScenario.js';
13
+ import createAddressWithInvalidPublicKeyScenario from '../common/payload-structure/addressWithInvalidPublicKeyScenario.js';
14
+ import createRequesterPublicKeyValidationScenario from '../common/requesterPublicKeyValidationScenario.js';
15
+ import InvalidHashValidationScenario from '../common/payload-structure/invalidHashValidationScenario.js';
16
+ import InvalidSignatureValidationScenario, { SignatureMutationStrategy } from '../common/payload-structure/invalidSignatureValidationScenario.js';
17
+ import InvalidMessageComponentValidationScenario, { MessageComponentStrategy } from '../common/invalidMessageComponentValidationScenario.js';
18
+ import InitializationDisabledScenario from '../common/payload-structure/initializationDisabledScenario.js';
19
+ import AdminEntryDecodeFailureScenario from '../common/access-control/adminEntryDecodeFailureScenario.js';
20
+ import AdminOnlyGuardScenario from '../common/access-control/adminOnlyGuardScenario.js';
21
+ import AdminConsistencyMismatchScenario from '../common/access-control/adminConsistencyMismatchScenario.js';
22
+ import IndexerSequenceStateInvalidScenario from '../common/indexer/indexerSequenceStateInvalidScenario.js';
23
+ import TransactionValidityMismatchScenario from '../common/transactionValidityMismatchScenario.js';
24
+
25
+ balanceInitializationHappyPathScenario();
26
+
27
+ new InvalidPayloadValidationScenario({
28
+ title: 'State.apply balanceInitialization rejects payloads that fail schema validation',
29
+ setupScenario: setupBalanceInitializationScenario,
30
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
31
+ mutatePayload: mutateBalanceInitializationPayloadForInvalidSchema,
32
+ assertStateUnchanged: assertBalanceInitializationFailureState,
33
+ expectedLogs: ['Contract schema validation failed.']
34
+ }).performScenario();
35
+
36
+ new RequesterAddressValidationScenario({
37
+ title: 'State.apply balanceInitialization requester address is invalid',
38
+ setupScenario: setupBalanceInitializationScenario,
39
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
40
+ assertStateUnchanged: assertBalanceInitializationFailureState,
41
+ expectedLogs: ['Requester address is invalid.']
42
+ }).performScenario();
43
+
44
+ createRequesterPublicKeyValidationScenario({
45
+ title: 'State.apply balanceInitialization requester public key is invalid',
46
+ setupScenario: setupBalanceInitializationScenario,
47
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
48
+ assertStateUnchanged: assertBalanceInitializationFailureState,
49
+ expectedLogs: ['Error while decoding requester public key.']
50
+ }).performScenario();
51
+
52
+ new InvalidAddressValidationScenario({
53
+ title: 'State.apply balanceInitialization recipient address is invalid',
54
+ setupScenario: setupBalanceInitializationScenario,
55
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
56
+ assertStateUnchanged: assertBalanceInitializationFailureState,
57
+ addressPath: ['bio', 'ia'],
58
+ expectedLogs: ['Recipient address is invalid.']
59
+ }).performScenario();
60
+
61
+ createAddressWithInvalidPublicKeyScenario({
62
+ title: 'State.apply balanceInitialization recipient public key is invalid',
63
+ setupScenario: setupBalanceInitializationScenario,
64
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
65
+ assertStateUnchanged: assertBalanceInitializationFailureState,
66
+ addressPath: ['bio', 'ia'],
67
+ expectedLogs: ['Failed to decode recipient public key.']
68
+ }).performScenario();
69
+
70
+ balanceInitializationInvalidAmountScenario();
71
+
72
+ new InitializationDisabledScenario({
73
+ title: 'State.apply balanceInitialization aborts when initialization is disabled',
74
+ setupScenario: setupBalanceInitializationScenario,
75
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
76
+ assertStateUnchanged: assertBalanceInitializationFailureState,
77
+ expectedLogs: ['Balance initialization is disabled.']
78
+ }).performScenario();
79
+
80
+ new AdminEntryDecodeFailureScenario({
81
+ title: 'State.apply balanceInitialization aborts when admin entry cannot be decoded',
82
+ setupScenario: setupBalanceInitializationScenario,
83
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
84
+ assertStateUnchanged: (t, context) =>
85
+ assertBalanceInitializationFailureState(t, context, { skipSync: true }),
86
+ expectedLogs: ['Failed to decode admin entry.']
87
+ }).performScenario();
88
+
89
+ new AdminOnlyGuardScenario({
90
+ title: 'State.apply balanceInitialization rejects non-admin nodes',
91
+ setupScenario: setupBalanceInitializationScenario,
92
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
93
+ assertStateUnchanged: (t, context) =>
94
+ assertBalanceInitializationFailureState(t, context, { skipSync: true }),
95
+ expectedLogs: ['Node is not allowed to perform this operation. (ADMIN ONLY)']
96
+ }).performScenario();
97
+
98
+ new AdminConsistencyMismatchScenario({
99
+ title: 'State.apply balanceInitialization rejects when admin key mismatch occurs',
100
+ setupScenario: setupBalanceInitializationScenario,
101
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
102
+ assertStateUnchanged: (t, context) =>
103
+ assertBalanceInitializationFailureState(t, context, { skipSync: true }),
104
+ expectedLogs: ['System admin and node public keys do not match.']
105
+ }).performScenario();
106
+
107
+
108
+ new InvalidHashValidationScenario({
109
+ title: 'State.apply balanceInitialization requester message hash mismatch',
110
+ setupScenario: setupBalanceInitializationScenario,
111
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
112
+ assertStateUnchanged: assertBalanceInitializationFailureState,
113
+ expectedLogs: ['Message hash does not match the tx_hash.']
114
+ }).performScenario();
115
+
116
+ new InvalidSignatureValidationScenario({
117
+ title: 'State.apply balanceInitialization requester signature is invalid (foreign signature)',
118
+ setupScenario: setupBalanceInitializationScenario,
119
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
120
+ assertStateUnchanged: assertBalanceInitializationFailureState,
121
+ expectedLogs: ['Failed to verify message signature.']
122
+ }).performScenario();
123
+
124
+ new InvalidSignatureValidationScenario({
125
+ title: 'State.apply balanceInitialization requester signature is invalid (type mismatch)',
126
+ setupScenario: setupBalanceInitializationScenario,
127
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
128
+ assertStateUnchanged: assertBalanceInitializationFailureState,
129
+ strategy: SignatureMutationStrategy.TYPE_MISMATCH,
130
+ expectedLogs: ['Failed to verify message signature.']
131
+ }).performScenario();
132
+
133
+ new InvalidSignatureValidationScenario({
134
+ title: 'State.apply balanceInitialization requester signature is invalid (zero fill)',
135
+ setupScenario: setupBalanceInitializationScenario,
136
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
137
+ assertStateUnchanged: assertBalanceInitializationFailureState,
138
+ strategy: SignatureMutationStrategy.ZERO_FILL,
139
+ expectedLogs: ['Failed to verify message signature.']
140
+ }).performScenario();
141
+
142
+ new InvalidSignatureValidationScenario({
143
+ title: 'State.apply balanceInitialization amount signature is invalid',
144
+ setupScenario: setupBalanceInitializationScenario,
145
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
146
+ assertStateUnchanged: assertBalanceInitializationFailureState,
147
+ strategy: SignatureMutationStrategy.AMOUNT_SIGNATURE,
148
+ expectedLogs: ['Message hash does not match the tx_hash.']
149
+ }).performScenario();
150
+
151
+ new IndexerSequenceStateInvalidScenario({
152
+ title: 'State.apply balanceInitialization rejects payloads when indexer sequence state is invalid',
153
+ setupScenario: setupBalanceInitializationScenario,
154
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
155
+ assertStateUnchanged: (t, context) =>
156
+ assertBalanceInitializationFailureState(t, context, { skipSync: true }),
157
+ expectedLogs: ['Indexer sequence state is invalid.']
158
+ }).performScenario();
159
+
160
+ new TransactionValidityMismatchScenario({
161
+ title: 'State.apply balanceInitialization rejects payload when tx validity mismatches indexer state',
162
+ setupScenario: setupBalanceInitializationScenario,
163
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
164
+ assertStateUnchanged: assertBalanceInitializationFailureState,
165
+ txValidityPath: ['bio', 'txv'],
166
+ rebuildPayloadWithTxValidity: ({ context, validPayload, mutatedTxValidity }) =>
167
+ buildBalanceInitializationPayloadWithTxValidity({ context, validPayload, mutatedTxValidity }),
168
+ expectedLogs: ['Transaction was not executed.']
169
+ }).performScenario();
170
+
171
+ new InvalidMessageComponentValidationScenario({
172
+ title: 'State.apply balanceInitialization transaction validity mismatch',
173
+ setupScenario: setupBalanceInitializationScenario,
174
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
175
+ assertStateUnchanged: assertBalanceInitializationFailureState,
176
+ strategy: MessageComponentStrategy.TX_VALIDITY,
177
+ expectedLogs: ['Message hash does not match the tx_hash.']
178
+ }).performScenario();
179
+
180
+ new InvalidMessageComponentValidationScenario({
181
+ title: 'State.apply balanceInitialization requester nonce mismatch',
182
+ setupScenario: setupBalanceInitializationScenario,
183
+ buildValidPayload: buildDefaultBalanceInitializationPayload,
184
+ assertStateUnchanged: assertBalanceInitializationFailureState,
185
+ strategy: MessageComponentStrategy.NONCE,
186
+ expectedLogs: ['Message hash does not match the tx_hash.']
187
+ }).performScenario();
188
+
189
+ balanceInitializationNodeEntryBalanceUpdateFailureScenario();
@@ -0,0 +1,155 @@
1
+ import { test } from 'brittle';
2
+ import b4a from 'b4a';
3
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
4
+ import {
5
+ selectWriterPeer,
6
+ buildAddWriterPayload,
7
+ assertAddWriterSuccessState
8
+ } from '../addWriter/addWriterScenarioHelpers.js';
9
+ import {
10
+ setupBanValidatorScenario,
11
+ buildBanValidatorPayload,
12
+ assertBanValidatorSuccessState
13
+ } from './banValidatorScenarioHelpers.js';
14
+ import {
15
+ buildAppendWhitelistPayload
16
+ } from '../appendWhitelist/appendWhitelistScenarioHelpers.js';
17
+ import nodeEntryUtils, { ZERO_LICENSE } from '../../../../../src/core/state/utils/nodeEntry.js';
18
+ import addressUtils from '../../../../../src/core/state/utils/address.js';
19
+ import { EntryType } from '../../../../../src/utils/constants.js';
20
+ import { BALANCE_ZERO, toBalance } from '../../../../../src/core/state/utils/balance.js';
21
+ import lengthEntryUtils from '../../../../../src/core/state/utils/lengthEntry.js';
22
+
23
+ export default function banValidatorBanAndReWhitelistScenario() {
24
+ test('State.apply banValidator allows re-whitelisting without changing license', async t => {
25
+ const context = await setupBanValidatorScenario(t);
26
+ const adminPeer = context.adminBootstrap;
27
+ const validatorPeer = context.banValidatorScenario?.validatorPeer ?? selectWriterPeer(context);
28
+
29
+ const validatorEntryBefore = await adminPeer.base.view.get(validatorPeer.wallet.address);
30
+ t.ok(validatorEntryBefore, 'validator entry exists before banValidator');
31
+ const decodedBefore = nodeEntryUtils.decode(validatorEntryBefore?.value);
32
+ t.ok(decodedBefore, 'validator entry decodes before banValidator');
33
+ if (!decodedBefore) return;
34
+
35
+ const adminEntryBefore = await adminPeer.base.view.get(adminPeer.wallet.address);
36
+ t.ok(adminEntryBefore, 'admin entry exists before banValidator');
37
+
38
+ const banPayload = await buildBanValidatorPayload(context, { adminPeer, validatorPeer });
39
+
40
+ await adminPeer.base.append(banPayload);
41
+ await adminPeer.base.update();
42
+ await eventFlush();
43
+
44
+ await assertBanValidatorSuccessState(t, context, {
45
+ validatorPeer,
46
+ adminPeer,
47
+ validatorEntryBefore,
48
+ adminEntryBefore,
49
+ payload: banPayload
50
+ });
51
+
52
+ const bannedEntry = await adminPeer.base.view.get(validatorPeer.wallet.address);
53
+ t.ok(bannedEntry, 'validator entry exists after ban');
54
+ const decodedBanned = nodeEntryUtils.decode(bannedEntry?.value);
55
+ t.ok(decodedBanned, 'validator entry decodes after ban');
56
+ if (!decodedBanned) return;
57
+ const bannedBalance = toBalance(decodedBanned.balance);
58
+ t.ok(bannedBalance, 'balance decodes after ban');
59
+
60
+ const whitelistPayload = await buildAppendWhitelistPayload(
61
+ context,
62
+ validatorPeer.wallet.address
63
+ );
64
+
65
+ await adminPeer.base.append(whitelistPayload);
66
+ await adminPeer.base.update();
67
+ await eventFlush();
68
+
69
+ const rewhitelistedEntry = await adminPeer.base.view.get(validatorPeer.wallet.address);
70
+ t.ok(rewhitelistedEntry, 'validator entry exists after re-whitelist');
71
+ const decodedRewhitelisted = nodeEntryUtils.decode(rewhitelistedEntry?.value);
72
+ t.ok(decodedRewhitelisted, 'validator entry decodes after re-whitelist');
73
+ if (!decodedRewhitelisted || !bannedBalance) return;
74
+
75
+ t.is(decodedRewhitelisted.isWhitelisted, true, 'node marked whitelisted after reapply');
76
+ t.is(decodedRewhitelisted.isWriter, false, 'writer flag remains cleared after reapply');
77
+ t.is(decodedRewhitelisted.isIndexer, false, 'indexer flag remains cleared after reapply');
78
+ t.ok(b4a.equals(decodedRewhitelisted.wk, decodedBefore.wk), 'writing key preserved');
79
+ t.ok(
80
+ b4a.equals(decodedRewhitelisted.balance, bannedBalance.value),
81
+ 'balance unchanged by re-whitelist'
82
+ );
83
+ t.ok(
84
+ b4a.equals(decodedRewhitelisted.stakedBalance, BALANCE_ZERO.value),
85
+ 'staked balance remains zero after re-whitelist'
86
+ );
87
+ t.ok(!b4a.equals(decodedRewhitelisted.license, ZERO_LICENSE), 'license retained after reapply');
88
+ t.ok(
89
+ b4a.equals(decodedRewhitelisted.license, decodedBefore.license),
90
+ 'license unchanged after ban and re-whitelist'
91
+ );
92
+
93
+ const licenseId = decodedBefore.license.readUInt32BE();
94
+ const licenseIndexEntry = await adminPeer.base.view.get(`${EntryType.LICENSE_INDEX}${licenseId}`);
95
+ t.ok(licenseIndexEntry, 'license index entry persists after re-whitelist');
96
+ const addressBuffer = addressUtils.addressToBuffer(validatorPeer.wallet.address);
97
+ if (licenseIndexEntry?.value && addressBuffer) {
98
+ t.ok(
99
+ b4a.equals(licenseIndexEntry.value, addressBuffer),
100
+ 'license index still maps to validator address'
101
+ );
102
+ }
103
+
104
+ const writersLengthEntry = await adminPeer.base.view.get(EntryType.WRITERS_LENGTH);
105
+ const writersLengthBefore = writersLengthEntry
106
+ ? lengthEntryUtils.decodeBE(writersLengthEntry.value)
107
+ : 0;
108
+
109
+ const addWriterPayload = await buildAddWriterPayload(context, {
110
+ readerPeer: validatorPeer,
111
+ validatorPeer: adminPeer
112
+ });
113
+
114
+ await adminPeer.base.append(addWriterPayload);
115
+ await adminPeer.base.update();
116
+ await eventFlush();
117
+
118
+ await assertAddWriterSuccessState(t, context, {
119
+ readerPeer: validatorPeer,
120
+ validatorPeer: adminPeer,
121
+ writerInitialBalance: decodedRewhitelisted.balance,
122
+ expectedWriterIndex: writersLengthBefore,
123
+ payload: addWriterPayload,
124
+ skipSync: true
125
+ });
126
+
127
+ const repromotedEntry = await adminPeer.base.view.get(validatorPeer.wallet.address);
128
+ t.ok(repromotedEntry, 'validator entry exists after re-promotion to writer');
129
+ const decodedRepromoted = nodeEntryUtils.decode(repromotedEntry?.value);
130
+ t.ok(decodedRepromoted, 'validator entry decodes after re-promotion to writer');
131
+ if (decodedRepromoted) {
132
+ t.is(decodedRepromoted.isWhitelisted, true, 'node remains whitelisted after re-promotion');
133
+ t.is(decodedRepromoted.isWriter, true, 'writer role reassigned after re-promotion');
134
+ t.is(decodedRepromoted.isIndexer, false, 'indexer flag stays cleared after re-promotion');
135
+ t.ok(
136
+ b4a.equals(decodedRepromoted.license, decodedBefore.license),
137
+ 'license unchanged after re-promotion'
138
+ );
139
+ }
140
+
141
+ await context.sync();
142
+ const replicatedEntry = await validatorPeer.base.view.get(validatorPeer.wallet.address);
143
+ t.ok(replicatedEntry, 'replicated entry exists after re-whitelist and re-promotion');
144
+ const decodedReplicated = nodeEntryUtils.decode(replicatedEntry?.value);
145
+ t.ok(decodedReplicated, 'replicated entry decodes after re-whitelist and re-promotion');
146
+ if (decodedReplicated) {
147
+ t.is(decodedReplicated.isWhitelisted, true, 'replicated entry whitelisted');
148
+ t.is(decodedReplicated.isWriter, true, 'replicated entry writer flag set');
149
+ t.ok(
150
+ b4a.equals(decodedReplicated.license, decodedBefore.license),
151
+ 'replicated entry license unchanged'
152
+ );
153
+ }
154
+ });
155
+ }