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,19 @@
1
+ import b4a from 'b4a';
2
+ import { WRITER_MASK } from '../../../../../../src/core/state/utils/roles.js';
3
+ import ValidatorConsistencyScenarioBase from './base/validatorConsistencyScenarioBase.js';
4
+
5
+ export default class ValidatorInactiveScenario extends ValidatorConsistencyScenarioBase {
6
+ constructor(options) {
7
+ super({
8
+ ...options,
9
+ mutateEntry: entry => {
10
+ if (!entry.value) {
11
+ throw new Error('Validator inactive scenario requires an existing validator entry.');
12
+ }
13
+ const mutated = b4a.from(entry.value);
14
+ mutated[0] &= ~WRITER_MASK;
15
+ return { ...entry, value: mutated };
16
+ }
17
+ });
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ import b4a from 'b4a';
2
+ import ValidatorConsistencyScenarioBase from './base/validatorConsistencyScenarioBase.js';
3
+
4
+ export default class ValidatorWriterKeyMismatchScenario extends ValidatorConsistencyScenarioBase {
5
+ constructor(options) {
6
+ super({
7
+ ...options,
8
+ mutateEntry: entry => {
9
+ if (!entry.value) {
10
+ throw new Error('Validator writer key mismatch scenario requires an existing validator entry.');
11
+ }
12
+ const mutated = b4a.from(entry.value);
13
+ if (mutated.length > 33) mutated[1] ^= 0xff;
14
+ return { ...entry, value: mutated };
15
+ }
16
+ });
17
+ }
18
+ }
@@ -0,0 +1,314 @@
1
+ import b4a from 'b4a';
2
+ import OperationValidationScenarioBase from '../../base/OperationValidationScenarioBase.js';
3
+ import nodeEntryUtils from '../../../../../../../src/core/state/utils/nodeEntry.js';
4
+ import { safeDecodeApplyOperation } from '../../../../../../../src/utils/protobuf/operationHelpers.js';
5
+ import addressUtils from '../../../../../../../src/core/state/utils/address.js';
6
+ import { eventFlush } from '../../../../../../helpers/autobaseTestHelpers.js';
7
+ import { BALANCE_ZERO } from '../../../../../../../src/core/state/utils/balance.js';
8
+
9
+ const DEFAULT_VALIDATOR_ADDRESS_PATH = ['rao', 'va'];
10
+ const VALIDATOR_ENTRY_MARK = Symbol('validator-entry-mark');
11
+ const VALIDATOR_BALANCE_MARK = Symbol('validator-balance-mark');
12
+
13
+ export default class ValidatorEntryValidationScenarioBase extends OperationValidationScenarioBase {
14
+ constructor({
15
+ title,
16
+ setupScenario,
17
+ buildValidPayload,
18
+ assertStateUnchanged,
19
+ mutateEntry = entryPassThrough,
20
+ mutateDecodedEntry = null,
21
+ selectNode = defaultSelectNode,
22
+ validatorAddressPath = DEFAULT_VALIDATOR_ADDRESS_PATH,
23
+ expectedLogs,
24
+ mutatePayload,
25
+ applyInvalidPayload,
26
+ failNextBalanceAdd = false,
27
+ failNextBalanceUpdate = false
28
+ }) {
29
+ if (typeof mutateEntry !== 'function') {
30
+ throw new Error('Validator entry validation scenario requires a mutateEntry function.');
31
+ }
32
+
33
+ super({
34
+ title,
35
+ setupScenario,
36
+ buildValidPayload,
37
+ mutatePayload: mutatePayload ?? passThroughPayload,
38
+ applyInvalidPayload:
39
+ typeof applyInvalidPayload === 'function'
40
+ ? applyInvalidPayload
41
+ : createApplyInvalidPayload({
42
+ selectNode,
43
+ validatorAddressPath,
44
+ mutateEntry,
45
+ mutateDecodedEntry,
46
+ failNextBalanceAdd,
47
+ failNextBalanceUpdate
48
+ }),
49
+ assertStateUnchanged,
50
+ expectedLogs
51
+ });
52
+ }
53
+ }
54
+
55
+ function passThroughPayload(_t, payload) {
56
+ return payload;
57
+ }
58
+
59
+ function entryPassThrough(entry) {
60
+ return entry;
61
+ }
62
+
63
+ function defaultSelectNode(context) {
64
+ return context.adminBootstrap ?? context.bootstrap ?? context.peers?.[0] ?? null;
65
+ }
66
+
67
+ function createApplyInvalidPayload({
68
+ selectNode,
69
+ validatorAddressPath,
70
+ mutateEntry,
71
+ mutateDecodedEntry,
72
+ failNextBalanceAdd,
73
+ failNextBalanceUpdate
74
+ }) {
75
+ return async (context, payload, t, validPayload) => {
76
+ const node = selectNode(context);
77
+ if (!node?.base) {
78
+ throw new Error('Validator entry validation scenario requires a writable node.');
79
+ }
80
+
81
+ const validatorAddress = extractValidatorAddress(validPayload ?? payload, validatorAddressPath);
82
+ if (!validatorAddress) {
83
+ throw new Error('Validator address could not be derived from payload.');
84
+ }
85
+
86
+ const cleanup = patchValidatorEntry({
87
+ base: node.base,
88
+ mutateEntry,
89
+ mutateDecodedEntry,
90
+ failNextBalanceAdd,
91
+ failNextBalanceUpdate,
92
+ context,
93
+ t,
94
+ validatorAddressString: validatorAddress.string,
95
+ validatorAddressBuffer: validatorAddress.buffer
96
+ });
97
+
98
+ try {
99
+ await node.base.append(payload);
100
+ await node.base.update();
101
+ await eventFlush();
102
+ } finally {
103
+ await cleanup();
104
+ }
105
+ };
106
+ }
107
+
108
+ function patchValidatorEntry({
109
+ base,
110
+ mutateEntry,
111
+ mutateDecodedEntry,
112
+ failNextBalanceAdd,
113
+ failNextBalanceUpdate,
114
+ context,
115
+ t,
116
+ validatorAddressString,
117
+ validatorAddressBuffer
118
+ }) {
119
+ const originalApply = base._handlers.apply;
120
+ const originalDecode = nodeEntryUtils.decode;
121
+ const balancePrototype = Object.getPrototypeOf(BALANCE_ZERO);
122
+ const originalAdd = balancePrototype.add;
123
+ const originalUpdate = balancePrototype.update;
124
+
125
+ let shouldInterceptNextApply = true;
126
+ let targetEntryBuffer = null;
127
+ let shouldFailNextAdd = false;
128
+ let shouldMarkNextBalance = false;
129
+
130
+ const setTargetEntryBuffer = buffer => {
131
+ if (buffer && b4a.isBuffer(buffer)) {
132
+ targetEntryBuffer = b4a.from(buffer);
133
+ } else {
134
+ targetEntryBuffer = null;
135
+ }
136
+ };
137
+
138
+ const isTargetEntryBuffer = buffer =>
139
+ targetEntryBuffer &&
140
+ b4a.isBuffer(buffer) &&
141
+ buffer.length === targetEntryBuffer.length &&
142
+ b4a.equals(buffer, targetEntryBuffer);
143
+
144
+ nodeEntryUtils.decode = function patchedDecode(buffer) {
145
+ const decoded = originalDecode(buffer);
146
+ if (isTargetEntryBuffer(buffer)) {
147
+ markValidatorBuffer(buffer);
148
+ shouldFailNextAdd = failNextBalanceAdd;
149
+ shouldMarkNextBalance = failNextBalanceUpdate;
150
+
151
+ if (mutateDecodedEntry) {
152
+ const mutated = mutateDecodedEntry(decoded, {
153
+ context,
154
+ t,
155
+ validatorAddress: validatorAddressString
156
+ });
157
+ if (mutated && typeof mutated === 'object') {
158
+ return mutated;
159
+ }
160
+ }
161
+ }
162
+ return decoded;
163
+ };
164
+
165
+ balancePrototype.add = function patchedAdd(balance) {
166
+ if (shouldFailNextAdd) {
167
+ shouldFailNextAdd = false;
168
+ return null;
169
+ }
170
+ const result = originalAdd.call(this, balance);
171
+ if (shouldMarkNextBalance && result) {
172
+ shouldMarkNextBalance = false;
173
+ result[VALIDATOR_BALANCE_MARK] = true;
174
+ }
175
+ return result;
176
+ };
177
+
178
+ balancePrototype.update = function patchedUpdate(nodeEntryBuffer) {
179
+ if (this?.[VALIDATOR_BALANCE_MARK]) {
180
+ this[VALIDATOR_BALANCE_MARK] = false;
181
+ return null;
182
+ }
183
+ return originalUpdate.call(this, nodeEntryBuffer);
184
+ };
185
+
186
+ base._handlers.apply = async function patchedApply(nodes, view, baseCtx) {
187
+ if (!shouldInterceptNextApply) {
188
+ return originalApply.call(this, nodes, view, baseCtx);
189
+ }
190
+
191
+ shouldInterceptNextApply = false;
192
+ const originalBatch = view.batch;
193
+ view.batch = function patchedBatch(...args) {
194
+ const batch = originalBatch.apply(this, args);
195
+ if (!batch || typeof batch.get !== 'function') {
196
+ return batch;
197
+ }
198
+
199
+ const originalGet = batch.get.bind(batch);
200
+ let hasMutatedEntry = false;
201
+
202
+ batch.get = async key => {
203
+ if (
204
+ !hasMutatedEntry &&
205
+ isValidatorKeyMatch(key, validatorAddressString, validatorAddressBuffer)
206
+ ) {
207
+ hasMutatedEntry = true;
208
+ const entry = await originalGet(key);
209
+ if (entry?.value && b4a.isBuffer(entry.value)) {
210
+ setTargetEntryBuffer(entry.value);
211
+ markValidatorBuffer(entry.value);
212
+ } else {
213
+ setTargetEntryBuffer(null);
214
+ }
215
+
216
+ const mutation = await mutateEntry(entry, {
217
+ context,
218
+ t,
219
+ validatorAddress: validatorAddressString
220
+ });
221
+
222
+ const mutatedEntry = applyEntryMutation(entry, mutation);
223
+ if (mutatedEntry?.value && b4a.isBuffer(mutatedEntry.value)) {
224
+ setTargetEntryBuffer(mutatedEntry.value);
225
+ markValidatorBuffer(mutatedEntry.value);
226
+ } else if (mutatedEntry === null) {
227
+ setTargetEntryBuffer(null);
228
+ }
229
+ return mutatedEntry;
230
+ }
231
+
232
+ return originalGet(key);
233
+ };
234
+
235
+ return batch;
236
+ };
237
+
238
+ try {
239
+ return await originalApply.call(this, nodes, view, baseCtx);
240
+ } finally {
241
+ view.batch = originalBatch;
242
+ }
243
+ };
244
+
245
+ return async () => {
246
+ base._handlers.apply = originalApply;
247
+ nodeEntryUtils.decode = originalDecode;
248
+ const prototype = Object.getPrototypeOf(BALANCE_ZERO);
249
+ prototype.add = originalAdd;
250
+ prototype.update = originalUpdate;
251
+ };
252
+ }
253
+
254
+ function applyEntryMutation(entry, mutation) {
255
+ if (typeof mutation === 'undefined') {
256
+ return entry;
257
+ }
258
+
259
+ if (mutation === null) {
260
+ return null;
261
+ }
262
+
263
+ if (entry && (b4a.isBuffer(mutation) || mutation instanceof Uint8Array)) {
264
+ const wrapped = { ...entry, value: b4a.from(mutation) };
265
+ markValidatorBuffer(wrapped.value);
266
+ return wrapped;
267
+ }
268
+
269
+ if (mutation && typeof mutation === 'object') {
270
+ if (mutation.value && b4a.isBuffer(mutation.value)) {
271
+ markValidatorBuffer(mutation.value);
272
+ }
273
+ return mutation;
274
+ }
275
+
276
+ throw new Error('Invalid validator entry mutation result.');
277
+ }
278
+
279
+ function extractValidatorAddress(payloadBuffer, path) {
280
+ const decoded = safeDecodeApplyOperation(payloadBuffer);
281
+ if (!decoded) return null;
282
+
283
+ const value = Array.isArray(path) && path.length > 0 ? traversePath(decoded, path) : null;
284
+ if (!value || !b4a.isBuffer(value) || value.length === 0) {
285
+ return null;
286
+ }
287
+
288
+ const addressString = addressUtils.bufferToAddress(value);
289
+ if (!addressString) {
290
+ return null;
291
+ }
292
+
293
+ return { buffer: value, string: addressString };
294
+ }
295
+
296
+ function traversePath(payload, path) {
297
+ return path.reduce((current, segment) => (current ? current[segment] : null), payload);
298
+ }
299
+
300
+ function isValidatorKeyMatch(key, targetString, targetBuffer) {
301
+ if (typeof key === 'string') {
302
+ return key === targetString;
303
+ }
304
+ if (b4a.isBuffer(key) && targetBuffer) {
305
+ return b4a.equals(key, targetBuffer);
306
+ }
307
+ return false;
308
+ }
309
+
310
+ function markValidatorBuffer(buffer) {
311
+ if (buffer && typeof buffer === 'object') {
312
+ buffer[VALIDATOR_ENTRY_MARK] = true;
313
+ }
314
+ }
@@ -0,0 +1,18 @@
1
+ import b4a from 'b4a';
2
+ import ValidatorEntryValidationScenarioBase from './base/validatorEntryValidationScenarioBase.js';
3
+
4
+ export default class ValidatorEntryInvalidBalanceScenario extends ValidatorEntryValidationScenarioBase {
5
+ constructor(options) {
6
+ super({
7
+ ...options,
8
+ mutateDecodedEntry: decoded => {
9
+ if (!decoded) return decoded;
10
+ return {
11
+ ...decoded,
12
+ balance: b4a.alloc(1)
13
+ };
14
+ },
15
+ expectedLogs: options?.expectedLogs ?? ['Invalid validator balance.']
16
+ });
17
+ }
18
+ }
@@ -0,0 +1,43 @@
1
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
2
+ import OperationValidationScenarioBase from './base/OperationValidationScenarioBase.js';
3
+
4
+ export default class WriterKeyExistsValidationScenario extends OperationValidationScenarioBase {
5
+ constructor({
6
+ title,
7
+ setupScenario,
8
+ buildValidPayload,
9
+ assertStateUnchanged,
10
+ mutatePayload = passThroughPayload,
11
+ applyInvalidPayload,
12
+ expectedLogs
13
+ }) {
14
+ super({
15
+ title,
16
+ setupScenario,
17
+ buildValidPayload,
18
+ mutatePayload,
19
+ applyInvalidPayload: applyInvalidPayload ?? defaultApplyInvalidPayload,
20
+ assertStateUnchanged,
21
+ expectedLogs
22
+ });
23
+ }
24
+ }
25
+
26
+ function passThroughPayload(_t, payload) {
27
+ return payload;
28
+ }
29
+
30
+ async function defaultApplyInvalidPayload(context, invalidPayload, _t, validPayload) {
31
+ const adminNode = context.adminBootstrap ?? context.bootstrap;
32
+ if (!adminNode) {
33
+ throw new Error('Writer key validation scenario requires an admin bootstrap node.');
34
+ }
35
+
36
+ await adminNode.base.append(validPayload);
37
+ await adminNode.base.update();
38
+ await eventFlush();
39
+
40
+ await adminNode.base.append(invalidPayload);
41
+ await adminNode.base.update();
42
+ await eventFlush();
43
+ }
@@ -0,0 +1,53 @@
1
+ import { test } from 'brittle';
2
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
3
+ import setupDisableInitializationScenario, {
4
+ buildDisableInitializationPayload,
5
+ assertInitializationDisabledState
6
+ } from './disableInitializationScenarioHelpers.js';
7
+ import { safeDecodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
8
+
9
+ export default function disableInitializationAlreadyDisabledScenario() {
10
+ test('State.apply disableInitialization aborts when initialization already disabled', async t => {
11
+ const context = await setupDisableInitializationScenario(t);
12
+ const adminNode = context.adminBootstrap;
13
+ const readerNode = context.peers[1];
14
+
15
+ const firstPayload = await buildDisableInitializationPayload(context);
16
+ await adminNode.base.append(firstPayload);
17
+ await adminNode.base.update();
18
+ await eventFlush();
19
+ await assertInitializationDisabledState(t, adminNode.base, firstPayload);
20
+
21
+ const expectedLog = 'Balance initialization already disabled.';
22
+ const capturedLogs = [];
23
+ const originalConsoleError = console.error;
24
+ console.error = (...args) => {
25
+ capturedLogs.push(args);
26
+ originalConsoleError(...args);
27
+ };
28
+
29
+ const secondPayload = await buildDisableInitializationPayload(context);
30
+ try {
31
+ await adminNode.base.append(secondPayload);
32
+ await adminNode.base.update();
33
+ await eventFlush();
34
+ } finally {
35
+ console.error = originalConsoleError;
36
+ }
37
+
38
+ const decodedSecondPayload = safeDecodeApplyOperation(secondPayload);
39
+ t.ok(decodedSecondPayload?.cao?.tx, 'fixtures decode');
40
+ const secondTxKey = decodedSecondPayload.cao.tx.toString('hex');
41
+ const secondTxEntry = await adminNode.base.view.get(secondTxKey);
42
+ t.is(secondTxEntry, null, 'second disable tx not recorded');
43
+
44
+ await assertInitializationDisabledState(t, adminNode.base);
45
+ await context.sync();
46
+ await assertInitializationDisabledState(t, readerNode.base);
47
+
48
+ const logFound = capturedLogs.some(args =>
49
+ args.some(arg => typeof arg === 'string' && arg.includes(expectedLog))
50
+ );
51
+ t.ok(logFound, `expected apply log "${expectedLog}" was emitted`);
52
+ });
53
+ }
@@ -0,0 +1,24 @@
1
+ import { test } from 'brittle';
2
+ import setupDisableInitializationScenario, {
3
+ buildDisableInitializationPayload,
4
+ assertInitializationDisabledState
5
+ } from './disableInitializationScenarioHelpers.js';
6
+ import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
7
+
8
+ export default function disableInitializationHappyPathScenario() {
9
+ test('State.apply disableInitialization disables initialization - happy path', async t => {
10
+ const context = await setupDisableInitializationScenario(t);
11
+ const adminNode = context.adminBootstrap;
12
+ const readerNode = context.peers[1];
13
+
14
+ const disablePayload = await buildDisableInitializationPayload(context);
15
+ await adminNode.base.append(disablePayload);
16
+ await adminNode.base.update();
17
+ await eventFlush();
18
+
19
+ await assertInitializationDisabledState(t, adminNode.base, disablePayload);
20
+
21
+ await context.sync();
22
+ await assertInitializationDisabledState(t, readerNode.base, disablePayload);
23
+ });
24
+ }
@@ -0,0 +1,197 @@
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, EntryType } from '../../../../../src/utils/constants.js';
11
+ import { safeDecodeApplyOperation, safeEncodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
12
+ import { safeWriteUInt32BE } from '../../../../../src/utils/buffer.js';
13
+ import { buildAddAdminRequesterPayload } from '../addAdmin/addAdminScenarioHelpers.js';
14
+
15
+ export async function setupDisableInitializationScenario(t) {
16
+ const context = await setupStateNetwork({
17
+ nodes: 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
+ export async function setupDisableInitializationAdminReaderScenario(t) {
33
+ const context = await setupDisableInitializationScenario(t);
34
+ const adminNode = context.adminBootstrap;
35
+ const readerNode = context.peers[1];
36
+
37
+ if (!readerNode) {
38
+ throw new Error('Disable initialization scenarios require at least one reader node.');
39
+ }
40
+
41
+ return { context, adminNode, readerNode };
42
+ }
43
+
44
+ async function bootstrapAdmin(context) {
45
+ const adminNode = context.adminBootstrap;
46
+ const payload = await buildAddAdminRequesterPayload(context);
47
+
48
+ await adminNode.base.append(payload);
49
+ await adminNode.base.update();
50
+ await eventFlush();
51
+ }
52
+
53
+ export async function buildDisableInitializationPayload(context) {
54
+ const adminNode = context.adminBootstrap;
55
+ const txValidity = await deriveIndexerSequenceState(adminNode.base);
56
+
57
+ return CompleteStateMessageOperations.assembleDisableInitializationMessage(
58
+ adminNode.wallet,
59
+ adminNode.base.local.key,
60
+ txValidity
61
+ );
62
+ }
63
+
64
+ export async function buildDisableInitializationPayloadWithTxValidity(context, txValidity) {
65
+ const adminNode = context.adminBootstrap;
66
+ return CompleteStateMessageOperations.assembleDisableInitializationMessage(
67
+ adminNode.wallet,
68
+ adminNode.base.local.key,
69
+ txValidity
70
+ );
71
+ }
72
+
73
+ export async function assertInitializationDisabledState(t, base, payload) {
74
+ const initializationEntry = await base.view.get(EntryType.INITIALIZATION);
75
+ t.ok(initializationEntry, 'initialization entry exists');
76
+ t.ok(
77
+ b4a.equals(initializationEntry.value, safeWriteUInt32BE(0, 0)),
78
+ 'initialization flag cleared'
79
+ );
80
+
81
+ if (!payload) return;
82
+
83
+ const decodedPayload = safeDecodeApplyOperation(payload);
84
+ t.ok(decodedPayload?.cao?.tx, 'disable payload decodes');
85
+
86
+ const txKey = decodedPayload.cao.tx.toString('hex');
87
+ const txEntry = await base.view.get(txKey);
88
+ t.ok(txEntry, 'disable initialization tx recorded to prevent replays');
89
+ }
90
+
91
+ export function mutateDisableInitializationPayloadForInvalidSchema(t, validPayload) {
92
+ const decodedPayload = safeDecodeApplyOperation(validPayload);
93
+ t.ok(decodedPayload, 'fixtures decode');
94
+
95
+ decodedPayload.cao.tx = b4a.alloc(decodedPayload.cao.tx.length);
96
+ return safeEncodeApplyOperation(decodedPayload);
97
+ }
98
+
99
+ export async function assertDisableInitializationFailureState(
100
+ t,
101
+ context,
102
+ { skipSync = false, validPayload = null } = {}
103
+ ) {
104
+ const adminNode = context.adminBootstrap;
105
+ const readerNodes = context.peers.slice(1);
106
+
107
+ await assertInitializationEnabled(t, adminNode.base);
108
+ await assertOperationNotRecorded(t, adminNode.base, validPayload);
109
+
110
+ if (!skipSync) {
111
+ await context.sync();
112
+ for (const reader of readerNodes) {
113
+ await assertInitializationEnabled(t, reader.base);
114
+ await assertOperationNotRecorded(t, reader.base, validPayload);
115
+ }
116
+ }
117
+ }
118
+
119
+ async function assertInitializationEnabled(t, base) {
120
+ const initializationEntry = await base.view.get(EntryType.INITIALIZATION);
121
+ t.ok(initializationEntry, 'initialization entry exists');
122
+ t.ok(
123
+ b4a.equals(initializationEntry.value, safeWriteUInt32BE(1, 0)),
124
+ 'initialization flag remains enabled'
125
+ );
126
+ }
127
+
128
+ async function assertOperationNotRecorded(t, base, payload) {
129
+ if (!payload) return;
130
+
131
+ const decodedPayload = safeDecodeApplyOperation(payload);
132
+ if (!decodedPayload?.cao?.tx) return;
133
+
134
+ const txKey = decodedPayload.cao.tx.toString('hex');
135
+ const txEntry = await base.view.get(txKey);
136
+ t.is(txEntry, null, 'disable initialization tx not recorded');
137
+ }
138
+
139
+ export function bypassDisableInitializationAlreadyDisabledGuardOnce(context) {
140
+ const adminNode = context.adminBootstrap;
141
+ if (!adminNode?.base) {
142
+ throw new Error('Disable initialization guard bypass requires an admin bootstrap node.');
143
+ }
144
+
145
+ const originalApply = adminNode.base._handlers.apply;
146
+ let shouldBypass = true;
147
+
148
+ adminNode.base._handlers.apply = async (nodes, view, baseCtx) => {
149
+ if (!shouldBypass) {
150
+ return originalApply(nodes, view, baseCtx);
151
+ }
152
+
153
+ shouldBypass = false;
154
+ const previousBatch = view.batch;
155
+ const boundBatch = previousBatch.bind(view);
156
+
157
+ view.batch = function patchedBatch(...args) {
158
+ const batch = boundBatch(...args);
159
+ const originalGet = batch.get?.bind(batch);
160
+ if (typeof originalGet === 'function') {
161
+ batch.get = async key => {
162
+ const entry = await originalGet(key);
163
+ if (isInitializationEntryKey(key) && entry?.value) {
164
+ return {
165
+ ...entry,
166
+ value: safeWriteUInt32BE(1, 0)
167
+ };
168
+ }
169
+ return entry;
170
+ };
171
+ }
172
+ return batch;
173
+ };
174
+
175
+ try {
176
+ return await originalApply(nodes, view, baseCtx);
177
+ } finally {
178
+ view.batch = previousBatch;
179
+ }
180
+ };
181
+
182
+ return () => {
183
+ adminNode.base._handlers.apply = originalApply;
184
+ };
185
+ }
186
+
187
+ function isInitializationEntryKey(key) {
188
+ if (typeof key === 'string') {
189
+ return key === EntryType.INITIALIZATION;
190
+ }
191
+ if (b4a.isBuffer(key)) {
192
+ return b4a.equals(key, b4a.from(EntryType.INITIALIZATION));
193
+ }
194
+ return false;
195
+ }
196
+
197
+ export default setupDisableInitializationScenario;