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.
- package/.dockerignore +16 -0
- package/.github/workflows/acceptance-tests.yml +7 -0
- package/.github/workflows/publish.yml +40 -0
- package/.github/workflows/{CI.yml → unit-tests.yml} +1 -1
- package/README.md +175 -50
- package/docker-compose.yml +16 -0
- package/dockerfile +41 -0
- package/docs/fee_distribution.md +89 -0
- package/msb.mjs +12 -14
- package/package.json +8 -4
- package/rpc/constants.mjs +4 -1
- package/rpc/handlers.mjs +109 -66
- package/rpc/routes/v1.mjs +3 -1
- package/rpc/rpc_services.js +126 -0
- package/rpc/utils/confirmedParameter.mjs +17 -0
- package/rpc/utils/url.mjs +38 -0
- package/src/core/network/Network.js +27 -10
- package/src/core/network/identity/NetworkWalletFactory.js +78 -0
- package/src/core/network/services/ConnectionManager.js +2 -2
- package/src/core/network/services/ValidatorObserverService.js +7 -4
- package/src/core/state/State.js +28 -22
- package/src/index.js +197 -385
- package/src/utils/cliCommands.js +280 -0
- package/src/utils/constants.js +3 -1
- package/tests/acceptance/v1/account/account.test.mjs +123 -0
- package/tests/acceptance/v1/balance/balance.test.mjs +55 -0
- package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +111 -0
- package/tests/acceptance/v1/confirmed-length/confirmed-length.test.mjs +19 -0
- package/tests/acceptance/v1/fee/fee.test.mjs +11 -0
- package/tests/acceptance/v1/rpc.test.mjs +62 -291
- package/tests/acceptance/v1/tx/tx.test.mjs +98 -0
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +195 -0
- package/tests/acceptance/v1/tx-hashes/tx-hashes.test.mjs +72 -0
- package/tests/acceptance/v1/tx-payloads-bulk/tx-payloads-bulk.test.mjs +27 -0
- package/tests/acceptance/v1/txv/txv.test.mjs +11 -0
- package/tests/acceptance/v1/unconfirmed-length/unconfirmed-length.test.mjs +11 -0
- package/tests/helpers/StateNetworkFactory.js +157 -0
- package/tests/helpers/autobaseTestHelpers.js +369 -0
- package/tests/helpers/createTestSignature.js +12 -0
- package/tests/helpers/transactionPayloads.mjs +78 -0
- package/tests/unit/network/NetworkWalletFactory.test.js +156 -0
- package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +38 -0
- package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +273 -0
- package/tests/unit/state/apply/addAdmin/adminEntryEncodingFailureScenario.js +30 -0
- package/tests/unit/state/apply/addAdmin/adminEntryExistsScenario.js +78 -0
- package/tests/unit/state/apply/addAdmin/nodeEntryInitializationFailureScenario.js +30 -0
- package/tests/unit/state/apply/addAdmin/nonBootstrapNodeScenario.js +68 -0
- package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +155 -0
- package/tests/unit/state/apply/addIndexer/addIndexerHappyPathScenario.js +39 -0
- package/tests/unit/state/apply/addIndexer/addIndexerMultipleIndexersInTheNetworkScenario.js +167 -0
- package/tests/unit/state/apply/addIndexer/addIndexerPretenderAlreadyIndexerScenario.js +21 -0
- package/tests/unit/state/apply/addIndexer/addIndexerPretenderNotWriterScenario.js +21 -0
- package/tests/unit/state/apply/addIndexer/addIndexerRemoveAndReAddScenario.js +186 -0
- package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +445 -0
- package/tests/unit/state/apply/addIndexer/addIndexerWriterKeyAlreadyRegisteredScenario.js +32 -0
- package/tests/unit/state/apply/addIndexer/state.apply.addIndexer.test.js +297 -0
- package/tests/unit/state/apply/addWriter/addWriterHappyPathScenario.js +41 -0
- package/tests/unit/state/apply/addWriter/addWriterInvalidValidatorSignatureScenario.js +32 -0
- package/tests/unit/state/apply/addWriter/addWriterNewWkScenario.js +149 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterAlreadyWriterScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterBalanceInsufficientScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterEntryDecodeFailureScenario.js +19 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterEntryMissingScenario.js +19 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterIndexerScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterNotWhitelistedScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +757 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeBalanceUpdateFailureScenario.js +50 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeInsufficientBalanceScenario.js +29 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeInvalidBalanceScenario.js +29 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeInvalidEntryScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeStakedBalanceFailureScenario.js +37 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeSubtractFailureScenario.js +42 -0
- package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +105 -0
- package/tests/unit/state/apply/addWriter/addWriterWriterKeyMismatchScenario.js +54 -0
- package/tests/unit/state/apply/addWriter/addWriterWriterKeyOwnershipScenario.js +54 -0
- package/tests/unit/state/apply/addWriter/addWriterZeroWriterKeyScenario.js +29 -0
- package/tests/unit/state/apply/addWriter/state.apply.addWriter.test.js +309 -0
- package/tests/unit/state/apply/adminRecovery/adminRecoveryHappyPathScenario.js +30 -0
- package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +866 -0
- package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +439 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistBanAndReapplyScenario.js +78 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistExistingReaderHappyPathScenario.js +98 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistFeeAfterDisableScenario.js +66 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistHappyPathScenario.js +55 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistInsufficientAdminBalanceScenario.js +103 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistNodeAlreadyWhitelistedScenario.js +60 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +191 -0
- package/tests/unit/state/apply/appendWhitelist/state.apply.appendWhitelist.test.js +220 -0
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationHappyPathScenario.js +82 -0
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +106 -0
- package/tests/unit/state/apply/balanceInitialization/invalidAmountScenario.js +45 -0
- package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +81 -0
- package/tests/unit/state/apply/balanceInitialization/state.apply.balanceInitialization.test.js +189 -0
- package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +155 -0
- package/tests/unit/state/apply/banValidator/banValidatorHappyPathScenario.js +36 -0
- package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +534 -0
- package/tests/unit/state/apply/banValidator/banValidatorSequentialBansScenario.js +74 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetDecodeFailureScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetIndexerScenario.js +32 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetNodeEntryMissingScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetRoleUpdateFailureScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/banValidatorWhitelistedNonWriterScenario.js +38 -0
- package/tests/unit/state/apply/banValidator/banValidatorWhitelistedZeroBalanceScenario.js +91 -0
- package/tests/unit/state/apply/banValidator/banValidatorWithdrawFailureScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/state.apply.banValidator.test.js +266 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +142 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentHappyPathScenario.js +26 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentIncompleteOperationScenario.js +94 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentInvalidDeploymentEntryScenario.js +37 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentMultipleBootstrapScenario.js +86 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +344 -0
- package/tests/unit/state/apply/bootstrapDeployment/invalidValidatorNodeEntryScenario.js +57 -0
- package/tests/unit/state/apply/bootstrapDeployment/state.apply.bootstrapDeployment.test.js +429 -0
- package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +119 -0
- package/tests/unit/state/apply/common/access-control/adminEntryDecodeFailureScenario.js +130 -0
- package/tests/unit/state/apply/common/access-control/adminEntryExistsScenario.js +93 -0
- package/tests/unit/state/apply/common/access-control/adminEntryMissingScenario.js +108 -0
- package/tests/unit/state/apply/common/access-control/adminOnlyGuardScenario.js +126 -0
- package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +120 -0
- package/tests/unit/state/apply/common/access-control/roleAccessOperationValidationScenario.js +50 -0
- package/tests/unit/state/apply/common/adminControlOperationValidationScenario.js +56 -0
- package/tests/unit/state/apply/common/balances/adminEntryUpdateFailureScenario.js +52 -0
- package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +197 -0
- package/tests/unit/state/apply/common/balances/feeDecodeFailureScenario.js +52 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceDecodeFailureScenario.js +15 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceFeeApplicationFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceInsufficientScenario.js +15 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceUpdateFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/validatorEntryRewardFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/validatorEntryUpdateFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/validatorNodeEntryDecodeFailureScenario.js +40 -0
- package/tests/unit/state/apply/common/base/OperationValidationScenarioBase.js +114 -0
- package/tests/unit/state/apply/common/commonScenarioHelper.js +103 -0
- package/tests/unit/state/apply/common/indexer/indexerNodeEntryDecodeFailureScenario.js +36 -0
- package/tests/unit/state/apply/common/indexer/indexerNodeEntryMissingScenario.js +36 -0
- package/tests/unit/state/apply/common/indexer/indexerRoleUpdateFailureScenario.js +29 -0
- package/tests/unit/state/apply/common/indexer/indexerSequenceStateInvalidScenario.js +66 -0
- package/tests/unit/state/apply/common/invalidMessageComponentValidationScenario.js +84 -0
- package/tests/unit/state/apply/common/nodeEntryInitializationFailureScenario.js +47 -0
- package/tests/unit/state/apply/common/operationAlreadyAppliedScenario.js +85 -0
- package/tests/unit/state/apply/common/payload-structure/addressWithInvalidPublicKeyScenario.js +52 -0
- package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +49 -0
- package/tests/unit/state/apply/common/payload-structure/invalidAddressValidationScenario.js +73 -0
- package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +71 -0
- package/tests/unit/state/apply/common/payload-structure/invalidPayloadValidationScenario.js +31 -0
- package/tests/unit/state/apply/common/payload-structure/invalidSignatureValidationScenario.js +142 -0
- package/tests/unit/state/apply/common/payload-structure/partialOperationValidationScenario.js +87 -0
- package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +70 -0
- package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +72 -0
- package/tests/unit/state/apply/common/requester/requesterNodeEntryMissingScenario.js +36 -0
- package/tests/unit/state/apply/common/requesterAddressValidationScenario.js +44 -0
- package/tests/unit/state/apply/common/requesterPublicKeyValidationScenario.js +25 -0
- package/tests/unit/state/apply/common/transactionValidityMismatchScenario.js +98 -0
- package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +201 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorEntryDecodeFailureScenario.js +17 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorEntryMissingScenario.js +44 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorInactiveScenario.js +19 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorWriterKeyMismatchScenario.js +18 -0
- package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +314 -0
- package/tests/unit/state/apply/common/validatorEntryValidation/validatorEntryInvalidBalanceScenario.js +18 -0
- package/tests/unit/state/apply/common/writerKeyExistsValidationScenario.js +43 -0
- package/tests/unit/state/apply/disableInitialization/disableInitializationAlreadyDisabledScenario.js +53 -0
- package/tests/unit/state/apply/disableInitialization/disableInitializationHappyPathScenario.js +24 -0
- package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +197 -0
- package/tests/unit/state/apply/disableInitialization/state.apply.disableInitialization.test.js +161 -0
- package/tests/unit/state/apply/missing-tests.md +18 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerHappyPathScenario.js +58 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerReAddAndRemoveAgainScenario.js +98 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerRemoveMultipleIndexersScenario.js +167 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +428 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerTargetNotIndexerScenario.js +22 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerWriterKeyMissingScenario.js +20 -0
- package/tests/unit/state/apply/removeIndexer/state.apply.removeIndexer.test.js +291 -0
- package/tests/unit/state/apply/removeWriter/removeWriterAndAddWriterAgainScenario.js +87 -0
- package/tests/unit/state/apply/removeWriter/removeWriterHappyPathScenario.js +38 -0
- package/tests/unit/state/apply/removeWriter/removeWriterInvalidValidatorSignatureScenario.js +32 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterBalanceInsufficientScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryDecodeFailureScenario.js +19 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryMissingScenario.js +19 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterIndexerScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterNotWriterScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterRoleUpdateFailureScenario.js +19 -0
- package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +344 -0
- package/tests/unit/state/apply/removeWriter/removeWriterThroughWriterValidatorScenario.js +113 -0
- package/tests/unit/state/apply/removeWriter/removeWriterUnstakeFailureScenario.js +33 -0
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyMismatchScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyOwnershipScenario.js +26 -0
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyRegistryMissingScenario.js +22 -0
- package/tests/unit/state/apply/removeWriter/state.apply.removeWriter.test.js +307 -0
- package/tests/unit/state/apply/state.apply.test.js +24 -0
- package/tests/unit/state/apply/transfer/state.apply.transfer.test.js +819 -0
- package/tests/unit/state/apply/transfer/transferContractSchemaValidationScenario.js +22 -0
- package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +137 -0
- package/tests/unit/state/apply/transfer/transferDoubleSpendSameBatchScenario.js +63 -0
- package/tests/unit/state/apply/transfer/transferDoubleSpendSingleValidatorScenario.js +67 -0
- package/tests/unit/state/apply/transfer/transferExistingRecipientAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferExistingRecipientZeroAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferHandlerGuardScenarios.js +22 -0
- package/tests/unit/state/apply/transfer/transferHappyPathScenario.js +8 -0
- package/tests/unit/state/apply/transfer/transferInvalidIncomingDataScenario.js +66 -0
- package/tests/unit/state/apply/transfer/transferNewRecipientAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferNewRecipientZeroAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +1167 -0
- package/tests/unit/state/apply/transfer/transferSelfTransferAmountScenario.js +38 -0
- package/tests/unit/state/apply/transfer/transferSelfTransferZeroAmountScenario.js +38 -0
- package/tests/unit/state/apply/transfer/transferValidatorRecipientAmountScenario.js +38 -0
- package/tests/unit/state/apply/transfer/transferValidatorRecipientZeroAmountScenario.js +38 -0
- package/tests/unit/state/apply/txOperation/state.apply.txOperation.test.js +318 -0
- package/tests/unit/state/apply/txOperation/txOperationBootstrapNotRegisteredScenario.js +70 -0
- package/tests/unit/state/apply/txOperation/txOperationDifferentValidatorCreatorHappyPathScenario.js +23 -0
- package/tests/unit/state/apply/txOperation/txOperationInvalidDeploymentEntryScenario.js +48 -0
- package/tests/unit/state/apply/txOperation/txOperationInvalidFeeAmountScenario.js +39 -0
- package/tests/unit/state/apply/txOperation/txOperationInvalidSubnetCreatorAddressScenario.js +46 -0
- package/tests/unit/state/apply/txOperation/txOperationRequesterCreatorHappyPathScenario.js +21 -0
- package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +429 -0
- package/tests/unit/state/apply/txOperation/txOperationStandardHappyPathScenario.js +21 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddCreatorBalanceFailureScenario.js +26 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBalanceFailureScenario.js +25 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBonusFailureScenario.js +27 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeCreatorEntryScenario.js +18 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeRequesterEntryScenario.js +17 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeValidatorEntryScenario.js +31 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardBypassScenario.js +49 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +92 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInsufficientRequesterBalanceScenario.js +28 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidCreatorBalanceScenario.js +29 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterBalanceScenario.js +28 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterEntryScenario.js +17 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidValidatorBalanceScenario.js +33 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeMissingCreatorEntryScenario.js +18 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeSubtractFailureScenario.js +25 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateCreatorBalanceFailureScenario.js +26 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateFailureScenario.js +25 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBalanceFailureScenario.js +26 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBonusFailureScenario.js +27 -0
- package/tests/unit/state/apply/txOperation/txOperationValidatorCreatorHappyPathScenario.js +21 -0
- package/tests/unit/state/stateModule.test.js +1 -0
- package/tests/unit/state/stateTestUtils.js +5 -1
- package/.env +0 -3
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import CompleteStateMessageOperations from '../../../../../src/messages/completeStateMessages/CompleteStateMessageOperations.js';
|
|
3
|
+
import { deriveIndexerSequenceState, eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
|
|
4
|
+
import {
|
|
5
|
+
setupAddWriterScenario,
|
|
6
|
+
selectWriterPeer,
|
|
7
|
+
promotePeerToWriter
|
|
8
|
+
} from '../addWriter/addWriterScenarioHelpers.js';
|
|
9
|
+
import nodeEntryUtils from '../../../../../src/core/state/utils/nodeEntry.js';
|
|
10
|
+
import { toBalance } from '../../../../../src/core/state/utils/balance.js';
|
|
11
|
+
import transactionUtils from '../../../../../src/core/state/utils/transaction.js';
|
|
12
|
+
import addressUtils from '../../../../../src/core/state/utils/address.js';
|
|
13
|
+
import { safeDecodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
|
|
14
|
+
import nodeRoleUtils from '../../../../../src/core/state/utils/roles.js';
|
|
15
|
+
|
|
16
|
+
export function selectIndexerCandidatePeer(context, offset = 0) {
|
|
17
|
+
return selectWriterPeer(context, offset);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function setupAddIndexerScenario(t, options = {}) {
|
|
21
|
+
const context = await setupAddWriterScenario(t, options);
|
|
22
|
+
const candidatePeer = selectIndexerCandidatePeer(context);
|
|
23
|
+
await promotePeerToWriter(t, context, { readerPeer: candidatePeer });
|
|
24
|
+
|
|
25
|
+
const adminPeer = context.adminBootstrap;
|
|
26
|
+
const writerEntryBefore = await adminPeer.base.view.get(candidatePeer.wallet.address);
|
|
27
|
+
const adminEntryBefore = await adminPeer.base.view.get(adminPeer.wallet.address);
|
|
28
|
+
|
|
29
|
+
context.addIndexerScenario = {
|
|
30
|
+
...(context.addIndexerScenario ?? {}),
|
|
31
|
+
writerPeer: candidatePeer,
|
|
32
|
+
writerEntryBefore: writerEntryBefore
|
|
33
|
+
? { value: b4a.from(writerEntryBefore.value) }
|
|
34
|
+
: null,
|
|
35
|
+
adminEntryBefore: adminEntryBefore ? { value: b4a.from(adminEntryBefore.value) } : null
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return context;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function buildAddIndexerPayload(
|
|
42
|
+
context,
|
|
43
|
+
{ writerPeer = selectIndexerCandidatePeer(context), adminPeer = context.adminBootstrap } = {}
|
|
44
|
+
) {
|
|
45
|
+
if (!writerPeer) {
|
|
46
|
+
throw new Error('buildAddIndexerPayload requires a writer peer.');
|
|
47
|
+
}
|
|
48
|
+
if (!adminPeer) {
|
|
49
|
+
throw new Error('buildAddIndexerPayload requires an admin peer.');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const txValidity = await deriveIndexerSequenceState(adminPeer.base);
|
|
53
|
+
return CompleteStateMessageOperations.assembleAddIndexerMessage(
|
|
54
|
+
adminPeer.wallet,
|
|
55
|
+
writerPeer.wallet.address,
|
|
56
|
+
txValidity
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function applyWithIndexerRoleUpdateFailure(context, invalidPayload) {
|
|
61
|
+
const node = context.adminBootstrap ?? context.bootstrap ?? context.peers?.[0] ?? null;
|
|
62
|
+
if (!node?.base) {
|
|
63
|
+
throw new Error('Indexer role mutation failure scenario requires a writable node.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const originalSetRole = nodeEntryUtils.setRole;
|
|
67
|
+
let shouldFailNextCall = true;
|
|
68
|
+
|
|
69
|
+
nodeEntryUtils.setRole = function patchedSetRole(...args) {
|
|
70
|
+
if (shouldFailNextCall) {
|
|
71
|
+
shouldFailNextCall = false;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return originalSetRole(...args);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await node.base.append(invalidPayload);
|
|
79
|
+
await node.base.update();
|
|
80
|
+
await eventFlush();
|
|
81
|
+
} finally {
|
|
82
|
+
nodeEntryUtils.setRole = originalSetRole;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function buildAddIndexerPayloadWithTxValidity(
|
|
87
|
+
context,
|
|
88
|
+
mutatedTxValidity,
|
|
89
|
+
{ writerPeer = selectIndexerCandidatePeer(context), adminPeer = context.adminBootstrap } = {}
|
|
90
|
+
) {
|
|
91
|
+
if (!b4a.isBuffer(mutatedTxValidity)) {
|
|
92
|
+
throw new Error('buildAddIndexerPayloadWithTxValidity requires a tx validity buffer.');
|
|
93
|
+
}
|
|
94
|
+
if (!writerPeer) {
|
|
95
|
+
throw new Error('buildAddIndexerPayloadWithTxValidity requires a writer peer.');
|
|
96
|
+
}
|
|
97
|
+
if (!adminPeer) {
|
|
98
|
+
throw new Error('buildAddIndexerPayloadWithTxValidity requires an admin peer.');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return CompleteStateMessageOperations.assembleAddIndexerMessage(
|
|
102
|
+
adminPeer.wallet,
|
|
103
|
+
writerPeer.wallet.address,
|
|
104
|
+
mutatedTxValidity
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function ensureIndexerRegistration(base, writingKey) {
|
|
109
|
+
return registerWriterKeyInSystemIndexers(base, writingKey);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function applyWithIndexerWriterKeyAlreadyRegistered(context, invalidPayload) {
|
|
113
|
+
const adminPeer = context.adminBootstrap;
|
|
114
|
+
const writerPeer = context.addIndexerScenario?.writerPeer ?? selectIndexerCandidatePeer(context);
|
|
115
|
+
if (!adminPeer?.base || !writerPeer?.base?.local?.key) {
|
|
116
|
+
throw new Error('Indexer writer key registration scenario requires admin and writer peers.');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let cleanup = context.addIndexerScenario?.writerKeyMembershipCleanup ?? null;
|
|
120
|
+
if (cleanup) {
|
|
121
|
+
context.addIndexerScenario.writerKeyMembershipCleanup = null;
|
|
122
|
+
} else {
|
|
123
|
+
cleanup = ensureIndexerRegistration(adminPeer.base, writerPeer.base.local.key);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await adminPeer.base.append(invalidPayload);
|
|
128
|
+
await adminPeer.base.update();
|
|
129
|
+
await eventFlush();
|
|
130
|
+
} finally {
|
|
131
|
+
if (typeof cleanup === 'function') {
|
|
132
|
+
cleanup();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function buildRemoveIndexerPayload(
|
|
138
|
+
context,
|
|
139
|
+
{ indexerPeer = selectIndexerCandidatePeer(context), adminPeer = context.adminBootstrap } = {}
|
|
140
|
+
) {
|
|
141
|
+
if (!indexerPeer) {
|
|
142
|
+
throw new Error('buildRemoveIndexerPayload requires an indexer peer.');
|
|
143
|
+
}
|
|
144
|
+
if (!adminPeer) {
|
|
145
|
+
throw new Error('buildRemoveIndexerPayload requires an admin peer.');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const txValidity = await deriveIndexerSequenceState(adminPeer.base);
|
|
149
|
+
return CompleteStateMessageOperations.assembleRemoveIndexerMessage(
|
|
150
|
+
adminPeer.wallet,
|
|
151
|
+
indexerPeer.wallet.address,
|
|
152
|
+
txValidity
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function buildRemoveIndexerPayloadWithTxValidity(
|
|
157
|
+
context,
|
|
158
|
+
mutatedTxValidity,
|
|
159
|
+
{ indexerPeer = selectIndexerCandidatePeer(context), adminPeer = context.adminBootstrap } = {}
|
|
160
|
+
) {
|
|
161
|
+
if (!b4a.isBuffer(mutatedTxValidity)) {
|
|
162
|
+
throw new Error('buildRemoveIndexerPayloadWithTxValidity requires a tx validity buffer.');
|
|
163
|
+
}
|
|
164
|
+
if (!indexerPeer) {
|
|
165
|
+
throw new Error('buildRemoveIndexerPayloadWithTxValidity requires an indexer peer.');
|
|
166
|
+
}
|
|
167
|
+
if (!adminPeer) {
|
|
168
|
+
throw new Error('buildRemoveIndexerPayloadWithTxValidity requires an admin peer.');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return CompleteStateMessageOperations.assembleRemoveIndexerMessage(
|
|
172
|
+
adminPeer.wallet,
|
|
173
|
+
indexerPeer.wallet.address,
|
|
174
|
+
mutatedTxValidity
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function assertAddIndexerSuccessState(
|
|
179
|
+
t,
|
|
180
|
+
context,
|
|
181
|
+
{
|
|
182
|
+
writerPeer = selectIndexerCandidatePeer(context),
|
|
183
|
+
adminPeer = context.adminBootstrap,
|
|
184
|
+
writerEntryBefore,
|
|
185
|
+
adminEntryBefore,
|
|
186
|
+
payload,
|
|
187
|
+
skipSync = false
|
|
188
|
+
} = {}
|
|
189
|
+
) {
|
|
190
|
+
if (!writerEntryBefore?.value) {
|
|
191
|
+
throw new Error('assertAddIndexerSuccessState requires writerEntryBefore.');
|
|
192
|
+
}
|
|
193
|
+
if (!adminEntryBefore?.value) {
|
|
194
|
+
throw new Error('assertAddIndexerSuccessState requires adminEntryBefore.');
|
|
195
|
+
}
|
|
196
|
+
if (!payload) {
|
|
197
|
+
throw new Error('assertAddIndexerSuccessState requires the processed payload.');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const writerAddress = writerPeer.wallet.address;
|
|
201
|
+
const decodedWriterBefore = nodeEntryUtils.decode(b4a.from(writerEntryBefore.value));
|
|
202
|
+
t.ok(decodedWriterBefore, 'writer entry before addIndexer decodes');
|
|
203
|
+
if (!decodedWriterBefore) return;
|
|
204
|
+
|
|
205
|
+
await assertIndexerNodeEntry(t, adminPeer.base, writerAddress, decodedWriterBefore, {
|
|
206
|
+
verifyMembership: false
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await assertAdminPaidFee(t, adminPeer.base, adminPeer.wallet.address, adminEntryBefore.value);
|
|
210
|
+
|
|
211
|
+
await assertAddIndexerPayloadMetadata(
|
|
212
|
+
t,
|
|
213
|
+
adminPeer.base,
|
|
214
|
+
payload,
|
|
215
|
+
adminPeer.wallet.address,
|
|
216
|
+
writerAddress
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (!skipSync) {
|
|
220
|
+
await context.sync();
|
|
221
|
+
await assertIndexerNodeEntry(t, writerPeer.base, writerAddress, decodedWriterBefore, {
|
|
222
|
+
verifyMembership: true
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export async function assertAddIndexerFailureState(
|
|
228
|
+
t,
|
|
229
|
+
context,
|
|
230
|
+
{ writerPeer = selectIndexerCandidatePeer(context), adminPeer = context.adminBootstrap, skipSync = false } = {}
|
|
231
|
+
) {
|
|
232
|
+
await assertWriterRemainsNonIndexer(t, adminPeer.base, writerPeer);
|
|
233
|
+
|
|
234
|
+
if (!skipSync) {
|
|
235
|
+
await context.sync();
|
|
236
|
+
await assertWriterRemainsNonIndexer(t, writerPeer.base, writerPeer);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function assertIndexerNodeEntry(t, base, address, referenceNodeEntry, { verifyMembership = true } = {}) {
|
|
241
|
+
const entry = await base.view.get(address);
|
|
242
|
+
t.ok(entry, 'indexer node entry exists');
|
|
243
|
+
const decoded = nodeEntryUtils.decode(entry?.value);
|
|
244
|
+
t.ok(decoded, 'indexer node entry decodes');
|
|
245
|
+
if (!decoded) return;
|
|
246
|
+
|
|
247
|
+
t.is(decoded.isWhitelisted, true, 'indexer remains whitelisted');
|
|
248
|
+
t.is(decoded.isWriter, true, 'indexer retains writer role');
|
|
249
|
+
t.is(decoded.isIndexer, true, 'indexer role assigned');
|
|
250
|
+
t.ok(b4a.equals(decoded.wk, referenceNodeEntry.wk), 'indexer writing key preserved');
|
|
251
|
+
t.ok(b4a.equals(decoded.balance, referenceNodeEntry.balance), 'indexer balance preserved');
|
|
252
|
+
t.ok(
|
|
253
|
+
b4a.equals(decoded.stakedBalance, referenceNodeEntry.stakedBalance),
|
|
254
|
+
'indexer staked balance preserved'
|
|
255
|
+
);
|
|
256
|
+
t.ok(b4a.equals(decoded.license, referenceNodeEntry.license), 'indexer license preserved');
|
|
257
|
+
if (verifyMembership) {
|
|
258
|
+
assertIndexerMembership(t, base, referenceNodeEntry.wk);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function assertAdminPaidFee(t, base, adminAddress, adminEntryBeforeValue) {
|
|
263
|
+
const decodedBefore = nodeEntryUtils.decode(b4a.from(adminEntryBeforeValue));
|
|
264
|
+
t.ok(decodedBefore, 'admin entry before addIndexer decodes');
|
|
265
|
+
if (!decodedBefore) return;
|
|
266
|
+
|
|
267
|
+
const adminEntryAfter = await base.view.get(adminAddress);
|
|
268
|
+
t.ok(adminEntryAfter, 'admin entry exists after addIndexer');
|
|
269
|
+
const decodedAfter = nodeEntryUtils.decode(adminEntryAfter?.value);
|
|
270
|
+
t.ok(decodedAfter, 'admin entry decodes after addIndexer');
|
|
271
|
+
if (!decodedAfter) return;
|
|
272
|
+
|
|
273
|
+
const balanceBefore = toBalance(decodedBefore.balance);
|
|
274
|
+
t.ok(balanceBefore, 'admin balance before addIndexer decodes');
|
|
275
|
+
if (!balanceBefore) return;
|
|
276
|
+
|
|
277
|
+
const feeAmount = toBalance(transactionUtils.FEE);
|
|
278
|
+
t.ok(feeAmount, 'addIndexer fee decodes');
|
|
279
|
+
if (!feeAmount) return;
|
|
280
|
+
|
|
281
|
+
const expectedBalance = balanceBefore.sub(feeAmount);
|
|
282
|
+
t.ok(expectedBalance, 'admin balance after fee computation succeeds');
|
|
283
|
+
if (!expectedBalance) return;
|
|
284
|
+
|
|
285
|
+
t.ok(
|
|
286
|
+
b4a.equals(decodedAfter.balance, expectedBalance.value),
|
|
287
|
+
'admin balance reduced by addIndexer fee'
|
|
288
|
+
);
|
|
289
|
+
t.ok(
|
|
290
|
+
b4a.equals(decodedAfter.stakedBalance, decodedBefore.stakedBalance),
|
|
291
|
+
'admin staked balance remains unchanged after addIndexer'
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function assertAddIndexerPayloadMetadata(t, base, payload, expectedAdminAddress, expectedCandidate) {
|
|
296
|
+
const decodedOperation = safeDecodeApplyOperation(payload);
|
|
297
|
+
t.ok(decodedOperation, 'addIndexer payload decodes');
|
|
298
|
+
if (!decodedOperation) return;
|
|
299
|
+
|
|
300
|
+
const requesterAddressBuffer = decodedOperation.address;
|
|
301
|
+
t.ok(requesterAddressBuffer, 'addIndexer payload contains requester address');
|
|
302
|
+
const requesterAddress = addressUtils.bufferToAddress(requesterAddressBuffer);
|
|
303
|
+
t.ok(requesterAddress, 'addIndexer requester address decodes');
|
|
304
|
+
if (requesterAddress) {
|
|
305
|
+
t.is(requesterAddress, expectedAdminAddress, 'addIndexer payload signed by admin');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const candidateAddressBuffer = decodedOperation?.aco?.ia;
|
|
309
|
+
t.ok(candidateAddressBuffer, 'addIndexer payload contains candidate address');
|
|
310
|
+
const candidateAddress = addressUtils.bufferToAddress(candidateAddressBuffer);
|
|
311
|
+
t.ok(candidateAddress, 'addIndexer candidate address decodes');
|
|
312
|
+
if (candidateAddress) {
|
|
313
|
+
t.is(candidateAddress, expectedCandidate, 'addIndexer payload nominates expected writer');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const txHashBuffer = decodedOperation?.aco?.tx;
|
|
317
|
+
t.ok(txHashBuffer, 'addIndexer tx hash extracted');
|
|
318
|
+
if (txHashBuffer) {
|
|
319
|
+
const txEntry = await base.view.get(txHashBuffer.toString('hex'));
|
|
320
|
+
t.ok(txEntry, 'addIndexer transaction recorded for replay protection');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function assertWriterRemainsNonIndexer(t, base, writerPeer) {
|
|
325
|
+
const entry = await base.view.get(writerPeer.wallet.address);
|
|
326
|
+
t.ok(entry, 'writer entry exists after failed addIndexer');
|
|
327
|
+
const decoded = nodeEntryUtils.decode(entry?.value);
|
|
328
|
+
t.ok(decoded, 'writer entry decodes after failed addIndexer');
|
|
329
|
+
if (!decoded) return;
|
|
330
|
+
t.is(decoded.isIndexer, false, 'writer not promoted to indexer');
|
|
331
|
+
assertIndexerMembershipAbsent(t, base, decoded.wk);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function assertIndexerMembership(t, base, writingKey) {
|
|
335
|
+
const hasMembership = indexerMembershipIncludes(base, writingKey);
|
|
336
|
+
t.ok(hasMembership, 'indexer writer key added to validator set');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function assertIndexerMembershipAbsent(t, base, writingKey) {
|
|
340
|
+
const hasMembership = indexerMembershipIncludes(base, writingKey);
|
|
341
|
+
t.is(hasMembership, false, 'writer key absent from validator set');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function indexerMembershipIncludes(base, writingKey) {
|
|
345
|
+
const entries = base?.system?.indexers;
|
|
346
|
+
if (!entries) return false;
|
|
347
|
+
return Object.values(entries).some(entry => entry?.key && b4a.equals(entry.key, writingKey));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function registerWriterKeyInSystemIndexers(base, writingKey) {
|
|
351
|
+
const system = base?.system;
|
|
352
|
+
if (!system) return () => {};
|
|
353
|
+
|
|
354
|
+
if (!Array.isArray(system.indexers)) {
|
|
355
|
+
system.indexers = system.indexers ? Array.from(system.indexers) : [];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const membership = system.indexers;
|
|
359
|
+
const existing = membership.find(entry => entry?.key && b4a.equals(entry.key, writingKey));
|
|
360
|
+
if (existing) {
|
|
361
|
+
return () => {};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const entry = { key: writingKey, length: 0 };
|
|
365
|
+
membership.push(entry);
|
|
366
|
+
|
|
367
|
+
if (system._indexerMap instanceof Map) {
|
|
368
|
+
system._indexerMap.set(writingKey.toString('hex'), entry);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return () => {
|
|
372
|
+
const index = membership.indexOf(entry);
|
|
373
|
+
if (index !== -1) {
|
|
374
|
+
membership.splice(index, 1);
|
|
375
|
+
}
|
|
376
|
+
if (system._indexerMap instanceof Map) {
|
|
377
|
+
system._indexerMap.delete(writingKey.toString('hex'));
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export async function applyWithPretenderRoleMutation(context, invalidPayload, role) {
|
|
383
|
+
const adminPeer = context.adminBootstrap;
|
|
384
|
+
const writerPeer = context.addIndexerScenario?.writerPeer ?? selectIndexerCandidatePeer(context);
|
|
385
|
+
if (!adminPeer?.base || !writerPeer?.wallet?.address) {
|
|
386
|
+
throw new Error('Pretender role mutation scenario requires admin and writer peers.');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const address = writerPeer.wallet.address;
|
|
390
|
+
const originalApply = adminPeer.base._handlers.apply;
|
|
391
|
+
const roleValue = typeof role === 'number' ? role : nodeRoleUtils.NodeRole.INDEXER;
|
|
392
|
+
|
|
393
|
+
adminPeer.base._handlers.apply = async function patchedApply(nodes, view, baseCtx) {
|
|
394
|
+
let mutatedOnce = false;
|
|
395
|
+
const originalBatch = view.batch;
|
|
396
|
+
view.batch = function patchedBatch(...args) {
|
|
397
|
+
const batch = originalBatch.apply(this, args);
|
|
398
|
+
if (!batch?.get) return batch;
|
|
399
|
+
|
|
400
|
+
const originalGet = batch.get.bind(batch);
|
|
401
|
+
batch.get = async key => {
|
|
402
|
+
if (mutatedOnce) return originalGet(key);
|
|
403
|
+
if (typeof key === 'string' ? key !== address : true) {
|
|
404
|
+
// string path comparison; buffer path is unlikely for address entries here
|
|
405
|
+
if (b4a.isBuffer(key)) {
|
|
406
|
+
const addrBuf = addressUtils.addressToBuffer(address);
|
|
407
|
+
if (!addrBuf || !b4a.equals(addrBuf, key)) {
|
|
408
|
+
return originalGet(key);
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
return originalGet(key);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const entry = await originalGet(key);
|
|
416
|
+
if (!entry?.value) {
|
|
417
|
+
throw new Error('Pretender role mutation scenario requires an existing writer entry.');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const mutated = nodeEntryUtils.setRole(b4a.from(entry.value), roleValue);
|
|
421
|
+
if (!mutated) {
|
|
422
|
+
throw new Error('Failed to mutate pretender node role.');
|
|
423
|
+
}
|
|
424
|
+
mutatedOnce = true;
|
|
425
|
+
return { ...entry, value: mutated };
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
return batch;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
return await originalApply.call(this, nodes, view, baseCtx);
|
|
433
|
+
} finally {
|
|
434
|
+
view.batch = originalBatch;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
await adminPeer.base.append(invalidPayload);
|
|
440
|
+
await adminPeer.base.update();
|
|
441
|
+
await eventFlush();
|
|
442
|
+
} finally {
|
|
443
|
+
adminPeer.base._handlers.apply = originalApply;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import OperationValidationScenarioBase from '../common/base/OperationValidationScenarioBase.js';
|
|
2
|
+
import {
|
|
3
|
+
setupAddIndexerScenario,
|
|
4
|
+
buildAddIndexerPayload,
|
|
5
|
+
assertAddIndexerFailureState,
|
|
6
|
+
applyWithIndexerWriterKeyAlreadyRegistered,
|
|
7
|
+
ensureIndexerRegistration,
|
|
8
|
+
selectIndexerCandidatePeer
|
|
9
|
+
} from './addIndexerScenarioHelpers.js';
|
|
10
|
+
|
|
11
|
+
export default function addIndexerWriterKeyAlreadyRegisteredScenario() {
|
|
12
|
+
new OperationValidationScenarioBase({
|
|
13
|
+
title: 'State.apply addIndexer rejects payloads when writer key already exists in indexer list',
|
|
14
|
+
setupScenario: async t => {
|
|
15
|
+
const context = await setupAddIndexerScenario(t);
|
|
16
|
+
const writerPeer =
|
|
17
|
+
context.addIndexerScenario?.writerPeer ?? selectIndexerCandidatePeer(context);
|
|
18
|
+
const cleanup = ensureIndexerRegistration(context.adminBootstrap.base, writerPeer.base.local.key);
|
|
19
|
+
context.addIndexerScenario = {
|
|
20
|
+
...(context.addIndexerScenario ?? {}),
|
|
21
|
+
writerKeyMembershipCleanup: cleanup,
|
|
22
|
+
writerPeer
|
|
23
|
+
};
|
|
24
|
+
return context;
|
|
25
|
+
},
|
|
26
|
+
buildValidPayload: context => buildAddIndexerPayload(context),
|
|
27
|
+
mutatePayload: (_t, payload) => payload,
|
|
28
|
+
applyInvalidPayload: applyWithIndexerWriterKeyAlreadyRegistered,
|
|
29
|
+
assertStateUnchanged: (t, context) => assertAddIndexerFailureState(t, context, { skipSync: true }),
|
|
30
|
+
expectedLogs: ['Writer key already exists in indexer list.']
|
|
31
|
+
}).performScenario();
|
|
32
|
+
}
|