@audius/sdk 0.0.0
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/.eslintrc +38 -0
- package/.prettierrc.js +1 -0
- package/.python-version +1 -0
- package/Dockerfile +15 -0
- package/README.md +3 -0
- package/babel.config.js +3 -0
- package/data-contracts/ABIs/AdminUpgradeabilityProxy.json +132 -0
- package/data-contracts/ABIs/BaseAdminUpgradeabilityProxy.json +113 -0
- package/data-contracts/ABIs/BaseUpgradeabilityProxy.json +22 -0
- package/data-contracts/ABIs/DiscoveryProviderFactory.json +189 -0
- package/data-contracts/ABIs/DiscoveryProviderFactoryInterface.json +61 -0
- package/data-contracts/ABIs/DiscoveryProviderStorage.json +205 -0
- package/data-contracts/ABIs/DiscoveryProviderStorageInterface.json +65 -0
- package/data-contracts/ABIs/ECDSA.json +4 -0
- package/data-contracts/ABIs/IPLDBlacklistFactory.json +168 -0
- package/data-contracts/ABIs/Initializable.json +4 -0
- package/data-contracts/ABIs/Migrations.json +67 -0
- package/data-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
- package/data-contracts/ABIs/Ownable.json +79 -0
- package/data-contracts/ABIs/PlaylistFactory.json +669 -0
- package/data-contracts/ABIs/PlaylistFactoryInterface.json +42 -0
- package/data-contracts/ABIs/PlaylistStorage.json +250 -0
- package/data-contracts/ABIs/PlaylistStorageInterface.json +129 -0
- package/data-contracts/ABIs/Proxy.json +10 -0
- package/data-contracts/ABIs/Registry.json +240 -0
- package/data-contracts/ABIs/RegistryContract.json +102 -0
- package/data-contracts/ABIs/RegistryContractInterface.json +28 -0
- package/data-contracts/ABIs/RegistryInterface.json +66 -0
- package/data-contracts/ABIs/SigningLogic.json +43 -0
- package/data-contracts/ABIs/SigningLogicInitializable.json +46 -0
- package/data-contracts/ABIs/SocialFeatureFactory.json +460 -0
- package/data-contracts/ABIs/SocialFeatureStorage.json +225 -0
- package/data-contracts/ABIs/SocialFeatureStorageInterface.json +123 -0
- package/data-contracts/ABIs/TestContract.json +135 -0
- package/data-contracts/ABIs/TestContractInterface.json +19 -0
- package/data-contracts/ABIs/TestContractWithStorage.json +165 -0
- package/data-contracts/ABIs/TestContractWithStorageInterface.json +24 -0
- package/data-contracts/ABIs/TestStorage.json +144 -0
- package/data-contracts/ABIs/TestStorageInterface.json +42 -0
- package/data-contracts/ABIs/TestUserReplicaSetManager.json +432 -0
- package/data-contracts/ABIs/TrackFactory.json +391 -0
- package/data-contracts/ABIs/TrackFactoryInterface.json +73 -0
- package/data-contracts/ABIs/TrackStorage.json +223 -0
- package/data-contracts/ABIs/TrackStorageInterface.json +121 -0
- package/data-contracts/ABIs/UpgradeabilityProxy.json +37 -0
- package/data-contracts/ABIs/UserFactory.json +657 -0
- package/data-contracts/ABIs/UserFactoryInterface.json +65 -0
- package/data-contracts/ABIs/UserLibraryFactory.json +334 -0
- package/data-contracts/ABIs/UserReplicaSetManager.json +418 -0
- package/data-contracts/ABIs/UserStorage.json +233 -0
- package/data-contracts/ABIs/UserStorageInterface.json +93 -0
- package/data-contracts/signatureSchemas.ts +1236 -0
- package/dist/core.d.ts +446 -0
- package/dist/core.js +769 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +689 -0
- package/dist/index.js +72850 -0
- package/dist/index.js.map +1 -0
- package/eth-contracts/ABIs/Address.json +4 -0
- package/eth-contracts/ABIs/AudiusAdminUpgradeabilityProxy.json +105 -0
- package/eth-contracts/ABIs/AudiusClaimDistributor.json +4968 -0
- package/eth-contracts/ABIs/AudiusToken.json +724 -0
- package/eth-contracts/ABIs/BaseUpgradeabilityProxy.json +23 -0
- package/eth-contracts/ABIs/Checkpointing.json +4 -0
- package/eth-contracts/ABIs/ClaimsManager.json +539 -0
- package/eth-contracts/ABIs/Context.json +11 -0
- package/eth-contracts/ABIs/DelegateManager.json +989 -0
- package/eth-contracts/ABIs/DelegateManagerV2.json +1049 -0
- package/eth-contracts/ABIs/DelegateManagerV2Bad.json +1049 -0
- package/eth-contracts/ABIs/ERC20.json +252 -0
- package/eth-contracts/ABIs/ERC20Burnable.json +287 -0
- package/eth-contracts/ABIs/ERC20Detailed.json +270 -0
- package/eth-contracts/ABIs/ERC20Mintable.json +364 -0
- package/eth-contracts/ABIs/ERC20Pausable.json +397 -0
- package/eth-contracts/ABIs/EthRewardsManager.json +174 -0
- package/eth-contracts/ABIs/Governance.json +938 -0
- package/eth-contracts/ABIs/GovernanceUpgraded.json +953 -0
- package/eth-contracts/ABIs/GovernanceV2.json +938 -0
- package/eth-contracts/ABIs/IERC20.json +200 -0
- package/eth-contracts/ABIs/Initializable.json +4 -0
- package/eth-contracts/ABIs/InitializableV2.json +14 -0
- package/eth-contracts/ABIs/Migrations.json +71 -0
- package/eth-contracts/ABIs/MinterRole.json +91 -0
- package/eth-contracts/ABIs/MockAccount.json +62 -0
- package/eth-contracts/ABIs/MockDelegateManager.json +55 -0
- package/eth-contracts/ABIs/MockStakingCaller.json +259 -0
- package/eth-contracts/ABIs/MockWormhole.json +106 -0
- package/eth-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
- package/eth-contracts/ABIs/Ownable.json +93 -0
- package/eth-contracts/ABIs/Pausable.json +150 -0
- package/eth-contracts/ABIs/PauserRole.json +91 -0
- package/eth-contracts/ABIs/Proxy.json +10 -0
- package/eth-contracts/ABIs/Registry.json +288 -0
- package/eth-contracts/ABIs/Roles.json +4 -0
- package/eth-contracts/ABIs/SafeERC20.json +4 -0
- package/eth-contracts/ABIs/SafeMath.json +4 -0
- package/eth-contracts/ABIs/ServiceProviderFactory.json +1153 -0
- package/eth-contracts/ABIs/ServiceTypeManager.json +337 -0
- package/eth-contracts/ABIs/Staking.json +555 -0
- package/eth-contracts/ABIs/StakingUpgraded.json +570 -0
- package/eth-contracts/ABIs/TestContract.json +44 -0
- package/eth-contracts/ABIs/TrustedNotifierManager.json +265 -0
- package/eth-contracts/ABIs/Uint256Helpers.json +4 -0
- package/eth-contracts/ABIs/UpgradeabilityProxy.json +40 -0
- package/eth-contracts/ABIs/Wormhole.json +45 -0
- package/eth-contracts/ABIs/WormholeClient.json +155 -0
- package/examples/file.mp3 +0 -0
- package/examples/initAudiusLibs.js +86 -0
- package/examples/initializeVersions.js +95 -0
- package/examples/pic.jpg +0 -0
- package/initScripts/configureLocalDiscProv.js +167 -0
- package/initScripts/helpers/claim.js +43 -0
- package/initScripts/helpers/distributeTokens.js +24 -0
- package/initScripts/helpers/spRegistration.js +138 -0
- package/initScripts/helpers/utils.js +34 -0
- package/initScripts/helpers/version.js +93 -0
- package/initScripts/local.js +617 -0
- package/initScripts/mainnet.js +131 -0
- package/initScripts/manageProdRelayerWallets.js +191 -0
- package/package.json +125 -0
- package/rollup.config.js +164 -0
- package/scripts/AudiusClaimDistributor.json +4968 -0
- package/scripts/Wormhole.json +155 -0
- package/scripts/addCIDToIpldBlacklist.js +124 -0
- package/scripts/circleci-test.sh +53 -0
- package/scripts/communityRewards/transferCommunityRewardsToSolana.js +222 -0
- package/scripts/ipfs.sh +58 -0
- package/scripts/migrate_contracts.sh +25 -0
- package/scripts/reset.sh +65 -0
- package/scripts/test.sh +77 -0
- package/src/api/account.js +670 -0
- package/src/api/base.js +122 -0
- package/src/api/file.js +168 -0
- package/src/api/playlist.js +328 -0
- package/src/api/rewards.d.ts +4 -0
- package/src/api/rewards.js +682 -0
- package/src/api/serviceProvider.js +154 -0
- package/src/api/track.js +604 -0
- package/src/api/user.js +888 -0
- package/src/api/user.test.js +172 -0
- package/src/constants.ts +7 -0
- package/src/core.ts +3 -0
- package/src/index.js +6 -0
- package/src/libs.d.ts +3 -0
- package/src/libs.js +619 -0
- package/src/sanityChecks/addSecondaries.js +40 -0
- package/src/sanityChecks/assignReplicaSetIfNecessary.js +10 -0
- package/src/sanityChecks/index.d.ts +9 -0
- package/src/sanityChecks/index.js +31 -0
- package/src/sanityChecks/isCreator.js +73 -0
- package/src/sanityChecks/needsRecoveryEmail.js +20 -0
- package/src/sanityChecks/rolloverNodes.js +74 -0
- package/src/sanityChecks/sanitizeNodes.js +24 -0
- package/src/sanityChecks/syncNodes.js +28 -0
- package/src/sdk/constants.ts +10 -0
- package/src/sdk/index.ts +1 -0
- package/src/sdk/oauth/Oauth.ts +265 -0
- package/src/sdk/oauth/index.ts +1 -0
- package/src/sdk/sdk.ts +102 -0
- package/src/service-selection/ServiceSelection.test.ts +320 -0
- package/src/service-selection/ServiceSelection.ts +460 -0
- package/src/service-selection/constants.ts +14 -0
- package/src/service-selection/index.ts +1 -0
- package/src/services/ABIDecoder/AudiusABIDecoder.ts +71 -0
- package/src/services/ABIDecoder/index.ts +1 -0
- package/src/services/comstock/Comstock.ts +39 -0
- package/src/services/comstock/index.ts +1 -0
- package/src/services/contracts/ContractClient.ts +227 -0
- package/src/services/contracts/GovernedContractClient.ts +53 -0
- package/src/services/contracts/ProviderSelection.ts +42 -0
- package/src/services/creatorNode/CreatorNode.ts +1065 -0
- package/src/services/creatorNode/CreatorNodeSelection.test.ts +997 -0
- package/src/services/creatorNode/CreatorNodeSelection.ts +488 -0
- package/src/services/creatorNode/constants.ts +10 -0
- package/src/services/creatorNode/index.ts +2 -0
- package/src/services/dataContracts/AudiusContracts.ts +234 -0
- package/src/services/dataContracts/IPLDBlacklistFactoryClient.ts +73 -0
- package/src/services/dataContracts/PlaylistFactoryClient.ts +370 -0
- package/src/services/dataContracts/RegistryClient.ts +95 -0
- package/src/services/dataContracts/SocialFeatureFactoryClient.ts +196 -0
- package/src/services/dataContracts/TrackFactoryClient.ts +131 -0
- package/src/services/dataContracts/UserFactoryClient.ts +351 -0
- package/src/services/dataContracts/UserLibraryFactoryClient.ts +115 -0
- package/src/services/dataContracts/UserReplicaSetManagerClient.ts +206 -0
- package/src/services/dataContracts/index.ts +1 -0
- package/src/services/discoveryProvider/DiscoveryProvider.ts +1168 -0
- package/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +536 -0
- package/src/services/discoveryProvider/DiscoveryProviderSelection.ts +383 -0
- package/src/services/discoveryProvider/constants.ts +13 -0
- package/src/services/discoveryProvider/index.ts +1 -0
- package/src/services/discoveryProvider/requests.ts +629 -0
- package/src/services/ethContracts/AudiusTokenClient.ts +163 -0
- package/src/services/ethContracts/ClaimDistributionClient.ts +45 -0
- package/src/services/ethContracts/ClaimsManagerClient.ts +102 -0
- package/src/services/ethContracts/DelegateManagerClient.ts +480 -0
- package/src/services/ethContracts/EthContracts.ts +359 -0
- package/src/services/ethContracts/EthRewardsManagerClient.ts +33 -0
- package/src/services/ethContracts/GovernanceClient.ts +451 -0
- package/src/services/ethContracts/RegistryClient.ts +33 -0
- package/src/services/ethContracts/ServiceProviderFactoryClient.ts +691 -0
- package/src/services/ethContracts/ServiceTypeManagerClient.ts +112 -0
- package/src/services/ethContracts/StakingProxyClient.ts +97 -0
- package/src/services/ethContracts/TrustedNotifierManagerClient.ts +101 -0
- package/src/services/ethContracts/WormholeClient.ts +97 -0
- package/src/services/ethContracts/index.ts +1 -0
- package/src/services/ethWeb3Manager/EthWeb3Manager.ts +239 -0
- package/src/services/ethWeb3Manager/index.ts +1 -0
- package/src/services/hedgehog/Hedgehog.ts +96 -0
- package/src/services/hedgehog/index.ts +1 -0
- package/src/services/identity/IdentityService.ts +551 -0
- package/src/services/identity/index.ts +1 -0
- package/src/services/identity/requests.ts +65 -0
- package/src/services/schemaValidator/SchemaValidator.ts +105 -0
- package/src/services/schemaValidator/index.ts +1 -0
- package/src/services/schemaValidator/schemas/trackSchema.json +267 -0
- package/src/services/schemaValidator/schemas/userSchema.json +230 -0
- package/src/services/solanaAudiusData/errors.ts +20 -0
- package/src/services/solanaAudiusData/index.ts +1189 -0
- package/src/services/solanaWeb3Manager/errors.js +101 -0
- package/src/services/solanaWeb3Manager/index.d.ts +46 -0
- package/src/services/solanaWeb3Manager/index.js +655 -0
- package/src/services/solanaWeb3Manager/padBNToUint8Array.ts +7 -0
- package/src/services/solanaWeb3Manager/rewards.js +941 -0
- package/src/services/solanaWeb3Manager/rewardsAttester.ts +1093 -0
- package/src/services/solanaWeb3Manager/tokenAccount.js +149 -0
- package/src/services/solanaWeb3Manager/transactionHandler.js +345 -0
- package/src/services/solanaWeb3Manager/transfer.js +272 -0
- package/src/services/solanaWeb3Manager/userBank.js +160 -0
- package/src/services/solanaWeb3Manager/utils.d.ts +31 -0
- package/src/services/solanaWeb3Manager/utils.js +163 -0
- package/src/services/solanaWeb3Manager/wAudio.js +28 -0
- package/src/services/solanaWeb3Manager/wAudio.test.js +30 -0
- package/src/services/web3Manager/Web3Config.ts +14 -0
- package/src/services/web3Manager/Web3Manager.ts +360 -0
- package/src/services/web3Manager/XMLHttpRequest.ts +11 -0
- package/src/services/web3Manager/index.ts +2 -0
- package/src/services/wormhole/index.js +424 -0
- package/src/types.ts +8 -0
- package/src/userStateManager.ts +53 -0
- package/src/utils/apiSigning.ts +51 -0
- package/src/utils/captcha.ts +97 -0
- package/src/utils/estimateGas.ts +64 -0
- package/src/utils/fileHasher.ts +278 -0
- package/src/utils/importContractABI.d.ts +9 -0
- package/src/utils/importContractABI.js +19 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/multiProvider.ts +72 -0
- package/src/utils/network.test.ts +127 -0
- package/src/utils/network.ts +308 -0
- package/src/utils/promiseFight.test.ts +87 -0
- package/src/utils/promiseFight.ts +36 -0
- package/src/utils/signatures.ts +139 -0
- package/src/utils/types.ts +34 -0
- package/src/utils/utils.test.ts +36 -0
- package/src/utils/utils.ts +235 -0
- package/src/utils/uuid.ts +14 -0
- package/src/web3.d.ts +9 -0
- package/src/web3.js +8 -0
- package/tests/assets/static_image.png +0 -0
- package/tests/assets/static_text.txt +1 -0
- package/tests/audiusTokenClientTest.js +37 -0
- package/tests/creatorNodeTest.js +19 -0
- package/tests/fileHasherTest.js +125 -0
- package/tests/governanceTest.js +382 -0
- package/tests/helpers.js +105 -0
- package/tests/index.js +14 -0
- package/tests/playlistClientTest.js +157 -0
- package/tests/providerSelectionTest.js +241 -0
- package/tests/registryClientTest.js +19 -0
- package/tests/rewardsAttesterTest.js +373 -0
- package/tests/serviceTypeManagerClientTest.js +33 -0
- package/tests/socialFeatureClientTest.js +79 -0
- package/tests/stakingTest.js +302 -0
- package/tests/trackClientTest.js +86 -0
- package/tests/userClientTest.js +121 -0
- package/tsconfig.json +10 -0
- package/types/@audius-hedgehog/index.d.ts +39 -0
- package/types/abi-decoder/index.d.ts +41 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import type { AxiosResponse } from 'axios'
|
|
2
|
+
import _ from 'lodash'
|
|
3
|
+
import type { EthContracts } from '../ethContracts'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ServiceSelection,
|
|
7
|
+
ServiceSelectionConfig
|
|
8
|
+
} from '../../service-selection'
|
|
9
|
+
import {
|
|
10
|
+
timeRequests,
|
|
11
|
+
sortServiceTimings,
|
|
12
|
+
Service,
|
|
13
|
+
ServiceName,
|
|
14
|
+
Timing,
|
|
15
|
+
Logger
|
|
16
|
+
} from '../../utils'
|
|
17
|
+
import { CREATOR_NODE_SERVICE_NAME, DECISION_TREE_STATE } from './constants'
|
|
18
|
+
|
|
19
|
+
type Timeout = number | null
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* In memory dictionary used to query spID from endpoint
|
|
23
|
+
* Eliminates duplicate web3 calls within same session
|
|
24
|
+
*/
|
|
25
|
+
const contentNodeEndpointToSpID: Record<string, number | undefined> = {}
|
|
26
|
+
|
|
27
|
+
export function getSpIDForEndpoint(endpoint: string) {
|
|
28
|
+
return contentNodeEndpointToSpID[endpoint]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function setSpIDForEndpoint(endpoint: string, spID?: number) {
|
|
32
|
+
contentNodeEndpointToSpID[endpoint] = spID
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type CreatorNode = {
|
|
36
|
+
getSyncStatus: (
|
|
37
|
+
service: Service,
|
|
38
|
+
timeout: Timeout
|
|
39
|
+
) => Promise<{ isBehind: boolean; isConfigured: boolean }>
|
|
40
|
+
passList?: Set<string>
|
|
41
|
+
blockList?: Set<string>
|
|
42
|
+
monitoringCallbacks: {
|
|
43
|
+
healthCheck?: (config: Record<string, unknown>) => Promise<AxiosResponse>
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
type CreatorNodeSelectionConfig = Omit<
|
|
47
|
+
ServiceSelectionConfig,
|
|
48
|
+
'getServices'
|
|
49
|
+
> & {
|
|
50
|
+
creatorNode: CreatorNode
|
|
51
|
+
numberOfNodes: number
|
|
52
|
+
ethContracts: EthContracts
|
|
53
|
+
maxStorageUsedPercent?: number
|
|
54
|
+
timeout?: Timeout
|
|
55
|
+
equivalencyDelta?: number | null
|
|
56
|
+
preferHigherPatchForPrimary?: boolean
|
|
57
|
+
preferHigherPatchForSecondaries?: boolean
|
|
58
|
+
logger?: Logger
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface Decision {
|
|
62
|
+
stage: DECISION_TREE_STATE
|
|
63
|
+
val?: unknown
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class CreatorNodeSelection extends ServiceSelection {
|
|
67
|
+
override decisionTree: Decision[]
|
|
68
|
+
currentVersion: string | null = ''
|
|
69
|
+
ethContracts: EthContracts
|
|
70
|
+
creatorNode: CreatorNode
|
|
71
|
+
numberOfNodes: number
|
|
72
|
+
timeout: Timeout
|
|
73
|
+
equivalencyDelta: number | null
|
|
74
|
+
preferHigherPatchForPrimary: boolean
|
|
75
|
+
preferHigherPatchForSecondaries: boolean
|
|
76
|
+
healthCheckPath: string
|
|
77
|
+
backupsList: string[]
|
|
78
|
+
backupTimings: Timing[]
|
|
79
|
+
maxStorageUsedPercent: number
|
|
80
|
+
logger: Logger
|
|
81
|
+
|
|
82
|
+
constructor({
|
|
83
|
+
creatorNode,
|
|
84
|
+
numberOfNodes,
|
|
85
|
+
ethContracts,
|
|
86
|
+
whitelist,
|
|
87
|
+
blacklist,
|
|
88
|
+
logger = console,
|
|
89
|
+
maxStorageUsedPercent = 95,
|
|
90
|
+
timeout = null,
|
|
91
|
+
equivalencyDelta = null,
|
|
92
|
+
preferHigherPatchForPrimary = true,
|
|
93
|
+
preferHigherPatchForSecondaries = true
|
|
94
|
+
}: CreatorNodeSelectionConfig) {
|
|
95
|
+
super({
|
|
96
|
+
getServices: async () => {
|
|
97
|
+
this.currentVersion = await ethContracts.getCurrentVersion(
|
|
98
|
+
CREATOR_NODE_SERVICE_NAME
|
|
99
|
+
)
|
|
100
|
+
const services = await this.ethContracts.getServiceProviderList(
|
|
101
|
+
CREATOR_NODE_SERVICE_NAME
|
|
102
|
+
)
|
|
103
|
+
return services.map((e) => {
|
|
104
|
+
setSpIDForEndpoint(e.endpoint, e.spID)
|
|
105
|
+
return e.endpoint
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
// Use the content node's configured whitelist if not provided
|
|
109
|
+
whitelist: whitelist ?? creatorNode?.passList,
|
|
110
|
+
blacklist: blacklist ?? creatorNode?.blockList
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
this.creatorNode = creatorNode
|
|
114
|
+
this.numberOfNodes = numberOfNodes
|
|
115
|
+
this.ethContracts = ethContracts
|
|
116
|
+
this.timeout = timeout
|
|
117
|
+
this.equivalencyDelta = equivalencyDelta
|
|
118
|
+
this.preferHigherPatchForPrimary = preferHigherPatchForPrimary
|
|
119
|
+
this.preferHigherPatchForSecondaries = preferHigherPatchForSecondaries
|
|
120
|
+
this.logger = logger
|
|
121
|
+
|
|
122
|
+
this.healthCheckPath = 'health_check/verbose'
|
|
123
|
+
// String array of healthy Content Node endpoints
|
|
124
|
+
this.backupsList = []
|
|
125
|
+
this.backupTimings = []
|
|
126
|
+
// Max percentage (represented out of 100) allowed before determining CN is unsuitable for selection
|
|
127
|
+
this.maxStorageUsedPercent = maxStorageUsedPercent
|
|
128
|
+
// The decision tree path that was taken. Reset on each new selection.
|
|
129
|
+
this.decisionTree = []
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Selects a primary and secondary Content Nodes. Order of preference is highest version, then response time.
|
|
134
|
+
*
|
|
135
|
+
* 1. Retrieve all the Content Node services
|
|
136
|
+
* 2. Filter from/out Content Nodes based off of the whitelist and blacklist
|
|
137
|
+
* 3. Filter out unhealthy, outdated, and still syncing nodes via health and sync check
|
|
138
|
+
* 4. Sort by healthiest (highest version -> lowest version); secondary check if equal version based off of responseTime
|
|
139
|
+
* 5. Select a primary and numberOfNodes-1 number of secondaries (most likely 2) from backups
|
|
140
|
+
* @param performSyncCheck whether or not to check whether the nodes need syncs before selection
|
|
141
|
+
*/
|
|
142
|
+
override async select(performSyncCheck = true, log = true) {
|
|
143
|
+
// Reset decision tree and backups
|
|
144
|
+
this.decisionTree = []
|
|
145
|
+
this.clearBackups()
|
|
146
|
+
this.clearUnhealthy()
|
|
147
|
+
|
|
148
|
+
// Get all the Content Node endpoints on chain and filter
|
|
149
|
+
let services = await this.getServices()
|
|
150
|
+
this.decisionTree.push({
|
|
151
|
+
stage: DECISION_TREE_STATE.GET_ALL_SERVICES,
|
|
152
|
+
val: services
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
if (this.whitelist) {
|
|
156
|
+
services = this.filterToWhitelist(services)
|
|
157
|
+
}
|
|
158
|
+
this.decisionTree.push({
|
|
159
|
+
stage: DECISION_TREE_STATE.FILTER_TO_WHITELIST,
|
|
160
|
+
val: services
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
if (this.blacklist) {
|
|
164
|
+
services = this.filterFromBlacklist(services)
|
|
165
|
+
}
|
|
166
|
+
this.decisionTree.push({
|
|
167
|
+
stage: DECISION_TREE_STATE.FILTER_FROM_BLACKLIST,
|
|
168
|
+
val: services
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// TODO: add a sample size selection round to not send requests to all available nodes
|
|
172
|
+
|
|
173
|
+
if (performSyncCheck) {
|
|
174
|
+
services = await this._performSyncChecks(services, this.timeout)
|
|
175
|
+
this.decisionTree.push({
|
|
176
|
+
stage: DECISION_TREE_STATE.FILTER_OUT_SYNC_IN_PROGRESS,
|
|
177
|
+
val: services
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
const {
|
|
181
|
+
healthyServicesList,
|
|
182
|
+
healthyServicesMap: servicesMap,
|
|
183
|
+
healthyServiceTimings
|
|
184
|
+
} = await this._performHealthChecks(services)
|
|
185
|
+
services = healthyServicesList
|
|
186
|
+
|
|
187
|
+
let primary: string
|
|
188
|
+
if (this.preferHigherPatchForPrimary) {
|
|
189
|
+
const serviceTimingsSortedByVersion = sortServiceTimings({
|
|
190
|
+
serviceTimings: healthyServiceTimings,
|
|
191
|
+
currentVersion: this.currentVersion,
|
|
192
|
+
sortByVersion: true,
|
|
193
|
+
equivalencyDelta: this.equivalencyDelta
|
|
194
|
+
})
|
|
195
|
+
const servicesSortedByVersion = serviceTimingsSortedByVersion.map(
|
|
196
|
+
(service) => service.request.id as string
|
|
197
|
+
)
|
|
198
|
+
primary = this.getPrimary(servicesSortedByVersion)
|
|
199
|
+
} else {
|
|
200
|
+
primary = this.getPrimary(services)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// `this.backupsList` & this.backupTimings are used in selecting secondaries
|
|
204
|
+
const backupsList = _.without(services, primary)
|
|
205
|
+
const backupTimings = healthyServiceTimings.filter(
|
|
206
|
+
(timing) => timing.request.id !== primary
|
|
207
|
+
)
|
|
208
|
+
this.setBackupsList(backupsList, backupTimings)
|
|
209
|
+
|
|
210
|
+
const secondaries = this.getSecondaries()
|
|
211
|
+
|
|
212
|
+
this.decisionTree.push({
|
|
213
|
+
stage: DECISION_TREE_STATE.SELECT_PRIMARY_AND_SECONDARIES,
|
|
214
|
+
val: {
|
|
215
|
+
primary,
|
|
216
|
+
secondaries: secondaries.toString(),
|
|
217
|
+
services: Object.keys(servicesMap).toString()
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
if (log) {
|
|
222
|
+
this.logger.info(
|
|
223
|
+
'CreatorNodeSelection - final decision tree state',
|
|
224
|
+
this.decisionTree
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
return { primary, secondaries, services: servicesMap }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Checks the sync progress of a Content Node
|
|
232
|
+
* @param service Content Node endopint
|
|
233
|
+
* @param timeout ms
|
|
234
|
+
*/
|
|
235
|
+
async getSyncStatus(service: ServiceName, timeout: Timeout = null) {
|
|
236
|
+
try {
|
|
237
|
+
const syncStatus = await this.creatorNode.getSyncStatus(service, timeout)
|
|
238
|
+
return { service, syncStatus, error: null }
|
|
239
|
+
} catch (e) {
|
|
240
|
+
return { service, syncStatus: null, error: e }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Sets backupsList to input
|
|
246
|
+
* @param backupsList string array of Content Node endpoints
|
|
247
|
+
*/
|
|
248
|
+
setBackupsList(backupsList: ServiceName[], backupTimings: Timing[]) {
|
|
249
|
+
// Rest of services that are not selected as the primary are valid backups. Add as backup
|
|
250
|
+
// This backups list will also be in order of descending highest version/fastest
|
|
251
|
+
this.backupsList = backupsList
|
|
252
|
+
this.backupTimings = backupTimings
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get backups in the form of an array
|
|
257
|
+
*/
|
|
258
|
+
getBackupsList() {
|
|
259
|
+
return this.backupsList
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get backup timings in the form of an array
|
|
264
|
+
*/
|
|
265
|
+
getBackupTimings() {
|
|
266
|
+
return this.backupTimings
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Select a primary Content Node
|
|
271
|
+
* @param {string[]} services all healthy Content Node endpoints
|
|
272
|
+
*/
|
|
273
|
+
getPrimary(services: string[]) {
|
|
274
|
+
// Index 0 of services will be the most optimal Content Node candidate
|
|
275
|
+
// TODO: fix `as` cast
|
|
276
|
+
return services[0] as string
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Selects secondary Content Nodes
|
|
281
|
+
* Returns first nodes from `services`, optionally sorted by version
|
|
282
|
+
*/
|
|
283
|
+
getSecondaries() {
|
|
284
|
+
const numberOfSecondaries = this.numberOfNodes - 1
|
|
285
|
+
const backupsList = this.getBackupsList()
|
|
286
|
+
const backupTimings = this.getBackupTimings()
|
|
287
|
+
|
|
288
|
+
let secondaries
|
|
289
|
+
if (this.preferHigherPatchForSecondaries) {
|
|
290
|
+
const backupTimingsSortedByVersion = sortServiceTimings({
|
|
291
|
+
serviceTimings: backupTimings,
|
|
292
|
+
currentVersion: this.currentVersion,
|
|
293
|
+
sortByVersion: true,
|
|
294
|
+
equivalencyDelta: this.equivalencyDelta
|
|
295
|
+
})
|
|
296
|
+
const secondaryTimings = backupTimingsSortedByVersion.slice(
|
|
297
|
+
0,
|
|
298
|
+
numberOfSecondaries
|
|
299
|
+
)
|
|
300
|
+
secondaries = secondaryTimings.map(
|
|
301
|
+
(timing) => timing.request.id as string
|
|
302
|
+
)
|
|
303
|
+
} else {
|
|
304
|
+
secondaries = backupsList.slice(0, numberOfSecondaries)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return secondaries
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Performs a sync check for every endpoint in services. Returns an array of successful sync checked endpoints and
|
|
312
|
+
* adds the err'd sync checked endpoints to this.unhealthy
|
|
313
|
+
* @param services content node endpoints
|
|
314
|
+
* @param timeout ms applied to each request
|
|
315
|
+
*/
|
|
316
|
+
async _performSyncChecks(services: ServiceName[], timeout: Timeout = null) {
|
|
317
|
+
const successfulSyncCheckServices: ServiceName[] = []
|
|
318
|
+
const syncResponses = await Promise.all(
|
|
319
|
+
services.map(
|
|
320
|
+
async (service) => await this.getSyncStatus(service, timeout)
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
// Perform sync checks on all services
|
|
324
|
+
for (const response of syncResponses) {
|
|
325
|
+
// Could not perform a sync check. Add to unhealthy
|
|
326
|
+
if (response.error) {
|
|
327
|
+
this.logger.warn(
|
|
328
|
+
`CreatorNodeSelection - Failed sync status check for ${response.service}: ${response.error}`
|
|
329
|
+
)
|
|
330
|
+
this.addUnhealthy(response.service)
|
|
331
|
+
continue
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const { syncStatus } = response
|
|
335
|
+
if (!syncStatus) continue
|
|
336
|
+
const { isBehind, isConfigured } = syncStatus
|
|
337
|
+
// a first time creator will have a sync status as isBehind = true and isConfigured = false. this is ok
|
|
338
|
+
const firstTimeCreator = isBehind && !isConfigured
|
|
339
|
+
// an existing creator will have a sync status (assuming healthy) as isBehind = false and isConfigured = true. this is also ok
|
|
340
|
+
const existingCreator = !isBehind && isConfigured
|
|
341
|
+
// if either of these two are true, the cnode is suited to be selected
|
|
342
|
+
if (firstTimeCreator || existingCreator) {
|
|
343
|
+
successfulSyncCheckServices.push(response.service)
|
|
344
|
+
} else {
|
|
345
|
+
// else, add to unhealthy
|
|
346
|
+
this.addUnhealthy(response.service)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return successfulSyncCheckServices
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Performs a health check for every endpoint in services. Returns an array of successful health checked endpoints and
|
|
355
|
+
* adds the err'd health checked endpoints to this.unhealthy, and a mapping of successful endpoint to its health check response.
|
|
356
|
+
* @param services content node endpoints
|
|
357
|
+
*/
|
|
358
|
+
async _performHealthChecks(services: string[]) {
|
|
359
|
+
// Perform a health check on services that passed the sync checks
|
|
360
|
+
const healthCheckedServices = await timeRequests({
|
|
361
|
+
requests: services.map((node) => ({
|
|
362
|
+
id: node,
|
|
363
|
+
url: `${node}/${this.healthCheckPath}`
|
|
364
|
+
})),
|
|
365
|
+
sortByVersion: false,
|
|
366
|
+
currentVersion: this.currentVersion,
|
|
367
|
+
timeout: this.timeout,
|
|
368
|
+
equivalencyDelta: this.equivalencyDelta
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
const healthyServices = healthCheckedServices.filter((resp) => {
|
|
372
|
+
const endpoint = resp.request.id as string
|
|
373
|
+
let isHealthy = false
|
|
374
|
+
|
|
375
|
+
// Check that the health check:
|
|
376
|
+
// 1. Responded with status code 200
|
|
377
|
+
// 2. Version is up to date on major and minor
|
|
378
|
+
// 3. Has enough storage space
|
|
379
|
+
// - Max capacity percent is defined from CN health check response. If not present,
|
|
380
|
+
// use existing value from `this.maxStorageUsedPercent`
|
|
381
|
+
if (resp.response) {
|
|
382
|
+
const isUp = resp.response.status === 200
|
|
383
|
+
const versionIsUpToDate = this.ethContracts.hasSameMajorAndMinorVersion(
|
|
384
|
+
this.currentVersion as string,
|
|
385
|
+
resp.response.data.data.version
|
|
386
|
+
)
|
|
387
|
+
const { storagePathSize, storagePathUsed, maxStorageUsedPercent } =
|
|
388
|
+
resp.response.data.data
|
|
389
|
+
if (maxStorageUsedPercent) {
|
|
390
|
+
this.maxStorageUsedPercent = maxStorageUsedPercent
|
|
391
|
+
} else {
|
|
392
|
+
this.logger.warn(
|
|
393
|
+
`maxStorageUsedPercent not found in health check response. Using constructor value of ${this.maxStorageUsedPercent}% as maxStorageUsedPercent.`
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
const hasEnoughStorage = this._hasEnoughStorageSpace(
|
|
397
|
+
storagePathSize,
|
|
398
|
+
storagePathUsed
|
|
399
|
+
)
|
|
400
|
+
isHealthy = isUp && versionIsUpToDate && hasEnoughStorage
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!isHealthy) {
|
|
404
|
+
this.addUnhealthy(endpoint)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return isHealthy
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
// Create a mapping of healthy services and their responses. Used on dapp to display the healthy services for selection
|
|
411
|
+
// Also update services to be healthy services
|
|
412
|
+
const servicesMap: Record<string, AxiosResponse['data']> = {}
|
|
413
|
+
const healthyServicesList = healthyServices.map((service) => {
|
|
414
|
+
const requestId = service.request.id as string
|
|
415
|
+
servicesMap[requestId] = service.response?.data
|
|
416
|
+
return service.request.id as string
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
this.decisionTree.push({
|
|
420
|
+
stage:
|
|
421
|
+
DECISION_TREE_STATE.FILTER_OUT_UNHEALTHY_OUTDATED_AND_NO_STORAGE_SPACE,
|
|
422
|
+
val: healthyServicesList
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
// Record metrics
|
|
426
|
+
if (this.creatorNode?.monitoringCallbacks.healthCheck) {
|
|
427
|
+
healthCheckedServices.forEach((check) => {
|
|
428
|
+
if (check.response?.data) {
|
|
429
|
+
const url = new URL(check.request.url)
|
|
430
|
+
const data = check.response.data.data
|
|
431
|
+
try {
|
|
432
|
+
// @ts-expect-error we make a check that it exists above, not sure why this isn't caught
|
|
433
|
+
this.creatorNode.monitoringCallbacks.healthCheck({
|
|
434
|
+
endpoint: url.origin,
|
|
435
|
+
pathname: url.pathname,
|
|
436
|
+
searchParams: url.searchParams,
|
|
437
|
+
version: data.version,
|
|
438
|
+
git: data.git,
|
|
439
|
+
selectedDiscoveryNode: data.selectedDiscoveryProvider,
|
|
440
|
+
databaseSize: data.databaseSize,
|
|
441
|
+
databaseConnections: data.databaseConnections,
|
|
442
|
+
totalMemory: data.totalMemory,
|
|
443
|
+
usedMemory: data.usedMemory,
|
|
444
|
+
totalStorage: data.storagePathSize,
|
|
445
|
+
usedStorage: data.storagePathUsed,
|
|
446
|
+
maxFileDescriptors: data.maxFileDescriptors,
|
|
447
|
+
allocatedFileDescriptors: data.allocatedFileDescriptors,
|
|
448
|
+
receivedBytesPerSec: data.receivedBytesPerSec,
|
|
449
|
+
transferredBytesPerSec: data.transferredBytesPerSec,
|
|
450
|
+
transcodeWaiting: data.transcodeWaiting,
|
|
451
|
+
transcodeActive: data.transcodeActive,
|
|
452
|
+
fileProcessingWaiting: data.fileProcessingWaiting,
|
|
453
|
+
fileProcessingActive: data.fileProcessingActive
|
|
454
|
+
})
|
|
455
|
+
} catch (e) {
|
|
456
|
+
// Swallow errors -- this method should not throw generally
|
|
457
|
+
this.logger.error(e)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
})
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
healthyServicesList,
|
|
465
|
+
healthyServicesMap: servicesMap,
|
|
466
|
+
healthyServiceTimings: healthyServices
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
_hasEnoughStorageSpace(
|
|
471
|
+
storagePathSize?: number | null,
|
|
472
|
+
storagePathUsed?: number | null
|
|
473
|
+
) {
|
|
474
|
+
// If for any reason these values off the response is falsy value, default to enough storage
|
|
475
|
+
if (
|
|
476
|
+
storagePathSize === null ||
|
|
477
|
+
storagePathSize === undefined ||
|
|
478
|
+
storagePathUsed === null ||
|
|
479
|
+
storagePathUsed === undefined
|
|
480
|
+
) {
|
|
481
|
+
return true
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return (
|
|
485
|
+
(100 * storagePathUsed) / storagePathSize < this.maxStorageUsedPercent
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const CREATOR_NODE_SERVICE_NAME = 'content-node'
|
|
2
|
+
|
|
3
|
+
export enum DECISION_TREE_STATE {
|
|
4
|
+
GET_ALL_SERVICES = 'Get All Services',
|
|
5
|
+
FILTER_TO_WHITELIST = 'Filter To Whitelist',
|
|
6
|
+
FILTER_FROM_BLACKLIST = 'Filter From Blacklist',
|
|
7
|
+
FILTER_OUT_UNHEALTHY_OUTDATED_AND_NO_STORAGE_SPACE = 'Filter Out Unhealthy, Outdated, And No Storage Space',
|
|
8
|
+
FILTER_OUT_SYNC_IN_PROGRESS = 'Filter Out Sync In Progress',
|
|
9
|
+
SELECT_PRIMARY_AND_SECONDARIES = 'Select Primary And Secondaries'
|
|
10
|
+
}
|