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,87 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import OperationValidationScenarioBase from '../base/OperationValidationScenarioBase.js';
|
|
3
|
+
import {
|
|
4
|
+
safeDecodeApplyOperation,
|
|
5
|
+
safeEncodeApplyOperation
|
|
6
|
+
} from '../../../../../../src/utils/protobuf/operationHelpers.js';
|
|
7
|
+
import { eventFlush } from '../../../../../helpers/autobaseTestHelpers.js';
|
|
8
|
+
|
|
9
|
+
export const PartialOperationMutationStrategy = {
|
|
10
|
+
MISSING_COMPONENT: 'missing-component',
|
|
11
|
+
NONCE_MATCH: 'nonce-match',
|
|
12
|
+
ADDRESS_MATCH: 'address-match',
|
|
13
|
+
SIGNATURE_MATCH: 'signature-match'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default class PartialOperationValidationScenario extends OperationValidationScenarioBase {
|
|
17
|
+
constructor({
|
|
18
|
+
title,
|
|
19
|
+
setupScenario,
|
|
20
|
+
buildValidPayload,
|
|
21
|
+
assertStateUnchanged,
|
|
22
|
+
strategy,
|
|
23
|
+
parentKey = 'rao',
|
|
24
|
+
applyInvalidPayload = defaultApplyInvalidPayload,
|
|
25
|
+
expectedLogs
|
|
26
|
+
}) {
|
|
27
|
+
const mutatePayload = createMutationStrategy(strategy, parentKey);
|
|
28
|
+
super({
|
|
29
|
+
title,
|
|
30
|
+
setupScenario,
|
|
31
|
+
buildValidPayload,
|
|
32
|
+
mutatePayload,
|
|
33
|
+
applyInvalidPayload,
|
|
34
|
+
assertStateUnchanged,
|
|
35
|
+
expectedLogs
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createMutationStrategy(strategy, parentKey) {
|
|
41
|
+
switch (strategy) {
|
|
42
|
+
case PartialOperationMutationStrategy.MISSING_COMPONENT:
|
|
43
|
+
return (t, payload) => removeComponent(t, payload, parentKey, 'vs');
|
|
44
|
+
case PartialOperationMutationStrategy.NONCE_MATCH:
|
|
45
|
+
return (t, payload) => duplicateField(t, payload, parentKey, 'vn', 'in');
|
|
46
|
+
case PartialOperationMutationStrategy.ADDRESS_MATCH:
|
|
47
|
+
return (t, payload) => alignAddress(t, payload, parentKey);
|
|
48
|
+
case PartialOperationMutationStrategy.SIGNATURE_MATCH:
|
|
49
|
+
return (t, payload) => duplicateField(t, payload, parentKey, 'vs', 'is');
|
|
50
|
+
default:
|
|
51
|
+
throw new Error(`Unsupported partial operation strategy: ${strategy}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function removeComponent(t, validPayload, parentKey, component) {
|
|
56
|
+
const decoded = safeDecodeApplyOperation(validPayload);
|
|
57
|
+
t.ok(decoded, 'fixtures decode');
|
|
58
|
+
const parent = decoded[parentKey];
|
|
59
|
+
if (!parent) return validPayload;
|
|
60
|
+
delete parent[component];
|
|
61
|
+
return safeEncodeApplyOperation(decoded);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function duplicateField(t, validPayload, parentKey, targetKey, sourceKey) {
|
|
65
|
+
const decoded = safeDecodeApplyOperation(validPayload);
|
|
66
|
+
t.ok(decoded, 'fixtures decode');
|
|
67
|
+
const parent = decoded[parentKey];
|
|
68
|
+
if (!parent || !parent[sourceKey]) return validPayload;
|
|
69
|
+
parent[targetKey] = b4a.from(parent[sourceKey]);
|
|
70
|
+
return safeEncodeApplyOperation(decoded);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function alignAddress(t, validPayload, parentKey) {
|
|
74
|
+
const decoded = safeDecodeApplyOperation(validPayload);
|
|
75
|
+
t.ok(decoded, 'fixtures decode');
|
|
76
|
+
const parent = decoded[parentKey];
|
|
77
|
+
if (!parent || !decoded.address) return validPayload;
|
|
78
|
+
parent.va = b4a.from(decoded.address);
|
|
79
|
+
return safeEncodeApplyOperation(decoded);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function defaultApplyInvalidPayload(context, invalidPayload) {
|
|
83
|
+
const { bootstrap } = context;
|
|
84
|
+
await bootstrap.base.append(invalidPayload);
|
|
85
|
+
await bootstrap.base.update();
|
|
86
|
+
await eventFlush();
|
|
87
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import OperationValidationScenarioBase from '../base/OperationValidationScenarioBase.js';
|
|
3
|
+
import addressUtils from '../../../../../../src/core/state/utils/address.js';
|
|
4
|
+
import { eventFlush } from '../../../../../helpers/autobaseTestHelpers.js';
|
|
5
|
+
|
|
6
|
+
export default class RequesterNodeEntryBufferMissingScenario extends OperationValidationScenarioBase {
|
|
7
|
+
constructor({
|
|
8
|
+
title = 'State.apply operation rejects payloads when requester node entry buffer is missing',
|
|
9
|
+
setupScenario,
|
|
10
|
+
buildValidPayload,
|
|
11
|
+
assertStateUnchanged,
|
|
12
|
+
expectedLogs = ['Invalid requester node entry buffer.'],
|
|
13
|
+
selectNode = context => context.bootstrap ?? context.adminBootstrap ?? context.peers?.[0],
|
|
14
|
+
selectPeer = context => context.bootstrapDeployment?.deployerPeer ?? context.peers?.[1]
|
|
15
|
+
}) {
|
|
16
|
+
super({
|
|
17
|
+
title,
|
|
18
|
+
setupScenario,
|
|
19
|
+
buildValidPayload,
|
|
20
|
+
mutatePayload: (_t, payload) => payload,
|
|
21
|
+
applyInvalidPayload: async (context, payload) => {
|
|
22
|
+
const node = selectNode(context);
|
|
23
|
+
const peer = selectPeer(context);
|
|
24
|
+
if (!node?.base || !peer?.wallet?.address) {
|
|
25
|
+
throw new Error('Requester node entry buffer scenario requires node and peer.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const targetAddressString = peer.wallet.address;
|
|
29
|
+
const targetAddressBuffer = addressUtils.addressToBuffer(targetAddressString);
|
|
30
|
+
|
|
31
|
+
const originalApply = node.base._handlers.apply;
|
|
32
|
+
node.base._handlers.apply = async function patchedApply(nodes, view, baseCtx) {
|
|
33
|
+
const originalBatch = view.batch;
|
|
34
|
+
view.batch = function patchedBatch(...args) {
|
|
35
|
+
const batch = originalBatch.apply(this, args);
|
|
36
|
+
if (!batch?.get) return batch;
|
|
37
|
+
const originalGet = batch.get.bind(batch);
|
|
38
|
+
batch.get = async key => {
|
|
39
|
+
if (isTargetKey(key, targetAddressString, targetAddressBuffer)) return null;
|
|
40
|
+
return originalGet(key);
|
|
41
|
+
};
|
|
42
|
+
return batch;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
return await originalApply.call(this, nodes, view, baseCtx);
|
|
47
|
+
} finally {
|
|
48
|
+
view.batch = originalBatch;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await node.base.append(payload);
|
|
54
|
+
await node.base.update();
|
|
55
|
+
await eventFlush();
|
|
56
|
+
} finally {
|
|
57
|
+
node.base._handlers.apply = originalApply;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
assertStateUnchanged,
|
|
61
|
+
expectedLogs
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isTargetKey(key, targetAddressString, targetAddressBuffer) {
|
|
67
|
+
if (typeof key === 'string') return key === targetAddressString;
|
|
68
|
+
if (b4a.isBuffer(key) && targetAddressBuffer) return b4a.equals(key, targetAddressBuffer);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import OperationValidationScenarioBase from '../base/OperationValidationScenarioBase.js';
|
|
3
|
+
import addressUtils from '../../../../../../src/core/state/utils/address.js';
|
|
4
|
+
import { eventFlush } from '../../../../../helpers/autobaseTestHelpers.js';
|
|
5
|
+
|
|
6
|
+
export default class RequesterNodeEntryDecodeFailureScenario extends OperationValidationScenarioBase {
|
|
7
|
+
constructor({
|
|
8
|
+
title = 'State.apply operation rejects payloads when requester node entry cannot be decoded',
|
|
9
|
+
setupScenario,
|
|
10
|
+
buildValidPayload,
|
|
11
|
+
assertStateUnchanged,
|
|
12
|
+
expectedLogs = ['Invalid requester node entry.'],
|
|
13
|
+
selectNode = context => context.bootstrap ?? context.adminBootstrap ?? context.peers?.[0],
|
|
14
|
+
selectPeer = context => context.bootstrapDeployment?.deployerPeer ?? context.peers?.[1]
|
|
15
|
+
}) {
|
|
16
|
+
super({
|
|
17
|
+
title,
|
|
18
|
+
setupScenario,
|
|
19
|
+
buildValidPayload,
|
|
20
|
+
mutatePayload: (_t, payload) => payload,
|
|
21
|
+
applyInvalidPayload: async (context, payload) => {
|
|
22
|
+
const node = selectNode(context);
|
|
23
|
+
const peer = selectPeer(context);
|
|
24
|
+
if (!node?.base || !peer?.wallet?.address) {
|
|
25
|
+
throw new Error('Requester node entry decode scenario requires node and peer.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const targetAddressString = peer.wallet.address;
|
|
29
|
+
const targetAddressBuffer = addressUtils.addressToBuffer(targetAddressString);
|
|
30
|
+
|
|
31
|
+
const originalApply = node.base._handlers.apply;
|
|
32
|
+
node.base._handlers.apply = async function patchedApply(nodes, view, baseCtx) {
|
|
33
|
+
const originalBatch = view.batch;
|
|
34
|
+
view.batch = function patchedBatch(...args) {
|
|
35
|
+
const batch = originalBatch.apply(this, args);
|
|
36
|
+
if (!batch?.get) return batch;
|
|
37
|
+
const originalGet = batch.get.bind(batch);
|
|
38
|
+
batch.get = async key => {
|
|
39
|
+
if (isTargetKey(key, targetAddressString, targetAddressBuffer)) {
|
|
40
|
+
return { key, value: b4a.alloc(0) };
|
|
41
|
+
}
|
|
42
|
+
return originalGet(key);
|
|
43
|
+
};
|
|
44
|
+
return batch;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
return await originalApply.call(this, nodes, view, baseCtx);
|
|
49
|
+
} finally {
|
|
50
|
+
view.batch = originalBatch;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
await node.base.append(payload);
|
|
56
|
+
await node.base.update();
|
|
57
|
+
await eventFlush();
|
|
58
|
+
} finally {
|
|
59
|
+
node.base._handlers.apply = originalApply;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
assertStateUnchanged,
|
|
63
|
+
expectedLogs
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isTargetKey(key, targetAddressString, targetAddressBuffer) {
|
|
69
|
+
if (typeof key === 'string') return key === targetAddressString;
|
|
70
|
+
if (b4a.isBuffer(key) && targetAddressBuffer) return b4a.equals(key, targetAddressBuffer);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import OperationValidationScenarioBase from '../base/OperationValidationScenarioBase.js';
|
|
2
|
+
import { applyWithRequesterEntryRemoval } from '../../addWriter/addWriterScenarioHelpers.js';
|
|
3
|
+
|
|
4
|
+
const passThroughPayload = (_t, payload) => payload;
|
|
5
|
+
|
|
6
|
+
export default class RequesterNodeEntryMissingScenario extends OperationValidationScenarioBase {
|
|
7
|
+
constructor({
|
|
8
|
+
title,
|
|
9
|
+
setupScenario,
|
|
10
|
+
buildValidPayload,
|
|
11
|
+
assertStateUnchanged,
|
|
12
|
+
expectedLogs,
|
|
13
|
+
selectPeer,
|
|
14
|
+
mutatePayload = passThroughPayload
|
|
15
|
+
}) {
|
|
16
|
+
if (typeof selectPeer !== 'function') {
|
|
17
|
+
throw new Error('Requester node entry scenario requires a selectPeer function.');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
super({
|
|
21
|
+
title,
|
|
22
|
+
setupScenario,
|
|
23
|
+
buildValidPayload,
|
|
24
|
+
mutatePayload,
|
|
25
|
+
applyInvalidPayload: async (context, invalidPayload) => {
|
|
26
|
+
const peer = selectPeer(context);
|
|
27
|
+
if (!peer) {
|
|
28
|
+
throw new Error('Requester node entry scenario requires a peer instance.');
|
|
29
|
+
}
|
|
30
|
+
await applyWithRequesterEntryRemoval(context, invalidPayload, { peer });
|
|
31
|
+
},
|
|
32
|
+
assertStateUnchanged,
|
|
33
|
+
expectedLogs
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
|
|
3
|
+
import { safeDecodeApplyOperation, safeEncodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
|
|
4
|
+
import OperationValidationScenarioBase from './base/OperationValidationScenarioBase.js';
|
|
5
|
+
|
|
6
|
+
export default class RequesterAddressValidationScenario extends OperationValidationScenarioBase {
|
|
7
|
+
constructor({
|
|
8
|
+
title,
|
|
9
|
+
setupScenario,
|
|
10
|
+
buildValidPayload,
|
|
11
|
+
assertStateUnchanged,
|
|
12
|
+
mutatePayload = defaultMutateRequesterPayload,
|
|
13
|
+
applyInvalidPayload = defaultApplyInvalidRequesterPayload,
|
|
14
|
+
expectedLogs
|
|
15
|
+
}) {
|
|
16
|
+
super({
|
|
17
|
+
title,
|
|
18
|
+
setupScenario,
|
|
19
|
+
buildValidPayload,
|
|
20
|
+
mutatePayload,
|
|
21
|
+
applyInvalidPayload,
|
|
22
|
+
assertStateUnchanged,
|
|
23
|
+
expectedLogs
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function defaultMutateRequesterPayload(t, validPayload) {
|
|
29
|
+
const decodedPayload = safeDecodeApplyOperation(validPayload);
|
|
30
|
+
t.ok(decodedPayload, 'fixtures decode');
|
|
31
|
+
|
|
32
|
+
const invalidAddressBuffer = b4a.from(decodedPayload.address);
|
|
33
|
+
invalidAddressBuffer[0] = invalidAddressBuffer[0] === 120 ? 121 : 120;
|
|
34
|
+
decodedPayload.address = invalidAddressBuffer;
|
|
35
|
+
|
|
36
|
+
return safeEncodeApplyOperation(decodedPayload);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function defaultApplyInvalidRequesterPayload(context, invalidPayload) {
|
|
40
|
+
const { bootstrap } = context;
|
|
41
|
+
await bootstrap.base.append(invalidPayload);
|
|
42
|
+
await bootstrap.base.update();
|
|
43
|
+
await eventFlush();
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import RequesterAddressValidationScenario from './requesterAddressValidationScenario.js';
|
|
3
|
+
import { safeDecodeApplyOperation, safeEncodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
|
|
4
|
+
|
|
5
|
+
function mutateRequesterPublicKey(t, validPayload) {
|
|
6
|
+
const decodedPayload = safeDecodeApplyOperation(validPayload);
|
|
7
|
+
t.ok(decodedPayload, 'fixtures decode');
|
|
8
|
+
|
|
9
|
+
const mutatedAddress = b4a.from(decodedPayload.address);
|
|
10
|
+
const lastIndex = mutatedAddress.length - 1;
|
|
11
|
+
const currentChar = mutatedAddress[lastIndex];
|
|
12
|
+
const asciiP = 'p'.charCodeAt(0);
|
|
13
|
+
const asciiQ = 'q'.charCodeAt(0);
|
|
14
|
+
mutatedAddress[lastIndex] = currentChar === asciiP ? asciiQ : asciiP;
|
|
15
|
+
|
|
16
|
+
decodedPayload.address = mutatedAddress;
|
|
17
|
+
return safeEncodeApplyOperation(decodedPayload);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function createRequesterPublicKeyValidationScenario(config) {
|
|
21
|
+
return new RequesterAddressValidationScenario({
|
|
22
|
+
...config,
|
|
23
|
+
mutatePayload: mutateRequesterPublicKey
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import { eventFlush } from '../../../../helpers/autobaseTestHelpers.js';
|
|
3
|
+
import { safeDecodeApplyOperation } from '../../../../../src/utils/protobuf/operationHelpers.js';
|
|
4
|
+
import OperationValidationScenarioBase from './base/OperationValidationScenarioBase.js';
|
|
5
|
+
|
|
6
|
+
export default class TransactionValidityMismatchScenario extends OperationValidationScenarioBase {
|
|
7
|
+
constructor({
|
|
8
|
+
title,
|
|
9
|
+
setupScenario,
|
|
10
|
+
buildValidPayload,
|
|
11
|
+
assertStateUnchanged,
|
|
12
|
+
txValidityPath = ['cao', 'txv'],
|
|
13
|
+
applyInvalidPayload = defaultApplyInvalidPayload,
|
|
14
|
+
rebuildPayloadWithTxValidity,
|
|
15
|
+
expectedLogs
|
|
16
|
+
}) {
|
|
17
|
+
if (typeof rebuildPayloadWithTxValidity !== 'function') {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'Transaction validity mismatch scenario requires a rebuildPayloadWithTxValidity function.'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
super({
|
|
24
|
+
title,
|
|
25
|
+
setupScenario,
|
|
26
|
+
buildValidPayload,
|
|
27
|
+
mutatePayload: (t, payload, context) =>
|
|
28
|
+
mutateTxValidityPayload({
|
|
29
|
+
t,
|
|
30
|
+
validPayload: payload,
|
|
31
|
+
context,
|
|
32
|
+
txValidityPath,
|
|
33
|
+
rebuildPayloadWithTxValidity
|
|
34
|
+
}),
|
|
35
|
+
applyInvalidPayload,
|
|
36
|
+
assertStateUnchanged,
|
|
37
|
+
expectedLogs
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function mutateTxValidityPayload({
|
|
43
|
+
t,
|
|
44
|
+
validPayload,
|
|
45
|
+
context,
|
|
46
|
+
txValidityPath,
|
|
47
|
+
rebuildPayloadWithTxValidity
|
|
48
|
+
}) {
|
|
49
|
+
const path = Array.isArray(txValidityPath) ? txValidityPath : [txValidityPath];
|
|
50
|
+
if (!path.length) {
|
|
51
|
+
throw new Error('Transaction validity mutation requires a non-empty path.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const decodedPayload = safeDecodeApplyOperation(validPayload);
|
|
55
|
+
t.ok(decodedPayload, 'fixtures decode');
|
|
56
|
+
|
|
57
|
+
const txValidityBuffer = locateTxValidityBuffer(decodedPayload, path);
|
|
58
|
+
if (!txValidityBuffer) {
|
|
59
|
+
return validPayload;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const mutatedValidity = flipTxValidity(txValidityBuffer);
|
|
63
|
+
return rebuildPayloadWithTxValidity({
|
|
64
|
+
context,
|
|
65
|
+
t,
|
|
66
|
+
validPayload,
|
|
67
|
+
mutatedTxValidity: mutatedValidity
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function locateTxValidityBuffer(payload, path) {
|
|
72
|
+
let current = payload;
|
|
73
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
74
|
+
if (!current) return null;
|
|
75
|
+
current = current[path[i]];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const field = path[path.length - 1];
|
|
79
|
+
const value = current?.[field];
|
|
80
|
+
if (!b4a.isBuffer(value) || value.length === 0) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function flipTxValidity(txValidityBuffer) {
|
|
87
|
+
const mutatedValidity = b4a.from(txValidityBuffer);
|
|
88
|
+
const lastIndex = mutatedValidity.length - 1;
|
|
89
|
+
mutatedValidity[lastIndex] ^= 0xff;
|
|
90
|
+
return mutatedValidity;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function defaultApplyInvalidPayload(context, invalidPayload) {
|
|
94
|
+
const { bootstrap } = context;
|
|
95
|
+
await bootstrap.base.append(invalidPayload);
|
|
96
|
+
await bootstrap.base.update();
|
|
97
|
+
await eventFlush();
|
|
98
|
+
}
|
package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import OperationValidationScenarioBase from '../../base/OperationValidationScenarioBase.js';
|
|
3
|
+
import { safeDecodeApplyOperation } from '../../../../../../../src/utils/protobuf/operationHelpers.js';
|
|
4
|
+
import addressUtils from '../../../../../../../src/core/state/utils/address.js';
|
|
5
|
+
import { eventFlush } from '../../../../../../helpers/autobaseTestHelpers.js';
|
|
6
|
+
|
|
7
|
+
export const ValidatorEntryMutation = {
|
|
8
|
+
DELETE: Symbol('validator-entry-delete')
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_VALIDATOR_ADDRESS_PATH = ['rao', 'va'];
|
|
12
|
+
|
|
13
|
+
export default class ValidatorConsistencyScenarioBase extends OperationValidationScenarioBase {
|
|
14
|
+
constructor({
|
|
15
|
+
title,
|
|
16
|
+
setupScenario,
|
|
17
|
+
buildValidPayload,
|
|
18
|
+
assertStateUnchanged,
|
|
19
|
+
mutateEntry,
|
|
20
|
+
selectNode = defaultSelectNode,
|
|
21
|
+
validatorAddressPath = DEFAULT_VALIDATOR_ADDRESS_PATH,
|
|
22
|
+
expectedLogs,
|
|
23
|
+
applyInvalidPayload
|
|
24
|
+
}) {
|
|
25
|
+
if (typeof mutateEntry !== 'function') {
|
|
26
|
+
throw new Error('Validator consistency scenario requires a mutateEntry function.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
super({
|
|
30
|
+
title,
|
|
31
|
+
setupScenario,
|
|
32
|
+
buildValidPayload,
|
|
33
|
+
mutatePayload: passThroughPayload,
|
|
34
|
+
applyInvalidPayload:
|
|
35
|
+
typeof applyInvalidPayload === 'function'
|
|
36
|
+
? applyInvalidPayload
|
|
37
|
+
: createApplyInvalidPayload({
|
|
38
|
+
selectNode,
|
|
39
|
+
validatorAddressPath,
|
|
40
|
+
mutateEntry
|
|
41
|
+
}),
|
|
42
|
+
assertStateUnchanged,
|
|
43
|
+
expectedLogs
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function passThroughPayload(_t, payload) {
|
|
49
|
+
return payload;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function defaultSelectNode(context) {
|
|
53
|
+
return context.adminBootstrap ?? context.bootstrap ?? context.peers?.[0] ?? null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createApplyInvalidPayload({ selectNode, validatorAddressPath, mutateEntry }) {
|
|
57
|
+
return async (context, payload, t, validPayload) => {
|
|
58
|
+
const node = selectNode(context);
|
|
59
|
+
if (!node?.base) {
|
|
60
|
+
throw new Error('Validator consistency scenario requires a writable node.');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const validatorAddress = extractValidatorAddress(validPayload ?? payload, validatorAddressPath);
|
|
64
|
+
if (!validatorAddress) {
|
|
65
|
+
throw new Error('Validator address could not be derived from payload.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const cleanup = patchValidatorEntry({
|
|
69
|
+
base: node.base,
|
|
70
|
+
mutateEntry,
|
|
71
|
+
context,
|
|
72
|
+
t,
|
|
73
|
+
validatorAddressString: validatorAddress.string,
|
|
74
|
+
validatorAddressBuffer: validatorAddress.buffer
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await node.base.append(payload);
|
|
79
|
+
await node.base.update();
|
|
80
|
+
await eventFlush();
|
|
81
|
+
} finally {
|
|
82
|
+
await cleanup();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function patchValidatorEntry({
|
|
88
|
+
base,
|
|
89
|
+
mutateEntry,
|
|
90
|
+
context,
|
|
91
|
+
t,
|
|
92
|
+
validatorAddressString,
|
|
93
|
+
validatorAddressBuffer
|
|
94
|
+
}) {
|
|
95
|
+
const originalApply = base._handlers.apply;
|
|
96
|
+
let shouldInterceptNextApply = true;
|
|
97
|
+
|
|
98
|
+
base._handlers.apply = async function patchedApply(nodes, view, baseCtx) {
|
|
99
|
+
if (!shouldInterceptNextApply) {
|
|
100
|
+
return originalApply.call(this, nodes, view, baseCtx);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
shouldInterceptNextApply = false;
|
|
104
|
+
const originalBatch = view.batch;
|
|
105
|
+
view.batch = function patchedBatch(...args) {
|
|
106
|
+
const batch = originalBatch.apply(this, args);
|
|
107
|
+
if (!batch || typeof batch.get !== 'function') {
|
|
108
|
+
return batch;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const originalGet = batch.get.bind(batch);
|
|
112
|
+
let hasMutatedEntry = false;
|
|
113
|
+
|
|
114
|
+
batch.get = async key => {
|
|
115
|
+
if (
|
|
116
|
+
!hasMutatedEntry &&
|
|
117
|
+
isValidatorKeyMatch(key, validatorAddressString, validatorAddressBuffer)
|
|
118
|
+
) {
|
|
119
|
+
hasMutatedEntry = true;
|
|
120
|
+
const entry = await originalGet(key);
|
|
121
|
+
|
|
122
|
+
const mutation = await mutateEntry(entry, {
|
|
123
|
+
context,
|
|
124
|
+
t,
|
|
125
|
+
validatorAddress: validatorAddressString
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return applyEntryMutation(entry, mutation);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return originalGet(key);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return batch;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
return await originalApply.call(this, nodes, view, baseCtx);
|
|
139
|
+
} finally {
|
|
140
|
+
view.batch = originalBatch;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return async () => {
|
|
145
|
+
base._handlers.apply = originalApply;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function applyEntryMutation(entry, mutation) {
|
|
150
|
+
if (typeof mutation === 'undefined') {
|
|
151
|
+
return entry;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (mutation === ValidatorEntryMutation.DELETE) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (entry && (b4a.isBuffer(mutation) || mutation instanceof Uint8Array)) {
|
|
159
|
+
return {
|
|
160
|
+
...entry,
|
|
161
|
+
value: b4a.from(mutation)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (mutation && typeof mutation === 'object') {
|
|
166
|
+
return mutation;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
throw new Error('Invalid validator entry mutation result.');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function extractValidatorAddress(payloadBuffer, path) {
|
|
173
|
+
const decoded = safeDecodeApplyOperation(payloadBuffer);
|
|
174
|
+
if (!decoded) return null;
|
|
175
|
+
|
|
176
|
+
const value = Array.isArray(path) && path.length > 0 ? traversePath(decoded, path) : null;
|
|
177
|
+
if (!value || !b4a.isBuffer(value) || value.length === 0) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const addressString = addressUtils.bufferToAddress(value);
|
|
182
|
+
if (!addressString) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { buffer: value, string: addressString };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function traversePath(payload, path) {
|
|
190
|
+
return path.reduce((current, segment) => (current ? current[segment] : null), payload);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function isValidatorKeyMatch(key, targetString, targetBuffer) {
|
|
194
|
+
if (typeof key === 'string') {
|
|
195
|
+
return key === targetString;
|
|
196
|
+
}
|
|
197
|
+
if (b4a.isBuffer(key) && targetBuffer) {
|
|
198
|
+
return b4a.equals(key, targetBuffer);
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
package/tests/unit/state/apply/common/validatorConsistency/validatorEntryDecodeFailureScenario.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import ValidatorEntryValidationScenarioBase from '../validatorEntryValidation/base/validatorEntryValidationScenarioBase.js';
|
|
3
|
+
|
|
4
|
+
export default class ValidatorEntryDecodeFailureScenario extends ValidatorEntryValidationScenarioBase {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
super({
|
|
7
|
+
...options,
|
|
8
|
+
mutateEntry: entry => {
|
|
9
|
+
if (!entry?.value) {
|
|
10
|
+
throw new Error('Validator entry decode scenario requires an existing entry.');
|
|
11
|
+
}
|
|
12
|
+
return { ...entry, value: b4a.alloc(1) };
|
|
13
|
+
},
|
|
14
|
+
expectedLogs: options?.expectedLogs ?? ['Failed to decode validator entry.']
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import OperationValidationScenarioBase from '../base/OperationValidationScenarioBase.js';
|
|
2
|
+
import { eventFlush } from '../../../../../helpers/autobaseTestHelpers.js';
|
|
3
|
+
|
|
4
|
+
export default class ValidatorEntryMissingScenario extends OperationValidationScenarioBase {
|
|
5
|
+
constructor({
|
|
6
|
+
title,
|
|
7
|
+
setupScenario,
|
|
8
|
+
buildValidPayload,
|
|
9
|
+
assertStateUnchanged,
|
|
10
|
+
selectNode = defaultSelectNode,
|
|
11
|
+
expectedLogs
|
|
12
|
+
}) {
|
|
13
|
+
super({
|
|
14
|
+
title,
|
|
15
|
+
setupScenario,
|
|
16
|
+
buildValidPayload,
|
|
17
|
+
mutatePayload: passThroughPayload,
|
|
18
|
+
applyInvalidPayload: createApplyInvalidPayload(selectNode),
|
|
19
|
+
assertStateUnchanged,
|
|
20
|
+
expectedLogs
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function passThroughPayload(_t, payload) {
|
|
26
|
+
return payload;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function defaultSelectNode(context) {
|
|
30
|
+
return context.adminBootstrap ?? context.bootstrap ?? context.peers?.[0] ?? null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createApplyInvalidPayload(selectNode) {
|
|
34
|
+
return async (context, payload) => {
|
|
35
|
+
const node = selectNode(context);
|
|
36
|
+
if (!node.base) {
|
|
37
|
+
throw new Error('Validator entry missing scenario requires a writable node.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await node.base.append(payload);
|
|
41
|
+
await node.base.update();
|
|
42
|
+
await eventFlush();
|
|
43
|
+
};
|
|
44
|
+
}
|