trac-msb 0.2.18 → 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 +25 -8
- 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 +138 -10
- package/src/config/env.js +36 -18
- package/src/core/network/Network.js +52 -24
- package/src/core/network/protocols/ProtocolInterface.js +5 -5
- package/src/core/network/protocols/connectionPolicies.js +86 -0
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +1 -1
- package/src/core/network/protocols/legacy/handlers/BaseStateOperationHandler.js +2 -2
- package/src/core/network/protocols/legacy/handlers/LegacyGetRequestHandler.js +3 -3
- package/src/core/network/protocols/legacy/handlers/LegacyRoleOperationHandler.js +1 -1
- package/src/core/network/protocols/legacy/handlers/LegacySubnetworkOperationHandler.js +1 -1
- package/src/core/network/protocols/legacy/handlers/LegacyTransferOperationHandler.js +1 -1
- package/src/core/network/protocols/legacy/validators/base/BaseResponse.js +7 -10
- package/src/core/network/protocols/shared/validators/PartialBootstrapDeploymentValidator.js +2 -2
- package/src/core/network/protocols/shared/validators/PartialOperationValidator.js +23 -23
- package/src/core/network/protocols/shared/validators/PartialRoleAccessValidator.js +14 -14
- package/src/core/network/protocols/shared/validators/PartialTransactionValidator.js +5 -5
- package/src/core/network/protocols/shared/validators/PartialTransferValidator.js +12 -11
- package/src/core/network/protocols/v1/V1ProtocolError.js +5 -69
- package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +3 -2
- package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +130 -247
- package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +6 -5
- 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 +32 -31
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +5 -3
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +34 -50
- package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +1 -1
- package/src/core/network/services/ConnectionManager.js +11 -10
- package/src/core/network/services/MessageOrchestrator.js +53 -17
- package/src/core/network/services/PendingRequestService.js +22 -19
- package/src/core/network/services/TransactionCommitService.js +36 -36
- package/src/core/network/services/TransactionPoolService.js +39 -39
- package/src/core/network/services/TransactionRateLimiterService.js +8 -4
- package/src/core/network/services/ValidatorObserverService.js +397 -110
- package/src/core/state/State.js +190 -138
- 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 +316 -275
- package/src/messages/network/v1/NetworkMessageBuilder.js +12 -21
- 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/buffer.js +1 -1
- package/src/utils/check.js +12 -13
- package/src/utils/constants.js +7 -0
- package/src/utils/errors.js +20 -0
- package/src/utils/fileUtils.js +12 -3
- package/src/utils/helpers.js +2 -2
- package/src/utils/migrationUtils.js +3 -3
- package/.dockerignore +0 -16
- 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/v1/enums/message_type.proto +0 -16
- package/proto/network/v1/enums/result_code.proto +0 -84
- package/proto/network/v1/messages/broadcast_transaction_request.proto +0 -9
- package/proto/network/v1/messages/broadcast_transaction_response.proto +0 -13
- package/proto/network/v1/messages/liveness_request.proto +0 -8
- package/proto/network/v1/messages/liveness_response.proto +0 -11
- package/proto/network/v1/network_message.proto +0 -22
- package/rpc/rpc_services.js +0 -181
- package/scripts/generate-protobufs.js +0 -67
- package/src/core/network/identity/NetworkWalletFactory.js +0 -77
- package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +0 -27
- package/src/utils/cli.js +0 -130
- package/src/utils/cliCommands.js +0 -280
- package/tests/acceptance/v1/account/account.test.mjs +0 -129
- 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 -133
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +0 -241
- 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 -59
- 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 -436
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +0 -347
- 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/LegacyNetworkMessageRouter.test.js +0 -54
- package/tests/unit/network/NetworkWalletFactory.test.js +0 -156
- package/tests/unit/network/ProtocolSession.test.js +0 -127
- package/tests/unit/network/networkModule.test.js +0 -13
- package/tests/unit/network/services/ConnectionManager.test.js +0 -450
- package/tests/unit/network/services/MessageOrchestrator.test.js +0 -445
- package/tests/unit/network/services/PendingRequestService.test.js +0 -431
- package/tests/unit/network/services/TransactionCommitService.test.js +0 -246
- package/tests/unit/network/services/TransactionPoolService.test.js +0 -489
- package/tests/unit/network/services/TransactionRateLimiterService.test.js +0 -139
- package/tests/unit/network/services/ValidatorHealthCheckService.test.js +0 -115
- package/tests/unit/network/services/services.test.js +0 -17
- package/tests/unit/network/utils/v1TestUtils.js +0 -153
- package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +0 -151
- package/tests/unit/network/v1/V1BaseOperation.test.js +0 -356
- package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +0 -129
- package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +0 -53
- package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +0 -512
- package/tests/unit/network/v1/V1LivenessRequest.test.js +0 -32
- package/tests/unit/network/v1/V1LivenessResponse.test.js +0 -45
- package/tests/unit/network/v1/V1ResultCode.test.js +0 -84
- package/tests/unit/network/v1/V1ValidationSchema.test.js +0 -13
- package/tests/unit/network/v1/connectionPolicies.test.js +0 -49
- package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +0 -284
- package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +0 -794
- package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +0 -193
- package/tests/unit/network/v1/v1.handlers.test.js +0 -15
- package/tests/unit/network/v1/v1.test.js +0 -19
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +0 -119
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +0 -136
- package/tests/unit/network/v1/v1ValidationSchema/common.test.js +0 -308
- package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +0 -90
- package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +0 -133
- 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/deepEqualApplyPayload/deepEqualApplyPayload.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 -303
- 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 -21
- package/whitelist/.gitkeep +0 -0
|
@@ -1,178 +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
|
-
import
|
|
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";
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
const VALIDATOR_CANDIDATES_PER_CYCLE = 10
|
|
11
|
-
const
|
|
8
|
+
// Internal constants
|
|
9
|
+
const VALIDATOR_CANDIDATES_PER_CYCLE = 10;
|
|
10
|
+
const MAX_KEY_DECODE_CACHE_SIZE = 10_000;
|
|
12
11
|
|
|
13
12
|
class ValidatorObserverService {
|
|
14
|
-
#config;
|
|
15
|
-
#state;
|
|
16
13
|
#network;
|
|
17
|
-
#
|
|
14
|
+
#state;
|
|
18
15
|
#address;
|
|
19
|
-
#
|
|
16
|
+
#config;
|
|
17
|
+
#scheduler;
|
|
20
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;
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
|
-
* @param {
|
|
24
|
-
* @param {
|
|
25
|
-
* @param {string} address
|
|
26
|
-
* @param {
|
|
27
|
-
|
|
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
|
+
*/
|
|
28
32
|
constructor(network, state, address, config) {
|
|
29
|
-
this.#config = config
|
|
30
33
|
this.#network = network;
|
|
31
34
|
this.#state = state;
|
|
32
35
|
this.#address = address;
|
|
33
|
-
this.#
|
|
36
|
+
this.#config = config;
|
|
34
37
|
this.#logger = new Logger(config);
|
|
35
|
-
this
|
|
36
|
-
this
|
|
37
|
-
this
|
|
38
|
-
this
|
|
38
|
+
this.#hasBootstrapped = false;
|
|
39
|
+
this.#bootstrapStartedAt = 0;
|
|
40
|
+
this.#adminCache = { entry: null, lastUpdated: 0 };
|
|
41
|
+
this.#writersCache = { list: [], lastUpdated: 0 };
|
|
42
|
+
this.#isInterrupted = false;
|
|
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,
|
|
117
|
+
}
|
|
39
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");
|
|
40
140
|
|
|
41
|
-
|
|
42
|
-
return
|
|
141
|
+
const isAdmin = adminEntry && this.#address === adminEntry.address;
|
|
142
|
+
if (isAdmin) return;
|
|
143
|
+
|
|
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
|
+
}
|
|
43
149
|
}
|
|
44
150
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
*/
|
|
48
211
|
async start() {
|
|
49
212
|
if (!this.#shouldRun()) {
|
|
50
213
|
this.#logger.info('ValidatorObserverService can not start. Disabled by configuration.');
|
|
51
214
|
return;
|
|
52
215
|
}
|
|
216
|
+
|
|
53
217
|
if (this.#scheduler && this.#scheduler.isRunning) {
|
|
54
218
|
this.#logger.info('ValidatorObserverService is already started');
|
|
55
219
|
return;
|
|
56
220
|
}
|
|
57
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;
|
|
58
226
|
this.#isInterrupted = false;
|
|
59
|
-
this.#scheduler = new Scheduler(next => this.#worker(next), POLL_INTERVAL);
|
|
60
227
|
this.#scheduler.start();
|
|
228
|
+
this.#logger.debug("ValidatorObserverService started");
|
|
61
229
|
}
|
|
62
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
|
+
*/
|
|
63
236
|
async stopValidatorObserver(waitForCurrent = true) {
|
|
64
237
|
if (!this.#scheduler) return;
|
|
238
|
+
|
|
65
239
|
this.#isInterrupted = true;
|
|
240
|
+
|
|
66
241
|
await this.#scheduler.stop(waitForCurrent);
|
|
67
242
|
this.#scheduler = null;
|
|
68
|
-
this.#logger.
|
|
243
|
+
this.#logger.debug("ValidatorObserverService stopped");
|
|
69
244
|
}
|
|
70
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
|
+
*/
|
|
71
257
|
async #worker(next) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const length = await this.#lengthEntry()
|
|
75
|
-
|
|
76
|
-
const promises = [];
|
|
77
|
-
for (let i = 0; i < VALIDATOR_CANDIDATES_PER_CYCLE; i++) {
|
|
78
|
-
promises.push(this.#findValidator(this.#address, length + 1));
|
|
79
|
-
await sleep(DELAY_INTERVAL); // Low key dangerous as the network progresses
|
|
80
|
-
}
|
|
81
|
-
await Promise.all(promises);
|
|
258
|
+
const interval = this.#getPollInterval();
|
|
259
|
+
if (!this.#shouldRun()) return next(interval);
|
|
82
260
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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);
|
|
93
276
|
}
|
|
277
|
+
|
|
278
|
+
} catch (err) {
|
|
279
|
+
this.#logger.error(`ValidatorObserver worker error: ${err.message}`);
|
|
94
280
|
}
|
|
95
|
-
|
|
281
|
+
|
|
282
|
+
next(interval);
|
|
96
283
|
}
|
|
97
284
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|
|
104
293
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
attempts++;
|
|
110
|
-
}
|
|
294
|
+
const entry = await this.#state.getAdminEntry();
|
|
295
|
+
this.#adminCache = { entry, lastUpdated: now };
|
|
296
|
+
return entry;
|
|
297
|
+
}
|
|
111
298
|
|
|
112
|
-
|
|
113
|
-
|
|
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;
|
|
315
|
+
|
|
316
|
+
if (!this.#hasBootstrapped && manager.maxConnectionsReached()) {
|
|
317
|
+
this.#hasBootstrapped = true;
|
|
318
|
+
this.#logger.debug("Bootstrap complete (pool full)");
|
|
114
319
|
}
|
|
115
|
-
|
|
116
|
-
|
|
320
|
+
|
|
321
|
+
if (!this.#hasBootstrapped && now - this.#bootstrapStartedAt > this.#config.bootstrapTimeout) {
|
|
322
|
+
this.#hasBootstrapped = true;
|
|
323
|
+
this.#logger.debug("Bootstrap complete (timeout)");
|
|
117
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 };
|
|
118
334
|
|
|
119
|
-
|
|
335
|
+
return list;
|
|
336
|
+
}
|
|
120
337
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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;
|
|
125
346
|
|
|
126
|
-
|
|
127
|
-
this.#network.tryConnect(validatorPubKeyHex, 'validator');
|
|
128
|
-
}
|
|
129
|
-
};
|
|
347
|
+
let attempts = 0;
|
|
130
348
|
|
|
131
|
-
|
|
132
|
-
|
|
349
|
+
for (const candidate of candidates) {
|
|
350
|
+
if (!this.#shouldRun()) break;
|
|
351
|
+
if (attempts >= slots) break;
|
|
133
352
|
|
|
134
|
-
|
|
135
|
-
|
|
353
|
+
const isConnected = this.#network.validatorConnectionManager.connected(candidate.publicKey);
|
|
354
|
+
const isPending = this.#network.isConnectionPending(candidate.publicKeyHex);
|
|
136
355
|
|
|
137
|
-
|
|
138
|
-
const validatorEntry = await this.state.getNodeEntry(validatorAddress);
|
|
139
|
-
const adminEntry = await this.state.getAdminEntry();
|
|
356
|
+
if (isConnected || isPending) continue;
|
|
140
357
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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}`);
|
|
144
361
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
362
|
+
attempts++;
|
|
363
|
+
|
|
364
|
+
if (attempts < slots) {
|
|
365
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
148
366
|
}
|
|
149
367
|
}
|
|
368
|
+
}
|
|
150
369
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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();
|
|
163
386
|
}
|
|
164
387
|
|
|
165
|
-
|
|
166
|
-
|
|
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();
|
|
167
391
|
|
|
168
|
-
|
|
169
|
-
|
|
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());
|
|
170
444
|
}
|
|
171
445
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
+
}
|
|
175
462
|
}
|
|
176
463
|
}
|
|
177
464
|
|
|
178
|
-
export default ValidatorObserverService;
|
|
465
|
+
export default ValidatorObserverService;
|