trac-msb 0.2.17 → 0.2.19
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/README.md +62 -2
- package/SECURITY.md +9 -6
- package/cli/commandHandler.js +272 -0
- package/cli/handlers.js +210 -0
- package/cli/interactive.js +182 -0
- package/msb.mjs +7 -8
- package/package.json +33 -11
- package/rpc/create_server.js +44 -44
- package/rpc/handlers.js +43 -56
- package/rpc/routes/index.js +8 -8
- package/rpc/rpc_server.js +6 -2
- package/rpc/utils/helpers.js +46 -66
- package/scripts/pear-runner.js +170 -0
- package/src/config/args.js +60 -13
- package/src/config/config.js +163 -14
- package/src/config/env.js +57 -25
- package/src/core/network/Network.js +121 -56
- package/src/core/network/protocols/LegacyProtocol.js +21 -11
- package/src/core/network/protocols/NetworkMessages.js +38 -17
- package/src/core/network/protocols/ProtocolInterface.js +15 -3
- package/src/core/network/protocols/ProtocolSession.js +144 -17
- package/src/core/network/protocols/V1Protocol.js +37 -18
- package/src/core/network/protocols/connectionPolicies.js +174 -0
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +26 -20
- package/src/core/network/protocols/{shared/handlers/base/BaseOperationHandler.js → legacy/handlers/BaseStateOperationHandler.js} +25 -14
- package/src/core/network/protocols/legacy/handlers/{GetRequestHandler.js → LegacyGetRequestHandler.js} +9 -9
- package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +23 -0
- package/src/core/network/protocols/{shared/handlers/RoleOperationHandler.js → legacy/handlers/LegacyRoleOperationHandler.js} +19 -12
- package/src/core/network/protocols/{shared/handlers/SubnetworkOperationHandler.js → legacy/handlers/LegacySubnetworkOperationHandler.js} +29 -18
- package/src/core/network/protocols/{shared/handlers/TransferOperationHandler.js → legacy/handlers/LegacyTransferOperationHandler.js} +18 -12
- package/src/core/network/protocols/legacy/validators/base/BaseResponse.js +7 -10
- package/src/core/network/protocols/shared/validators/{PartialBootstrapDeployment.js → PartialBootstrapDeploymentValidator.js} +9 -4
- package/src/core/network/protocols/shared/validators/{base/PartialOperation.js → PartialOperationValidator.js} +53 -31
- package/src/core/network/protocols/shared/validators/{PartialRoleAccess.js → PartialRoleAccessValidator.js} +51 -17
- package/src/core/network/protocols/shared/validators/{PartialTransaction.js → PartialTransactionValidator.js} +21 -7
- package/src/core/network/protocols/shared/validators/{PartialTransfer.js → PartialTransferValidator.js} +32 -14
- package/src/core/network/protocols/v1/NetworkMessageRouter.js +91 -7
- package/src/core/network/protocols/v1/V1ProtocolError.js +27 -0
- package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +66 -0
- package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +272 -0
- package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +88 -0
- package/src/core/network/protocols/v1/handlers/broadcastTransaction/BroadcastTransactionErrorMapper.js +84 -0
- package/src/core/network/protocols/v1/handlers/broadcastTransaction/BroadcastTransactionOperationStrategies.js +128 -0
- package/src/core/network/protocols/v1/handlers/broadcastTransaction/BroadcastTransactionPayloadUtils.js +26 -0
- package/src/core/network/protocols/v1/validators/V1BaseOperation.js +212 -0
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +28 -0
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +260 -0
- package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +15 -0
- package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +17 -0
- package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +210 -0
- package/src/core/network/services/ConnectionManager.js +149 -96
- package/src/core/network/services/MessageOrchestrator.js +197 -37
- package/src/core/network/services/PendingRequestService.js +175 -0
- package/src/core/network/services/TransactionCommitService.js +149 -0
- package/src/core/network/services/TransactionPoolService.js +129 -18
- package/src/core/network/services/TransactionRateLimiterService.js +57 -35
- package/src/core/network/services/ValidatorHealthCheckService.js +127 -0
- package/src/core/network/services/ValidatorObserverService.js +400 -121
- package/src/core/state/State.js +259 -156
- package/src/core/state/utils/address.js +4 -9
- package/src/core/state/utils/balance.js +7 -7
- package/src/core/state/utils/roles.js +1 -1
- package/src/index.js +320 -278
- package/src/messages/network/v1/NetworkMessageBuilder.js +71 -100
- package/src/messages/network/v1/NetworkMessageDirector.js +16 -50
- package/src/messages/network/v1/networkMessageFactory.js +1 -1
- package/src/messages/state/ApplyStateMessageBuilder.js +6 -6
- package/src/messages/state/applyStateMessageFactory.js +1 -1
- package/src/utils/Scheduler.js +0 -8
- package/src/utils/buffer.js +1 -1
- package/src/utils/check.js +12 -13
- package/src/utils/constants.js +78 -5
- package/src/utils/deepEqualApplyPayload.js +40 -0
- package/src/utils/errors.js +20 -0
- package/src/utils/fileUtils.js +12 -3
- package/src/utils/helpers.js +10 -1
- package/src/utils/logger.js +25 -0
- package/src/utils/migrationUtils.js +3 -3
- package/src/utils/normalizers.js +38 -0
- package/src/utils/protobuf/networkV1.generated.cjs +2460 -0
- package/src/utils/protobuf/operationHelpers.js +24 -3
- package/.dockerignore +0 -16
- package/.github/workflows/acceptance-tests.yml +0 -38
- package/.github/workflows/lint-pr-title.yml +0 -26
- package/.github/workflows/publish.yml +0 -33
- package/.github/workflows/unit-tests.yml +0 -34
- package/CODE_OF_CONDUCT.md +0 -128
- package/docker-compose.yml +0 -17
- package/dockerfile +0 -41
- package/docs/fee_distribution.md +0 -89
- package/docs/trac_network_http_api.openapi.yaml +0 -889
- package/migration/.gitkeep +0 -0
- package/proto/applyOperations.proto +0 -117
- package/proto/network.proto +0 -74
- package/rpc/rpc_services.js +0 -163
- package/scripts/generate-protobufs.js +0 -42
- package/src/core/network/identity/NetworkWalletFactory.js +0 -77
- package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +0 -37
- package/src/utils/cli.js +0 -130
- package/src/utils/cliCommands.js +0 -280
- package/src/utils/protobuf/network.cjs +0 -840
- package/tests/acceptance/v1/account/account.test.mjs +0 -123
- package/tests/acceptance/v1/balance/balance.test.mjs +0 -54
- package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +0 -116
- package/tests/acceptance/v1/confirmed-length/confirmed-length.test.mjs +0 -19
- package/tests/acceptance/v1/fee/fee.test.mjs +0 -11
- package/tests/acceptance/v1/health/health.test.mjs +0 -33
- package/tests/acceptance/v1/rpc.test.mjs +0 -95
- package/tests/acceptance/v1/tx/tx.test.mjs +0 -111
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +0 -213
- package/tests/acceptance/v1/tx-hashes/tx-hashes.test.mjs +0 -72
- package/tests/acceptance/v1/tx-payloads-bulk/tx-payloads-bulk.test.mjs +0 -27
- package/tests/acceptance/v1/txv/txv.test.mjs +0 -11
- package/tests/acceptance/v1/unconfirmed-length/unconfirmed-length.test.mjs +0 -11
- package/tests/fixtures/apply.fixtures.js +0 -77
- package/tests/fixtures/assembleMessage.fixtures.js +0 -41
- package/tests/fixtures/check.fixtures.js +0 -428
- package/tests/fixtures/networkV1.fixtures.js +0 -85
- package/tests/fixtures/protobuf.fixtures.js +0 -388
- package/tests/helpers/StateNetworkFactory.js +0 -155
- package/tests/helpers/address.js +0 -6
- package/tests/helpers/autobaseTestHelpers.js +0 -419
- package/tests/helpers/config.js +0 -4
- package/tests/helpers/createTestSignature.js +0 -13
- package/tests/helpers/regexHelper.js +0 -3
- package/tests/helpers/setupApplyTests.js +0 -587
- package/tests/helpers/transactionPayloads.mjs +0 -92
- package/tests/helpers/wrapper.js +0 -28
- package/tests/unit/messages/messages.test.js +0 -12
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +0 -276
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +0 -201
- package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +0 -525
- package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +0 -237
- package/tests/unit/network/ConnectionManager.test.js +0 -191
- package/tests/unit/network/NetworkWalletFactory.test.js +0 -156
- package/tests/unit/network/networkModule.test.js +0 -10
- package/tests/unit/state/State.test.js +0 -80
- package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +0 -42
- package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +0 -276
- package/tests/unit/state/apply/addAdmin/adminEntryEncodingFailureScenario.js +0 -30
- package/tests/unit/state/apply/addAdmin/adminEntryExistsScenario.js +0 -78
- package/tests/unit/state/apply/addAdmin/nodeEntryInitializationFailureScenario.js +0 -30
- package/tests/unit/state/apply/addAdmin/nonBootstrapNodeScenario.js +0 -68
- package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +0 -159
- package/tests/unit/state/apply/addIndexer/addIndexerHappyPathScenario.js +0 -39
- package/tests/unit/state/apply/addIndexer/addIndexerMultipleIndexersInTheNetworkScenario.js +0 -167
- package/tests/unit/state/apply/addIndexer/addIndexerPretenderAlreadyIndexerScenario.js +0 -21
- package/tests/unit/state/apply/addIndexer/addIndexerPretenderNotWriterScenario.js +0 -21
- package/tests/unit/state/apply/addIndexer/addIndexerRemoveAndReAddScenario.js +0 -186
- package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +0 -443
- package/tests/unit/state/apply/addIndexer/addIndexerWriterKeyAlreadyRegisteredScenario.js +0 -32
- package/tests/unit/state/apply/addIndexer/state.apply.addIndexer.test.js +0 -297
- package/tests/unit/state/apply/addWriter/addWriterHappyPathScenario.js +0 -41
- package/tests/unit/state/apply/addWriter/addWriterInvalidValidatorSignatureScenario.js +0 -32
- package/tests/unit/state/apply/addWriter/addWriterNewWkScenario.js +0 -149
- package/tests/unit/state/apply/addWriter/addWriterRequesterAlreadyWriterScenario.js +0 -21
- package/tests/unit/state/apply/addWriter/addWriterRequesterBalanceInsufficientScenario.js +0 -21
- package/tests/unit/state/apply/addWriter/addWriterRequesterEntryDecodeFailureScenario.js +0 -19
- package/tests/unit/state/apply/addWriter/addWriterRequesterEntryMissingScenario.js +0 -19
- package/tests/unit/state/apply/addWriter/addWriterRequesterIndexerScenario.js +0 -21
- package/tests/unit/state/apply/addWriter/addWriterRequesterNotWhitelistedScenario.js +0 -21
- package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +0 -766
- package/tests/unit/state/apply/addWriter/addWriterStakeBalanceUpdateFailureScenario.js +0 -50
- package/tests/unit/state/apply/addWriter/addWriterStakeInsufficientBalanceScenario.js +0 -29
- package/tests/unit/state/apply/addWriter/addWriterStakeInvalidBalanceScenario.js +0 -29
- package/tests/unit/state/apply/addWriter/addWriterStakeInvalidEntryScenario.js +0 -21
- package/tests/unit/state/apply/addWriter/addWriterStakeStakedBalanceFailureScenario.js +0 -37
- package/tests/unit/state/apply/addWriter/addWriterStakeSubtractFailureScenario.js +0 -42
- package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +0 -106
- package/tests/unit/state/apply/addWriter/addWriterWriterKeyMismatchScenario.js +0 -54
- package/tests/unit/state/apply/addWriter/addWriterWriterKeyOwnershipScenario.js +0 -54
- package/tests/unit/state/apply/addWriter/addWriterZeroWriterKeyScenario.js +0 -29
- package/tests/unit/state/apply/addWriter/state.apply.addWriter.test.js +0 -309
- package/tests/unit/state/apply/adminRecovery/adminRecoveryHappyPathScenario.js +0 -30
- package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +0 -881
- package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +0 -435
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistBanAndReapplyScenario.js +0 -78
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistExistingReaderHappyPathScenario.js +0 -98
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistFeeAfterDisableScenario.js +0 -66
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistHappyPathScenario.js +0 -55
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistInsufficientAdminBalanceScenario.js +0 -103
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistNodeAlreadyWhitelistedScenario.js +0 -60
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +0 -189
- package/tests/unit/state/apply/appendWhitelist/state.apply.appendWhitelist.test.js +0 -220
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationHappyPathScenario.js +0 -82
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +0 -111
- package/tests/unit/state/apply/balanceInitialization/invalidAmountScenario.js +0 -45
- package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +0 -82
- package/tests/unit/state/apply/balanceInitialization/state.apply.balanceInitialization.test.js +0 -189
- package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +0 -156
- package/tests/unit/state/apply/banValidator/banValidatorHappyPathScenario.js +0 -36
- package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +0 -531
- package/tests/unit/state/apply/banValidator/banValidatorSequentialBansScenario.js +0 -74
- package/tests/unit/state/apply/banValidator/banValidatorTargetDecodeFailureScenario.js +0 -19
- package/tests/unit/state/apply/banValidator/banValidatorTargetIndexerScenario.js +0 -32
- package/tests/unit/state/apply/banValidator/banValidatorTargetNodeEntryMissingScenario.js +0 -19
- package/tests/unit/state/apply/banValidator/banValidatorTargetRoleUpdateFailureScenario.js +0 -19
- package/tests/unit/state/apply/banValidator/banValidatorWhitelistedNonWriterScenario.js +0 -38
- package/tests/unit/state/apply/banValidator/banValidatorWhitelistedZeroBalanceScenario.js +0 -91
- package/tests/unit/state/apply/banValidator/banValidatorWithdrawFailureScenario.js +0 -19
- package/tests/unit/state/apply/banValidator/state.apply.banValidator.test.js +0 -266
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +0 -143
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentHappyPathScenario.js +0 -26
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentIncompleteOperationScenario.js +0 -94
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentInvalidDeploymentEntryScenario.js +0 -37
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentMultipleBootstrapScenario.js +0 -86
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +0 -347
- package/tests/unit/state/apply/bootstrapDeployment/invalidValidatorNodeEntryScenario.js +0 -57
- package/tests/unit/state/apply/bootstrapDeployment/state.apply.bootstrapDeployment.test.js +0 -429
- package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +0 -120
- package/tests/unit/state/apply/common/access-control/adminEntryDecodeFailureScenario.js +0 -130
- package/tests/unit/state/apply/common/access-control/adminEntryExistsScenario.js +0 -93
- package/tests/unit/state/apply/common/access-control/adminEntryMissingScenario.js +0 -108
- package/tests/unit/state/apply/common/access-control/adminOnlyGuardScenario.js +0 -126
- package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +0 -121
- package/tests/unit/state/apply/common/access-control/roleAccessOperationValidationScenario.js +0 -50
- package/tests/unit/state/apply/common/adminControlOperationValidationScenario.js +0 -56
- package/tests/unit/state/apply/common/balances/adminEntryUpdateFailureScenario.js +0 -52
- package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +0 -198
- package/tests/unit/state/apply/common/balances/feeDecodeFailureScenario.js +0 -52
- package/tests/unit/state/apply/common/balances/requesterBalanceDecodeFailureScenario.js +0 -15
- package/tests/unit/state/apply/common/balances/requesterBalanceFeeApplicationFailureScenario.js +0 -11
- package/tests/unit/state/apply/common/balances/requesterBalanceInsufficientScenario.js +0 -15
- package/tests/unit/state/apply/common/balances/requesterBalanceUpdateFailureScenario.js +0 -11
- package/tests/unit/state/apply/common/balances/validatorEntryRewardFailureScenario.js +0 -11
- package/tests/unit/state/apply/common/balances/validatorEntryUpdateFailureScenario.js +0 -11
- package/tests/unit/state/apply/common/balances/validatorNodeEntryDecodeFailureScenario.js +0 -40
- package/tests/unit/state/apply/common/base/OperationValidationScenarioBase.js +0 -114
- package/tests/unit/state/apply/common/commonScenarioHelper.js +0 -103
- package/tests/unit/state/apply/common/indexer/indexerNodeEntryDecodeFailureScenario.js +0 -36
- package/tests/unit/state/apply/common/indexer/indexerNodeEntryMissingScenario.js +0 -36
- package/tests/unit/state/apply/common/indexer/indexerRoleUpdateFailureScenario.js +0 -29
- package/tests/unit/state/apply/common/indexer/indexerSequenceStateInvalidScenario.js +0 -66
- package/tests/unit/state/apply/common/invalidMessageComponentValidationScenario.js +0 -84
- package/tests/unit/state/apply/common/nodeEntryInitializationFailureScenario.js +0 -47
- package/tests/unit/state/apply/common/operationAlreadyAppliedScenario.js +0 -85
- package/tests/unit/state/apply/common/payload-structure/addressWithInvalidPublicKeyScenario.js +0 -52
- package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +0 -54
- package/tests/unit/state/apply/common/payload-structure/invalidAddressValidationScenario.js +0 -73
- package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +0 -71
- package/tests/unit/state/apply/common/payload-structure/invalidPayloadValidationScenario.js +0 -31
- package/tests/unit/state/apply/common/payload-structure/invalidSignatureValidationScenario.js +0 -142
- package/tests/unit/state/apply/common/payload-structure/partialOperationValidationScenario.js +0 -87
- package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +0 -71
- package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +0 -73
- package/tests/unit/state/apply/common/requester/requesterNodeEntryMissingScenario.js +0 -36
- package/tests/unit/state/apply/common/requesterAddressValidationScenario.js +0 -44
- package/tests/unit/state/apply/common/requesterPublicKeyValidationScenario.js +0 -25
- package/tests/unit/state/apply/common/transactionValidityMismatchScenario.js +0 -98
- package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +0 -202
- package/tests/unit/state/apply/common/validatorConsistency/validatorEntryDecodeFailureScenario.js +0 -17
- package/tests/unit/state/apply/common/validatorConsistency/validatorEntryMissingScenario.js +0 -44
- package/tests/unit/state/apply/common/validatorConsistency/validatorInactiveScenario.js +0 -19
- package/tests/unit/state/apply/common/validatorConsistency/validatorWriterKeyMismatchScenario.js +0 -18
- package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +0 -315
- package/tests/unit/state/apply/common/validatorEntryValidation/validatorEntryInvalidBalanceScenario.js +0 -18
- package/tests/unit/state/apply/common/writerKeyExistsValidationScenario.js +0 -43
- package/tests/unit/state/apply/disableInitialization/disableInitializationAlreadyDisabledScenario.js +0 -53
- package/tests/unit/state/apply/disableInitialization/disableInitializationHappyPathScenario.js +0 -24
- package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +0 -204
- package/tests/unit/state/apply/disableInitialization/state.apply.disableInitialization.test.js +0 -161
- package/tests/unit/state/apply/missing-tests.md +0 -18
- package/tests/unit/state/apply/removeIndexer/removeIndexerHappyPathScenario.js +0 -58
- package/tests/unit/state/apply/removeIndexer/removeIndexerReAddAndRemoveAgainScenario.js +0 -98
- package/tests/unit/state/apply/removeIndexer/removeIndexerRemoveMultipleIndexersScenario.js +0 -167
- package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +0 -429
- package/tests/unit/state/apply/removeIndexer/removeIndexerTargetNotIndexerScenario.js +0 -22
- package/tests/unit/state/apply/removeIndexer/removeIndexerWriterKeyMissingScenario.js +0 -20
- package/tests/unit/state/apply/removeIndexer/state.apply.removeIndexer.test.js +0 -291
- package/tests/unit/state/apply/removeWriter/removeWriterAndAddWriterAgainScenario.js +0 -87
- package/tests/unit/state/apply/removeWriter/removeWriterHappyPathScenario.js +0 -38
- package/tests/unit/state/apply/removeWriter/removeWriterInvalidValidatorSignatureScenario.js +0 -32
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterBalanceInsufficientScenario.js +0 -21
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryDecodeFailureScenario.js +0 -19
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryMissingScenario.js +0 -19
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterIndexerScenario.js +0 -21
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterNotWriterScenario.js +0 -21
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterRoleUpdateFailureScenario.js +0 -19
- package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +0 -348
- package/tests/unit/state/apply/removeWriter/removeWriterThroughWriterValidatorScenario.js +0 -113
- package/tests/unit/state/apply/removeWriter/removeWriterUnstakeFailureScenario.js +0 -33
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyMismatchScenario.js +0 -21
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyOwnershipScenario.js +0 -26
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyRegistryMissingScenario.js +0 -22
- package/tests/unit/state/apply/removeWriter/state.apply.removeWriter.test.js +0 -307
- package/tests/unit/state/apply/state.apply.test.js +0 -24
- package/tests/unit/state/apply/transfer/state.apply.transfer.test.js +0 -819
- package/tests/unit/state/apply/transfer/transferContractSchemaValidationScenario.js +0 -22
- package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +0 -146
- package/tests/unit/state/apply/transfer/transferDoubleSpendSameBatchScenario.js +0 -63
- package/tests/unit/state/apply/transfer/transferDoubleSpendSingleValidatorScenario.js +0 -67
- package/tests/unit/state/apply/transfer/transferExistingRecipientAmountScenario.js +0 -31
- package/tests/unit/state/apply/transfer/transferExistingRecipientZeroAmountScenario.js +0 -31
- package/tests/unit/state/apply/transfer/transferHandlerGuardScenarios.js +0 -22
- package/tests/unit/state/apply/transfer/transferHappyPathScenario.js +0 -8
- package/tests/unit/state/apply/transfer/transferInvalidIncomingDataScenario.js +0 -66
- package/tests/unit/state/apply/transfer/transferNewRecipientAmountScenario.js +0 -31
- package/tests/unit/state/apply/transfer/transferNewRecipientZeroAmountScenario.js +0 -31
- package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +0 -1170
- package/tests/unit/state/apply/transfer/transferSelfTransferAmountScenario.js +0 -38
- package/tests/unit/state/apply/transfer/transferSelfTransferZeroAmountScenario.js +0 -38
- package/tests/unit/state/apply/transfer/transferValidatorRecipientAmountScenario.js +0 -38
- package/tests/unit/state/apply/transfer/transferValidatorRecipientZeroAmountScenario.js +0 -38
- package/tests/unit/state/apply/txOperation/state.apply.txOperation.test.js +0 -318
- package/tests/unit/state/apply/txOperation/txOperationBootstrapNotRegisteredScenario.js +0 -70
- package/tests/unit/state/apply/txOperation/txOperationDifferentValidatorCreatorHappyPathScenario.js +0 -23
- package/tests/unit/state/apply/txOperation/txOperationInvalidDeploymentEntryScenario.js +0 -48
- package/tests/unit/state/apply/txOperation/txOperationInvalidFeeAmountScenario.js +0 -39
- package/tests/unit/state/apply/txOperation/txOperationInvalidSubnetCreatorAddressScenario.js +0 -46
- package/tests/unit/state/apply/txOperation/txOperationRequesterCreatorHappyPathScenario.js +0 -21
- package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +0 -432
- package/tests/unit/state/apply/txOperation/txOperationStandardHappyPathScenario.js +0 -21
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddCreatorBalanceFailureScenario.js +0 -26
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBalanceFailureScenario.js +0 -25
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBonusFailureScenario.js +0 -27
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeCreatorEntryScenario.js +0 -18
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeRequesterEntryScenario.js +0 -17
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeValidatorEntryScenario.js +0 -31
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardBypassScenario.js +0 -49
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +0 -93
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInsufficientRequesterBalanceScenario.js +0 -28
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidCreatorBalanceScenario.js +0 -29
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterBalanceScenario.js +0 -28
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterEntryScenario.js +0 -17
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidValidatorBalanceScenario.js +0 -33
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeMissingCreatorEntryScenario.js +0 -18
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeSubtractFailureScenario.js +0 -25
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateCreatorBalanceFailureScenario.js +0 -26
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateFailureScenario.js +0 -25
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBalanceFailureScenario.js +0 -26
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBonusFailureScenario.js +0 -27
- package/tests/unit/state/apply/txOperation/txOperationValidatorCreatorHappyPathScenario.js +0 -21
- package/tests/unit/state/stateModule.test.js +0 -23
- package/tests/unit/state/stateTestUtils.js +0 -32
- package/tests/unit/state/utils/address.test.js +0 -63
- package/tests/unit/state/utils/adminEntry.test.js +0 -52
- package/tests/unit/state/utils/balance.test.js +0 -320
- package/tests/unit/state/utils/lengthEntry.test.js +0 -54
- package/tests/unit/state/utils/nodeEntry.test.js +0 -298
- package/tests/unit/state/utils/roles.test.js +0 -44
- package/tests/unit/unit.test.js +0 -14
- package/tests/unit/utils/amountSerialization/amountSerialization.test.js +0 -237
- package/tests/unit/utils/buffer/buffer.test.js +0 -251
- package/tests/unit/utils/check/adminControlOperation.test.js +0 -156
- package/tests/unit/utils/check/balanceInitializationOperation.test.js +0 -54
- package/tests/unit/utils/check/bootstrapDeploymentOperation.test.js +0 -104
- package/tests/unit/utils/check/check.test.js +0 -17
- package/tests/unit/utils/check/common.test.js +0 -419
- package/tests/unit/utils/check/coreAdminOperation.test.js +0 -95
- package/tests/unit/utils/check/roleAccessOperation.test.js +0 -255
- package/tests/unit/utils/check/transactionOperation.test.js +0 -107
- package/tests/unit/utils/check/transferOperation.test.js +0 -102
- package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +0 -95
- package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +0 -149
- package/tests/unit/utils/helpers/helpers.test.js +0 -11
- package/tests/unit/utils/helpers/isHexString.test.js +0 -12
- package/tests/unit/utils/helpers/normalizeHex.test.js +0 -82
- package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +0 -100
- package/tests/unit/utils/normalizers/normalizers.test.js +0 -469
- package/tests/unit/utils/protobuf/applyOperations.test.js +0 -91
- package/tests/unit/utils/protobuf/operationHelpers.test.js +0 -305
- package/tests/unit/utils/protobuf/protobuf.test.js +0 -12
- package/tests/unit/utils/type/type.test.js +0 -25
- package/tests/unit/utils/utils.test.js +0 -20
- package/whitelist/.gitkeep +0 -0
|
@@ -1,186 +1,465 @@
|
|
|
1
|
-
import PeerWallet from "trac-wallet";
|
|
2
|
-
import b4a from "b4a";
|
|
3
|
-
import { bufferToAddress } from '../../state/utils/address.js';
|
|
4
|
-
import { sleep } from '../../../utils/helpers.js';
|
|
5
1
|
import Scheduler from "../../../utils/Scheduler.js";
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const debugLog = (...args) => {
|
|
16
|
-
if (DEBUG) {
|
|
17
|
-
console.log('DEBUG [ValidatorObserverService] ==> ', ...args);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
2
|
+
import { Logger } from "../../../utils/logger.js";
|
|
3
|
+
import b4a from "b4a";
|
|
4
|
+
import { bufferToAddress } from "../../../core/state/utils/address.js";
|
|
5
|
+
import tracCryptoApi from "trac-crypto-api";
|
|
6
|
+
import { WRITER_BYTE_LENGTH, CONNECTION_STATUS } from "../../../utils/constants.js";
|
|
7
|
+
|
|
8
|
+
// Internal constants
|
|
9
|
+
const VALIDATOR_CANDIDATES_PER_CYCLE = 10;
|
|
10
|
+
const MAX_KEY_DECODE_CACHE_SIZE = 10_000;
|
|
20
11
|
|
|
21
12
|
class ValidatorObserverService {
|
|
22
|
-
#config;
|
|
23
|
-
#state;
|
|
24
13
|
#network;
|
|
25
|
-
#
|
|
14
|
+
#state;
|
|
26
15
|
#address;
|
|
27
|
-
#
|
|
16
|
+
#config;
|
|
17
|
+
#scheduler;
|
|
18
|
+
#logger;
|
|
19
|
+
#hasBootstrapped; // Whether initial connection phase is complete
|
|
20
|
+
#bootstrapStartedAt; // Timestamp to enforce bootstrap timeout
|
|
21
|
+
#adminCache; // Cached admin entry with TTL
|
|
22
|
+
#writersCache; // Cached writer list with dynamic TTL
|
|
23
|
+
#isInterrupted; // Used to stop execution mid-cycle
|
|
24
|
+
#keyDecodeCache;
|
|
28
25
|
|
|
29
26
|
/**
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {string} address
|
|
33
|
-
* @param {
|
|
34
|
-
|
|
27
|
+
* @param {Object} network - The network layer instance (e.g., Hyperswarm wrapper).
|
|
28
|
+
* @param {Object} state - The state manager instance interacting with Autobase.
|
|
29
|
+
* @param {string} address - The physical address of the current node.
|
|
30
|
+
* @param {Object} config - The application configuration object.
|
|
31
|
+
*/
|
|
35
32
|
constructor(network, state, address, config) {
|
|
36
|
-
this.#config = config
|
|
37
33
|
this.#network = network;
|
|
38
34
|
this.#state = state;
|
|
39
35
|
this.#address = address;
|
|
36
|
+
this.#config = config;
|
|
37
|
+
this.#logger = new Logger(config);
|
|
38
|
+
this.#hasBootstrapped = false;
|
|
39
|
+
this.#bootstrapStartedAt = 0;
|
|
40
|
+
this.#adminCache = { entry: null, lastUpdated: 0 };
|
|
41
|
+
this.#writersCache = { list: [], lastUpdated: 0 };
|
|
40
42
|
this.#isInterrupted = false;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
this.#keyDecodeCache = new Map();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns configurable poll interval.
|
|
48
|
+
* @returns {number} The poll interval in milliseconds.
|
|
49
|
+
*/
|
|
50
|
+
#getPollInterval() {
|
|
51
|
+
return this.#config.pollInterval;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Selects a subset of valid validator candidates to connect to.
|
|
56
|
+
*
|
|
57
|
+
* Rules:
|
|
58
|
+
* - Assumes writers list is already pre-filtered and deduplicated from scan phase.
|
|
59
|
+
* - Excludes self.
|
|
60
|
+
* - Excludes already connected or pending nodes.
|
|
61
|
+
* - Applies admin connection restriction based on the threshold of ALIVE writers.
|
|
62
|
+
* - Randomizes selection (Fisher-Yates) to avoid bias.
|
|
63
|
+
* @param {Array<Object>} writers - The deduplicated list of active writers.
|
|
64
|
+
* @param {Object} adminEntry - The current admin node entry.
|
|
65
|
+
* @returns {{candidates: Array<Object>, slots: number}} The selected candidates and the number of available connection slots.
|
|
66
|
+
*/
|
|
67
|
+
#selectCandidates(writers, adminEntry) {
|
|
68
|
+
const manager = this.#network.validatorConnectionManager;
|
|
69
|
+
const adminThreshold = this.#config.maxWritersForAdminIndexerConnection;
|
|
70
|
+
|
|
71
|
+
// Evaluates the admin blockage policy based on fully connected/alive writers
|
|
72
|
+
const aliveWritersCount = this.#countAliveWriters(writers, adminEntry);
|
|
73
|
+
const adminBlocked = adminEntry && aliveWritersCount >= adminThreshold;
|
|
74
|
+
|
|
75
|
+
// Target calculation: ensures the observer deducts its own slot from the network size
|
|
76
|
+
// only if it belongs to the valid writers list.
|
|
77
|
+
const isSelfInWriters = writers.some(w => w.address === this.#address);
|
|
78
|
+
const networkSize = isSelfInWriters ? writers.length - 1 : writers.length;
|
|
79
|
+
const target = Math.min(networkSize, this.#config.maxValidators);
|
|
80
|
+
|
|
81
|
+
const isSelfAdmin = this.#address === adminEntry?.address;
|
|
82
|
+
const totalConnected = manager.connectedValidators().length;
|
|
83
|
+
|
|
84
|
+
const available = writers.filter((w) => {
|
|
85
|
+
if (w.address === this.#address) return false;
|
|
86
|
+
|
|
87
|
+
const isConnected = manager.connected(w.publicKey);
|
|
88
|
+
|
|
89
|
+
// Checks if we are ALREADY trying to connect to this specific node
|
|
90
|
+
const isPending = this.#network.isConnectionPending(w.publicKeyHex);
|
|
91
|
+
|
|
92
|
+
// Skips nodes we are already interacting with
|
|
93
|
+
if (isConnected || isPending) return false;
|
|
94
|
+
|
|
95
|
+
// The admin is actively filtered out from the pool if the threshold policy is triggered
|
|
96
|
+
if (adminBlocked && !isSelfAdmin && w.address === adminEntry?.address) return false;
|
|
97
|
+
|
|
98
|
+
return true;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const result = [...available];
|
|
102
|
+
|
|
103
|
+
// Fisher-Yates shuffle
|
|
104
|
+
for (let i = 0; i < result.length; i++) {
|
|
105
|
+
const j = i + Math.floor(Math.random() * (result.length - i));
|
|
106
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Open slots based ONLY on who is ALREADY connected,
|
|
110
|
+
// Ignoring pending/non-active nodes to avoid blocking the queue.
|
|
111
|
+
const slots = Math.max(0, target - totalConnected);
|
|
112
|
+
const limit = Math.min(slots, VALIDATOR_CANDIDATES_PER_CYCLE, result.length);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
candidates: result.slice(0, limit),
|
|
116
|
+
slots: slots,
|
|
46
117
|
}
|
|
47
118
|
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Enforces admin-specific connection rules.
|
|
122
|
+
*
|
|
123
|
+
* If the count of fully connected writers exceeds the threshold:
|
|
124
|
+
* - The admin node is excluded from the validator pool (handled in selection phase).
|
|
125
|
+
* - Non-admin nodes actively disconnect from the admin validator (if currently connected).
|
|
126
|
+
*
|
|
127
|
+
* NOTE:
|
|
128
|
+
* The admin is not forcibly disconnected from all roles.
|
|
129
|
+
* It can still maintain connections for tx submission and observation.
|
|
130
|
+
* @param {Array<Object>} writers - The deduplicated list of active writers.
|
|
131
|
+
* @param {Object} adminEntry - The current admin node entry.
|
|
132
|
+
*/
|
|
133
|
+
#enforceAdminPolicy(writers, adminEntry) {
|
|
134
|
+
// Evaluates against alive network peers rather than dormant database entries
|
|
135
|
+
const aliveWritersCount = this.#countAliveWriters(writers, adminEntry);
|
|
136
|
+
|
|
137
|
+
if (aliveWritersCount < this.#config.maxWritersForAdminIndexerConnection) return;
|
|
138
|
+
|
|
139
|
+
this.#logger.debug("Validator threshold reached → enforcing admin policy");
|
|
140
|
+
|
|
141
|
+
const isAdmin = adminEntry && this.#address === adminEntry.address;
|
|
142
|
+
if (isAdmin) return;
|
|
48
143
|
|
|
49
|
-
|
|
50
|
-
|
|
144
|
+
// Non-admin nodes: ensure the admin is not kept as a validator connection
|
|
145
|
+
const adminWriter = writers.find((w) => w.address === adminEntry?.address);
|
|
146
|
+
if (adminWriter?.publicKey) {
|
|
147
|
+
this.#network.validatorConnectionManager.remove(adminWriter.publicKey);
|
|
148
|
+
}
|
|
51
149
|
}
|
|
52
150
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Removes active connections to writers that are no longer valid (e.g., removed/soft-deleted).
|
|
153
|
+
* @param {Uint8Array} publicKey - The public key of the stale connection to be removed.
|
|
154
|
+
*/
|
|
155
|
+
#enforceNoStaleConnections(publicKey) {
|
|
156
|
+
const manager = this.#network.validatorConnectionManager;
|
|
157
|
+
|
|
158
|
+
if (manager.connected(publicKey)) {
|
|
159
|
+
manager.remove(publicKey);
|
|
160
|
+
this.#logger.debug(`Removed stale validator connection: ${b4a.toString(publicKey, "hex")}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Determines whether the observer should run.
|
|
166
|
+
* Controlled by a config flag + runtime interruption.
|
|
167
|
+
* @returns {boolean} True if the observer is enabled and not interrupted.
|
|
168
|
+
*/
|
|
169
|
+
#shouldRun() {
|
|
170
|
+
return this.#config.enableValidatorObserver && !this.#isInterrupted;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Counts the number of physically active and connected writers in the network.
|
|
175
|
+
* This method bridges the gap between the Autobase state (which may include
|
|
176
|
+
* offline/dormant writers due to its append-only/soft-delete nature) and the
|
|
177
|
+
* actual physical network layer. It ensures the admin threshold policy is
|
|
178
|
+
* evaluated against truly active nodes rather than historical state.
|
|
179
|
+
*
|
|
180
|
+
* @param {Array<Object>} writers - The deduplicated list of writers from the Autobase.
|
|
181
|
+
* @param {Object} adminEntry - The current admin node entry.
|
|
182
|
+
* @returns {number} The count of alive, fully connected writers (including self).
|
|
183
|
+
*/
|
|
184
|
+
#countAliveWriters(writers, adminEntry) {
|
|
185
|
+
let aliveCount = 0;
|
|
186
|
+
const manager = this.#network.validatorConnectionManager;
|
|
187
|
+
|
|
188
|
+
for (const w of writers) {
|
|
189
|
+
// Ignores the admin in the threshold calculation
|
|
190
|
+
if (w.address === adminEntry?.address) continue;
|
|
191
|
+
|
|
192
|
+
// Always counts the current node as alive if it's in the valid writers list
|
|
193
|
+
if (w.address === this.#address) {
|
|
194
|
+
aliveCount++;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Counts only fully connected nodes (ignores pending or offline peers)
|
|
199
|
+
if (manager.connected(w.publicKey)) {
|
|
200
|
+
aliveCount++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return aliveCount;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Starts the validator observer loop.
|
|
209
|
+
* Initializes the scheduler and bootstrap state.
|
|
210
|
+
*/
|
|
56
211
|
async start() {
|
|
57
212
|
if (!this.#shouldRun()) {
|
|
58
|
-
|
|
213
|
+
this.#logger.info('ValidatorObserverService can not start. Disabled by configuration.');
|
|
59
214
|
return;
|
|
60
215
|
}
|
|
216
|
+
|
|
61
217
|
if (this.#scheduler && this.#scheduler.isRunning) {
|
|
62
|
-
|
|
218
|
+
this.#logger.info('ValidatorObserverService is already started');
|
|
63
219
|
return;
|
|
64
220
|
}
|
|
65
221
|
|
|
222
|
+
const interval = this.#getPollInterval();
|
|
223
|
+
this.#scheduler = new Scheduler((next) => this.#worker(next), interval);
|
|
224
|
+
this.#bootstrapStartedAt = Date.now();
|
|
225
|
+
this.#hasBootstrapped = false;
|
|
66
226
|
this.#isInterrupted = false;
|
|
67
|
-
this.#scheduler = new Scheduler(next => this.#worker(next), POLL_INTERVAL);
|
|
68
227
|
this.#scheduler.start();
|
|
228
|
+
this.#logger.debug("ValidatorObserverService started");
|
|
69
229
|
}
|
|
70
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Stops the observer gracefully.
|
|
233
|
+
* Prevents new work and waits for the current cycle to finish if requested.
|
|
234
|
+
* @param {boolean} [waitForCurrent=true] - Whether to wait for the current worker cycle to finish.
|
|
235
|
+
*/
|
|
71
236
|
async stopValidatorObserver(waitForCurrent = true) {
|
|
72
237
|
if (!this.#scheduler) return;
|
|
238
|
+
|
|
73
239
|
this.#isInterrupted = true;
|
|
240
|
+
|
|
74
241
|
await this.#scheduler.stop(waitForCurrent);
|
|
75
242
|
this.#scheduler = null;
|
|
76
|
-
|
|
243
|
+
this.#logger.debug("ValidatorObserverService stopped");
|
|
77
244
|
}
|
|
78
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Main worker loop (executed periodically).
|
|
248
|
+
*
|
|
249
|
+
* Flow:
|
|
250
|
+
* 1. Fetch admin + writers (cached).
|
|
251
|
+
* 2. Enforce admin connection policy.
|
|
252
|
+
* 3. Skip if the connection pool is full.
|
|
253
|
+
* 4. Select candidates.
|
|
254
|
+
* 5. Attempt connections (with pacing).
|
|
255
|
+
* @param {Function} next - Callback to schedule the next execution.
|
|
256
|
+
*/
|
|
79
257
|
async #worker(next) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const length = await this.#lengthEntry()
|
|
83
|
-
|
|
84
|
-
const promises = [];
|
|
85
|
-
for (let i = 0; i < VALIDATOR_CANDIDATES_PER_CYCLE; i++) {
|
|
86
|
-
promises.push(this.#findValidator(this.#address, length + 1));
|
|
87
|
-
await sleep(DELAY_INTERVAL); // Low key dangerous as the network progresses
|
|
88
|
-
}
|
|
89
|
-
await Promise.all(promises);
|
|
258
|
+
const interval = this.#getPollInterval();
|
|
259
|
+
if (!this.#shouldRun()) return next(interval);
|
|
90
260
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
261
|
+
try {
|
|
262
|
+
const adminEntry = await this.#getAdminEntryCached();
|
|
263
|
+
const writers = await this.#getWritersCached(adminEntry);
|
|
264
|
+
|
|
265
|
+
if (writers.length === 0) return next(interval);
|
|
266
|
+
|
|
267
|
+
this.#enforceAdminPolicy(writers, adminEntry);
|
|
268
|
+
|
|
269
|
+
const manager = this.#network.validatorConnectionManager;
|
|
270
|
+
// CEILING: do not exceed the max connections limit
|
|
271
|
+
if (manager.maxConnectionsReached()) return next(interval);
|
|
272
|
+
|
|
273
|
+
const { candidates, slots} = this.#selectCandidates(writers, adminEntry);
|
|
274
|
+
if (candidates.length > 0) {
|
|
275
|
+
await this.#processCandidates(candidates, slots);
|
|
101
276
|
}
|
|
277
|
+
|
|
278
|
+
} catch (err) {
|
|
279
|
+
this.#logger.error(`ValidatorObserver worker error: ${err.message}`);
|
|
102
280
|
}
|
|
103
|
-
|
|
281
|
+
|
|
282
|
+
next(interval);
|
|
104
283
|
}
|
|
105
284
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Returns the cached admin entry with TTL.
|
|
287
|
+
* @returns {Promise<Object>} The admin entry object.
|
|
288
|
+
*/
|
|
289
|
+
async #getAdminEntryCached() {
|
|
290
|
+
const now = Date.now();
|
|
291
|
+
const ttl = this.#config.adminCacheTTL;
|
|
292
|
+
if (this.#adminCache.entry && now - this.#adminCache.lastUpdated < ttl) return this.#adminCache.entry;
|
|
112
293
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
294
|
+
const entry = await this.#state.getAdminEntry();
|
|
295
|
+
this.#adminCache = { entry, lastUpdated: now };
|
|
296
|
+
return entry;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Returns the cached writer list with dynamic TTL:
|
|
301
|
+
* - Short TTL during bootstrap (aggressive discovery).
|
|
302
|
+
* - Long TTL after bootstrap (stability).
|
|
303
|
+
*
|
|
304
|
+
* Bootstrap completes when:
|
|
305
|
+
* - Connection pool is full OR
|
|
306
|
+
* - Timeout is reached.
|
|
307
|
+
* @param {Object} adminEntry - The current admin node entry.
|
|
308
|
+
* @returns {Promise<Array<Object>>} The deduplicated list of writers.
|
|
309
|
+
*/
|
|
310
|
+
async #getWritersCached(adminEntry) {
|
|
311
|
+
const now = Date.now();
|
|
312
|
+
const cachedList = this.#writersCache.list;
|
|
313
|
+
const lastUpdated = this.#writersCache.lastUpdated;
|
|
314
|
+
const manager = this.#network.validatorConnectionManager;
|
|
119
315
|
|
|
120
|
-
if (
|
|
121
|
-
|
|
316
|
+
if (!this.#hasBootstrapped && manager.maxConnectionsReached()) {
|
|
317
|
+
this.#hasBootstrapped = true;
|
|
318
|
+
this.#logger.debug("Bootstrap complete (pool full)");
|
|
122
319
|
}
|
|
123
|
-
|
|
124
|
-
|
|
320
|
+
|
|
321
|
+
if (!this.#hasBootstrapped && now - this.#bootstrapStartedAt > this.#config.bootstrapTimeout) {
|
|
322
|
+
this.#hasBootstrapped = true;
|
|
323
|
+
this.#logger.debug("Bootstrap complete (timeout)");
|
|
125
324
|
}
|
|
325
|
+
|
|
326
|
+
const short = this.#config.writersShortCacheTTL;
|
|
327
|
+
const long = this.#config.writersLongCacheTTL;
|
|
328
|
+
|
|
329
|
+
const dynamicTTL = this.#hasBootstrapped ? long : short;
|
|
330
|
+
if (cachedList.length > 0 && now - lastUpdated < dynamicTTL) return cachedList;
|
|
331
|
+
|
|
332
|
+
const list = await this.#scanAutobaseWriters(adminEntry);
|
|
333
|
+
this.#writersCache = { list, lastUpdated: now };
|
|
126
334
|
|
|
127
|
-
|
|
335
|
+
return list;
|
|
336
|
+
}
|
|
128
337
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Sequentially processes selected candidates.
|
|
340
|
+
* Applies delay between connection attempts to avoid network bursts.
|
|
341
|
+
* @param {Array<Object>} candidates - The list of selected validator candidates.
|
|
342
|
+
* @param {number} slots - The number of available connection slots.
|
|
343
|
+
*/
|
|
344
|
+
async #processCandidates(candidates, slots) {
|
|
345
|
+
const delay = this.#config.validatorConnectionAttemptDelay;
|
|
133
346
|
|
|
134
|
-
|
|
135
|
-
this.#network.tryConnect(validatorPubKeyHex, 'validator');
|
|
136
|
-
}
|
|
137
|
-
};
|
|
347
|
+
let attempts = 0;
|
|
138
348
|
|
|
139
|
-
|
|
140
|
-
|
|
349
|
+
for (const candidate of candidates) {
|
|
350
|
+
if (!this.#shouldRun()) break;
|
|
351
|
+
if (attempts >= slots) break;
|
|
141
352
|
|
|
142
|
-
|
|
143
|
-
|
|
353
|
+
const isConnected = this.#network.validatorConnectionManager.connected(candidate.publicKey);
|
|
354
|
+
const isPending = this.#network.isConnectionPending(candidate.publicKeyHex);
|
|
144
355
|
|
|
145
|
-
|
|
146
|
-
const validatorEntry = await this.state.getNodeEntry(validatorAddress);
|
|
147
|
-
const adminEntry = await this.state.getAdminEntry();
|
|
356
|
+
if (isConnected || isPending) continue;
|
|
148
357
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
358
|
+
const result = await this.#tryConnect(candidate);
|
|
359
|
+
if (!result || result === CONNECTION_STATUS.IGNORED) continue;
|
|
360
|
+
this.#logger.debug(`Validator connection to ${candidate.publicKeyHex} resulted in: ${result}`);
|
|
152
361
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
362
|
+
attempts++;
|
|
363
|
+
|
|
364
|
+
if (attempts < slots) {
|
|
365
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
156
366
|
}
|
|
157
367
|
}
|
|
368
|
+
}
|
|
158
369
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Scans Autobase writer entries and builds the active validator list.
|
|
372
|
+
*
|
|
373
|
+
* Rules:
|
|
374
|
+
* - Skips invalid/malformed entries.
|
|
375
|
+
* - Skips non-admin indexers.
|
|
376
|
+
* - Deduplicates nodes by mapping their latest state to a unique address.
|
|
377
|
+
* - Removes stale connections for nodes flagged with 'isRemoved'.
|
|
378
|
+
* - Includes only valid and active writer nodes.
|
|
379
|
+
* @param {Object} adminEntry - The current admin node entry.
|
|
380
|
+
* @returns {Promise<Array<Object>>} A promise resolving to the deduplicated array of active writers.
|
|
381
|
+
*/
|
|
382
|
+
async #scanAutobaseWriters(adminEntry) {
|
|
383
|
+
// Prevents memory leak in case of a very long-running node
|
|
384
|
+
if (this.#keyDecodeCache.size > MAX_KEY_DECODE_CACHE_SIZE) {
|
|
385
|
+
this.#keyDecodeCache.clear();
|
|
171
386
|
}
|
|
172
387
|
|
|
173
|
-
|
|
174
|
-
|
|
388
|
+
// Map is used for deduplication, ensuring that each node physical address
|
|
389
|
+
// occupies only a single slot despite the append-only nature of Autobase
|
|
390
|
+
const activeMap = new Map();
|
|
175
391
|
|
|
176
|
-
|
|
177
|
-
|
|
392
|
+
try {
|
|
393
|
+
for await (const { key, value } of this.#state.base.system.list()) {
|
|
394
|
+
if (!this.#shouldRun()) break;
|
|
395
|
+
|
|
396
|
+
if (!key || b4a.byteLength(key) !== WRITER_BYTE_LENGTH) continue;
|
|
397
|
+
if (!value) continue;
|
|
398
|
+
|
|
399
|
+
const writerKeyHex = b4a.toString(key, "hex");
|
|
400
|
+
let decodedIdentity = this.#keyDecodeCache.get(writerKeyHex);
|
|
401
|
+
|
|
402
|
+
if (decodedIdentity === undefined) {
|
|
403
|
+
const addressBuffer = await this.#state.getRegisteredWriterKey(writerKeyHex);
|
|
404
|
+
|
|
405
|
+
if (!addressBuffer || b4a.byteLength(addressBuffer) !== this.#config.addressLength) {
|
|
406
|
+
this.#keyDecodeCache.set(writerKeyHex, null);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const addr = bufferToAddress(addressBuffer, this.#config.addressPrefix);
|
|
411
|
+
const publicKey = tracCryptoApi.address.decode(addr);
|
|
412
|
+
|
|
413
|
+
if (!publicKey) {
|
|
414
|
+
this.#keyDecodeCache.set(writerKeyHex, null);
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const publicKeyHex = b4a.toString(publicKey, "hex");
|
|
419
|
+
decodedIdentity = { addr, publicKey, publicKeyHex };
|
|
420
|
+
this.#keyDecodeCache.set(writerKeyHex, decodedIdentity);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (decodedIdentity === null) continue;
|
|
424
|
+
|
|
425
|
+
const { addr, publicKey, publicKeyHex } = decodedIdentity;
|
|
426
|
+
|
|
427
|
+
if (value.isIndexer && addr !== adminEntry?.address) continue;
|
|
428
|
+
|
|
429
|
+
// Handles soft-delete: drops stale connections and removes the node from the pool
|
|
430
|
+
if (value.isRemoved) {
|
|
431
|
+
this.#enforceNoStaleConnections(publicKey);
|
|
432
|
+
activeMap.delete(addr);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Registers or overwrites to maintain a single source of truth per machine
|
|
437
|
+
activeMap.set(addr, { key, address: addr, publicKey, publicKeyHex });
|
|
438
|
+
}
|
|
439
|
+
} catch (err) {
|
|
440
|
+
this.#logger.error(`Autobase writer scan error: ${err.message}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return Array.from(activeMap.values());
|
|
178
444
|
}
|
|
179
445
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
446
|
+
/**
|
|
447
|
+
* Attempts to establish a connection with a validator candidate.
|
|
448
|
+
*
|
|
449
|
+
* Assumes the candidate was pre-filtered and validated during scan/selection.
|
|
450
|
+
*
|
|
451
|
+
* Behavior:
|
|
452
|
+
* - Delegates connection attempt to the network layer.
|
|
453
|
+
* @param {Object} candidate - The target validator candidate object.
|
|
454
|
+
* @returns {Promise<string>} The connection status string returned by the network layer.
|
|
455
|
+
*/
|
|
456
|
+
async #tryConnect(candidate) {
|
|
457
|
+
try {
|
|
458
|
+
return await this.#network.tryConnect(candidate.publicKeyHex, "validator");
|
|
459
|
+
} catch (err) {
|
|
460
|
+
this.#logger.error(`Validator connection attempt failed: ${err.message}`);
|
|
461
|
+
}
|
|
183
462
|
}
|
|
184
463
|
}
|
|
185
464
|
|
|
186
|
-
export default ValidatorObserverService;
|
|
465
|
+
export default ValidatorObserverService;
|