@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.
Files changed (278) hide show
  1. package/.eslintrc +38 -0
  2. package/.prettierrc.js +1 -0
  3. package/.python-version +1 -0
  4. package/Dockerfile +15 -0
  5. package/README.md +3 -0
  6. package/babel.config.js +3 -0
  7. package/data-contracts/ABIs/AdminUpgradeabilityProxy.json +132 -0
  8. package/data-contracts/ABIs/BaseAdminUpgradeabilityProxy.json +113 -0
  9. package/data-contracts/ABIs/BaseUpgradeabilityProxy.json +22 -0
  10. package/data-contracts/ABIs/DiscoveryProviderFactory.json +189 -0
  11. package/data-contracts/ABIs/DiscoveryProviderFactoryInterface.json +61 -0
  12. package/data-contracts/ABIs/DiscoveryProviderStorage.json +205 -0
  13. package/data-contracts/ABIs/DiscoveryProviderStorageInterface.json +65 -0
  14. package/data-contracts/ABIs/ECDSA.json +4 -0
  15. package/data-contracts/ABIs/IPLDBlacklistFactory.json +168 -0
  16. package/data-contracts/ABIs/Initializable.json +4 -0
  17. package/data-contracts/ABIs/Migrations.json +67 -0
  18. package/data-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
  19. package/data-contracts/ABIs/Ownable.json +79 -0
  20. package/data-contracts/ABIs/PlaylistFactory.json +669 -0
  21. package/data-contracts/ABIs/PlaylistFactoryInterface.json +42 -0
  22. package/data-contracts/ABIs/PlaylistStorage.json +250 -0
  23. package/data-contracts/ABIs/PlaylistStorageInterface.json +129 -0
  24. package/data-contracts/ABIs/Proxy.json +10 -0
  25. package/data-contracts/ABIs/Registry.json +240 -0
  26. package/data-contracts/ABIs/RegistryContract.json +102 -0
  27. package/data-contracts/ABIs/RegistryContractInterface.json +28 -0
  28. package/data-contracts/ABIs/RegistryInterface.json +66 -0
  29. package/data-contracts/ABIs/SigningLogic.json +43 -0
  30. package/data-contracts/ABIs/SigningLogicInitializable.json +46 -0
  31. package/data-contracts/ABIs/SocialFeatureFactory.json +460 -0
  32. package/data-contracts/ABIs/SocialFeatureStorage.json +225 -0
  33. package/data-contracts/ABIs/SocialFeatureStorageInterface.json +123 -0
  34. package/data-contracts/ABIs/TestContract.json +135 -0
  35. package/data-contracts/ABIs/TestContractInterface.json +19 -0
  36. package/data-contracts/ABIs/TestContractWithStorage.json +165 -0
  37. package/data-contracts/ABIs/TestContractWithStorageInterface.json +24 -0
  38. package/data-contracts/ABIs/TestStorage.json +144 -0
  39. package/data-contracts/ABIs/TestStorageInterface.json +42 -0
  40. package/data-contracts/ABIs/TestUserReplicaSetManager.json +432 -0
  41. package/data-contracts/ABIs/TrackFactory.json +391 -0
  42. package/data-contracts/ABIs/TrackFactoryInterface.json +73 -0
  43. package/data-contracts/ABIs/TrackStorage.json +223 -0
  44. package/data-contracts/ABIs/TrackStorageInterface.json +121 -0
  45. package/data-contracts/ABIs/UpgradeabilityProxy.json +37 -0
  46. package/data-contracts/ABIs/UserFactory.json +657 -0
  47. package/data-contracts/ABIs/UserFactoryInterface.json +65 -0
  48. package/data-contracts/ABIs/UserLibraryFactory.json +334 -0
  49. package/data-contracts/ABIs/UserReplicaSetManager.json +418 -0
  50. package/data-contracts/ABIs/UserStorage.json +233 -0
  51. package/data-contracts/ABIs/UserStorageInterface.json +93 -0
  52. package/data-contracts/signatureSchemas.ts +1236 -0
  53. package/dist/core.d.ts +446 -0
  54. package/dist/core.js +769 -0
  55. package/dist/core.js.map +1 -0
  56. package/dist/index.d.ts +689 -0
  57. package/dist/index.js +72850 -0
  58. package/dist/index.js.map +1 -0
  59. package/eth-contracts/ABIs/Address.json +4 -0
  60. package/eth-contracts/ABIs/AudiusAdminUpgradeabilityProxy.json +105 -0
  61. package/eth-contracts/ABIs/AudiusClaimDistributor.json +4968 -0
  62. package/eth-contracts/ABIs/AudiusToken.json +724 -0
  63. package/eth-contracts/ABIs/BaseUpgradeabilityProxy.json +23 -0
  64. package/eth-contracts/ABIs/Checkpointing.json +4 -0
  65. package/eth-contracts/ABIs/ClaimsManager.json +539 -0
  66. package/eth-contracts/ABIs/Context.json +11 -0
  67. package/eth-contracts/ABIs/DelegateManager.json +989 -0
  68. package/eth-contracts/ABIs/DelegateManagerV2.json +1049 -0
  69. package/eth-contracts/ABIs/DelegateManagerV2Bad.json +1049 -0
  70. package/eth-contracts/ABIs/ERC20.json +252 -0
  71. package/eth-contracts/ABIs/ERC20Burnable.json +287 -0
  72. package/eth-contracts/ABIs/ERC20Detailed.json +270 -0
  73. package/eth-contracts/ABIs/ERC20Mintable.json +364 -0
  74. package/eth-contracts/ABIs/ERC20Pausable.json +397 -0
  75. package/eth-contracts/ABIs/EthRewardsManager.json +174 -0
  76. package/eth-contracts/ABIs/Governance.json +938 -0
  77. package/eth-contracts/ABIs/GovernanceUpgraded.json +953 -0
  78. package/eth-contracts/ABIs/GovernanceV2.json +938 -0
  79. package/eth-contracts/ABIs/IERC20.json +200 -0
  80. package/eth-contracts/ABIs/Initializable.json +4 -0
  81. package/eth-contracts/ABIs/InitializableV2.json +14 -0
  82. package/eth-contracts/ABIs/Migrations.json +71 -0
  83. package/eth-contracts/ABIs/MinterRole.json +91 -0
  84. package/eth-contracts/ABIs/MockAccount.json +62 -0
  85. package/eth-contracts/ABIs/MockDelegateManager.json +55 -0
  86. package/eth-contracts/ABIs/MockStakingCaller.json +259 -0
  87. package/eth-contracts/ABIs/MockWormhole.json +106 -0
  88. package/eth-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
  89. package/eth-contracts/ABIs/Ownable.json +93 -0
  90. package/eth-contracts/ABIs/Pausable.json +150 -0
  91. package/eth-contracts/ABIs/PauserRole.json +91 -0
  92. package/eth-contracts/ABIs/Proxy.json +10 -0
  93. package/eth-contracts/ABIs/Registry.json +288 -0
  94. package/eth-contracts/ABIs/Roles.json +4 -0
  95. package/eth-contracts/ABIs/SafeERC20.json +4 -0
  96. package/eth-contracts/ABIs/SafeMath.json +4 -0
  97. package/eth-contracts/ABIs/ServiceProviderFactory.json +1153 -0
  98. package/eth-contracts/ABIs/ServiceTypeManager.json +337 -0
  99. package/eth-contracts/ABIs/Staking.json +555 -0
  100. package/eth-contracts/ABIs/StakingUpgraded.json +570 -0
  101. package/eth-contracts/ABIs/TestContract.json +44 -0
  102. package/eth-contracts/ABIs/TrustedNotifierManager.json +265 -0
  103. package/eth-contracts/ABIs/Uint256Helpers.json +4 -0
  104. package/eth-contracts/ABIs/UpgradeabilityProxy.json +40 -0
  105. package/eth-contracts/ABIs/Wormhole.json +45 -0
  106. package/eth-contracts/ABIs/WormholeClient.json +155 -0
  107. package/examples/file.mp3 +0 -0
  108. package/examples/initAudiusLibs.js +86 -0
  109. package/examples/initializeVersions.js +95 -0
  110. package/examples/pic.jpg +0 -0
  111. package/initScripts/configureLocalDiscProv.js +167 -0
  112. package/initScripts/helpers/claim.js +43 -0
  113. package/initScripts/helpers/distributeTokens.js +24 -0
  114. package/initScripts/helpers/spRegistration.js +138 -0
  115. package/initScripts/helpers/utils.js +34 -0
  116. package/initScripts/helpers/version.js +93 -0
  117. package/initScripts/local.js +617 -0
  118. package/initScripts/mainnet.js +131 -0
  119. package/initScripts/manageProdRelayerWallets.js +191 -0
  120. package/package.json +125 -0
  121. package/rollup.config.js +164 -0
  122. package/scripts/AudiusClaimDistributor.json +4968 -0
  123. package/scripts/Wormhole.json +155 -0
  124. package/scripts/addCIDToIpldBlacklist.js +124 -0
  125. package/scripts/circleci-test.sh +53 -0
  126. package/scripts/communityRewards/transferCommunityRewardsToSolana.js +222 -0
  127. package/scripts/ipfs.sh +58 -0
  128. package/scripts/migrate_contracts.sh +25 -0
  129. package/scripts/reset.sh +65 -0
  130. package/scripts/test.sh +77 -0
  131. package/src/api/account.js +670 -0
  132. package/src/api/base.js +122 -0
  133. package/src/api/file.js +168 -0
  134. package/src/api/playlist.js +328 -0
  135. package/src/api/rewards.d.ts +4 -0
  136. package/src/api/rewards.js +682 -0
  137. package/src/api/serviceProvider.js +154 -0
  138. package/src/api/track.js +604 -0
  139. package/src/api/user.js +888 -0
  140. package/src/api/user.test.js +172 -0
  141. package/src/constants.ts +7 -0
  142. package/src/core.ts +3 -0
  143. package/src/index.js +6 -0
  144. package/src/libs.d.ts +3 -0
  145. package/src/libs.js +619 -0
  146. package/src/sanityChecks/addSecondaries.js +40 -0
  147. package/src/sanityChecks/assignReplicaSetIfNecessary.js +10 -0
  148. package/src/sanityChecks/index.d.ts +9 -0
  149. package/src/sanityChecks/index.js +31 -0
  150. package/src/sanityChecks/isCreator.js +73 -0
  151. package/src/sanityChecks/needsRecoveryEmail.js +20 -0
  152. package/src/sanityChecks/rolloverNodes.js +74 -0
  153. package/src/sanityChecks/sanitizeNodes.js +24 -0
  154. package/src/sanityChecks/syncNodes.js +28 -0
  155. package/src/sdk/constants.ts +10 -0
  156. package/src/sdk/index.ts +1 -0
  157. package/src/sdk/oauth/Oauth.ts +265 -0
  158. package/src/sdk/oauth/index.ts +1 -0
  159. package/src/sdk/sdk.ts +102 -0
  160. package/src/service-selection/ServiceSelection.test.ts +320 -0
  161. package/src/service-selection/ServiceSelection.ts +460 -0
  162. package/src/service-selection/constants.ts +14 -0
  163. package/src/service-selection/index.ts +1 -0
  164. package/src/services/ABIDecoder/AudiusABIDecoder.ts +71 -0
  165. package/src/services/ABIDecoder/index.ts +1 -0
  166. package/src/services/comstock/Comstock.ts +39 -0
  167. package/src/services/comstock/index.ts +1 -0
  168. package/src/services/contracts/ContractClient.ts +227 -0
  169. package/src/services/contracts/GovernedContractClient.ts +53 -0
  170. package/src/services/contracts/ProviderSelection.ts +42 -0
  171. package/src/services/creatorNode/CreatorNode.ts +1065 -0
  172. package/src/services/creatorNode/CreatorNodeSelection.test.ts +997 -0
  173. package/src/services/creatorNode/CreatorNodeSelection.ts +488 -0
  174. package/src/services/creatorNode/constants.ts +10 -0
  175. package/src/services/creatorNode/index.ts +2 -0
  176. package/src/services/dataContracts/AudiusContracts.ts +234 -0
  177. package/src/services/dataContracts/IPLDBlacklistFactoryClient.ts +73 -0
  178. package/src/services/dataContracts/PlaylistFactoryClient.ts +370 -0
  179. package/src/services/dataContracts/RegistryClient.ts +95 -0
  180. package/src/services/dataContracts/SocialFeatureFactoryClient.ts +196 -0
  181. package/src/services/dataContracts/TrackFactoryClient.ts +131 -0
  182. package/src/services/dataContracts/UserFactoryClient.ts +351 -0
  183. package/src/services/dataContracts/UserLibraryFactoryClient.ts +115 -0
  184. package/src/services/dataContracts/UserReplicaSetManagerClient.ts +206 -0
  185. package/src/services/dataContracts/index.ts +1 -0
  186. package/src/services/discoveryProvider/DiscoveryProvider.ts +1168 -0
  187. package/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +536 -0
  188. package/src/services/discoveryProvider/DiscoveryProviderSelection.ts +383 -0
  189. package/src/services/discoveryProvider/constants.ts +13 -0
  190. package/src/services/discoveryProvider/index.ts +1 -0
  191. package/src/services/discoveryProvider/requests.ts +629 -0
  192. package/src/services/ethContracts/AudiusTokenClient.ts +163 -0
  193. package/src/services/ethContracts/ClaimDistributionClient.ts +45 -0
  194. package/src/services/ethContracts/ClaimsManagerClient.ts +102 -0
  195. package/src/services/ethContracts/DelegateManagerClient.ts +480 -0
  196. package/src/services/ethContracts/EthContracts.ts +359 -0
  197. package/src/services/ethContracts/EthRewardsManagerClient.ts +33 -0
  198. package/src/services/ethContracts/GovernanceClient.ts +451 -0
  199. package/src/services/ethContracts/RegistryClient.ts +33 -0
  200. package/src/services/ethContracts/ServiceProviderFactoryClient.ts +691 -0
  201. package/src/services/ethContracts/ServiceTypeManagerClient.ts +112 -0
  202. package/src/services/ethContracts/StakingProxyClient.ts +97 -0
  203. package/src/services/ethContracts/TrustedNotifierManagerClient.ts +101 -0
  204. package/src/services/ethContracts/WormholeClient.ts +97 -0
  205. package/src/services/ethContracts/index.ts +1 -0
  206. package/src/services/ethWeb3Manager/EthWeb3Manager.ts +239 -0
  207. package/src/services/ethWeb3Manager/index.ts +1 -0
  208. package/src/services/hedgehog/Hedgehog.ts +96 -0
  209. package/src/services/hedgehog/index.ts +1 -0
  210. package/src/services/identity/IdentityService.ts +551 -0
  211. package/src/services/identity/index.ts +1 -0
  212. package/src/services/identity/requests.ts +65 -0
  213. package/src/services/schemaValidator/SchemaValidator.ts +105 -0
  214. package/src/services/schemaValidator/index.ts +1 -0
  215. package/src/services/schemaValidator/schemas/trackSchema.json +267 -0
  216. package/src/services/schemaValidator/schemas/userSchema.json +230 -0
  217. package/src/services/solanaAudiusData/errors.ts +20 -0
  218. package/src/services/solanaAudiusData/index.ts +1189 -0
  219. package/src/services/solanaWeb3Manager/errors.js +101 -0
  220. package/src/services/solanaWeb3Manager/index.d.ts +46 -0
  221. package/src/services/solanaWeb3Manager/index.js +655 -0
  222. package/src/services/solanaWeb3Manager/padBNToUint8Array.ts +7 -0
  223. package/src/services/solanaWeb3Manager/rewards.js +941 -0
  224. package/src/services/solanaWeb3Manager/rewardsAttester.ts +1093 -0
  225. package/src/services/solanaWeb3Manager/tokenAccount.js +149 -0
  226. package/src/services/solanaWeb3Manager/transactionHandler.js +345 -0
  227. package/src/services/solanaWeb3Manager/transfer.js +272 -0
  228. package/src/services/solanaWeb3Manager/userBank.js +160 -0
  229. package/src/services/solanaWeb3Manager/utils.d.ts +31 -0
  230. package/src/services/solanaWeb3Manager/utils.js +163 -0
  231. package/src/services/solanaWeb3Manager/wAudio.js +28 -0
  232. package/src/services/solanaWeb3Manager/wAudio.test.js +30 -0
  233. package/src/services/web3Manager/Web3Config.ts +14 -0
  234. package/src/services/web3Manager/Web3Manager.ts +360 -0
  235. package/src/services/web3Manager/XMLHttpRequest.ts +11 -0
  236. package/src/services/web3Manager/index.ts +2 -0
  237. package/src/services/wormhole/index.js +424 -0
  238. package/src/types.ts +8 -0
  239. package/src/userStateManager.ts +53 -0
  240. package/src/utils/apiSigning.ts +51 -0
  241. package/src/utils/captcha.ts +97 -0
  242. package/src/utils/estimateGas.ts +64 -0
  243. package/src/utils/fileHasher.ts +278 -0
  244. package/src/utils/importContractABI.d.ts +9 -0
  245. package/src/utils/importContractABI.js +19 -0
  246. package/src/utils/index.ts +11 -0
  247. package/src/utils/multiProvider.ts +72 -0
  248. package/src/utils/network.test.ts +127 -0
  249. package/src/utils/network.ts +308 -0
  250. package/src/utils/promiseFight.test.ts +87 -0
  251. package/src/utils/promiseFight.ts +36 -0
  252. package/src/utils/signatures.ts +139 -0
  253. package/src/utils/types.ts +34 -0
  254. package/src/utils/utils.test.ts +36 -0
  255. package/src/utils/utils.ts +235 -0
  256. package/src/utils/uuid.ts +14 -0
  257. package/src/web3.d.ts +9 -0
  258. package/src/web3.js +8 -0
  259. package/tests/assets/static_image.png +0 -0
  260. package/tests/assets/static_text.txt +1 -0
  261. package/tests/audiusTokenClientTest.js +37 -0
  262. package/tests/creatorNodeTest.js +19 -0
  263. package/tests/fileHasherTest.js +125 -0
  264. package/tests/governanceTest.js +382 -0
  265. package/tests/helpers.js +105 -0
  266. package/tests/index.js +14 -0
  267. package/tests/playlistClientTest.js +157 -0
  268. package/tests/providerSelectionTest.js +241 -0
  269. package/tests/registryClientTest.js +19 -0
  270. package/tests/rewardsAttesterTest.js +373 -0
  271. package/tests/serviceTypeManagerClientTest.js +33 -0
  272. package/tests/socialFeatureClientTest.js +79 -0
  273. package/tests/stakingTest.js +302 -0
  274. package/tests/trackClientTest.js +86 -0
  275. package/tests/userClientTest.js +121 -0
  276. package/tsconfig.json +10 -0
  277. package/types/@audius-hedgehog/index.d.ts +39 -0
  278. 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
+ }
@@ -0,0 +1,2 @@
1
+ export * from './CreatorNode'
2
+ export * from './CreatorNodeSelection'