@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,460 @@
|
|
|
1
|
+
import { sampleSize } from 'lodash'
|
|
2
|
+
import {
|
|
3
|
+
raceRequests,
|
|
4
|
+
allRequests,
|
|
5
|
+
ServiceName,
|
|
6
|
+
ServiceWithEndpoint,
|
|
7
|
+
Service,
|
|
8
|
+
Maybe
|
|
9
|
+
} from '../utils'
|
|
10
|
+
import { DECISION_TREE_STATE } from './constants'
|
|
11
|
+
import type { AxiosResponse } from 'axios'
|
|
12
|
+
|
|
13
|
+
function isVerbose(service: Service): service is ServiceWithEndpoint {
|
|
14
|
+
return typeof service !== 'string'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type GetServicesInput =
|
|
18
|
+
| (() => Promise<ServiceName[]>)
|
|
19
|
+
| ((config: { verbose: false }) => Promise<ServiceName[]>)
|
|
20
|
+
| ((config: { verbose: true }) => Promise<ServiceWithEndpoint[]>)
|
|
21
|
+
| ((config: {
|
|
22
|
+
verbose: boolean
|
|
23
|
+
}) => Promise<ServiceName[] | ServiceWithEndpoint[]>)
|
|
24
|
+
|
|
25
|
+
interface GetServices {
|
|
26
|
+
(): Promise<ServiceName[]>
|
|
27
|
+
(config: { verbose: false }): Promise<ServiceName[]>
|
|
28
|
+
(config: { verbose: true }): Promise<ServiceWithEndpoint[]>
|
|
29
|
+
(config: { verbose: boolean }): Promise<Service[]>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Decision {
|
|
33
|
+
stage: string
|
|
34
|
+
val?: unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type Backup = { block_difference: number; version: string }
|
|
38
|
+
|
|
39
|
+
export interface ServiceSelectionConfig {
|
|
40
|
+
// services from this list should not be picked
|
|
41
|
+
blacklist?: Set<string> | undefined | null
|
|
42
|
+
// only services from this list are allowed to be picked
|
|
43
|
+
whitelist?: Set<string> | undefined | null
|
|
44
|
+
/*
|
|
45
|
+
* an (async) method to get a
|
|
46
|
+
* list of services to choose from. Optionally may return a verbose object with service metadata
|
|
47
|
+
*/
|
|
48
|
+
getServices: GetServicesInput
|
|
49
|
+
/*
|
|
50
|
+
* the maximum number of requests allowed to fire at
|
|
51
|
+
* once. Tweaking this value may impact browser performance
|
|
52
|
+
*/
|
|
53
|
+
maxConcurrentRequests?: number
|
|
54
|
+
// the timeout at which to give up on a service
|
|
55
|
+
requestTimeout?: Maybe<number>
|
|
56
|
+
/*
|
|
57
|
+
*the point at which the unhealthy services are freed so they
|
|
58
|
+
* may be tried again (re-requested)
|
|
59
|
+
*/
|
|
60
|
+
unhealthyTTL?: number
|
|
61
|
+
/*
|
|
62
|
+
* the point at which backup services are freed so they may be
|
|
63
|
+
* tried again (re-requested)
|
|
64
|
+
*/
|
|
65
|
+
backupsTTL?: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A class that assists with autoselecting services.
|
|
70
|
+
* `ServiceSelection` is intended to be overridden with further
|
|
71
|
+
* business logic that a particular sevice might preference.
|
|
72
|
+
*
|
|
73
|
+
* The general use case is as follows:
|
|
74
|
+
*
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* const selector = new ServiceSelection({
|
|
78
|
+
* getServices: ethContracts.getDiscoveryProviders()
|
|
79
|
+
* })
|
|
80
|
+
*
|
|
81
|
+
* const service = await selector.select()
|
|
82
|
+
*
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* This class operates by taking a list of services and
|
|
86
|
+
* round-robin makes requests at them until a suitable one is found.
|
|
87
|
+
*
|
|
88
|
+
* Two types of "bad" services are defined below:
|
|
89
|
+
* - Unhealthy: this service is bad and should not be used
|
|
90
|
+
* - Backup: this service is bad, but if we can't find anything better, maybe use it
|
|
91
|
+
*
|
|
92
|
+
* Classes that extend `ServiceSelection` can choose to implement custom logic on top
|
|
93
|
+
* of them and is generally how this class is intended to be used.
|
|
94
|
+
*/
|
|
95
|
+
export class ServiceSelection {
|
|
96
|
+
blacklist: Set<string> | undefined | null
|
|
97
|
+
whitelist: Set<string> | undefined | null
|
|
98
|
+
getServices: GetServices
|
|
99
|
+
maxConcurrentRequests: number
|
|
100
|
+
requestTimeout: number
|
|
101
|
+
unhealthyTTL: number
|
|
102
|
+
backupsTTL: number
|
|
103
|
+
unhealthy: Set<string>
|
|
104
|
+
backups: Record<string, Backup>
|
|
105
|
+
totalAttempts: number
|
|
106
|
+
decisionTree: Decision[]
|
|
107
|
+
unhealthyCleanupTimeout: NodeJS.Timeout | null = null
|
|
108
|
+
backupCleanupTimeout: NodeJS.Timeout | null = null
|
|
109
|
+
|
|
110
|
+
constructor({
|
|
111
|
+
blacklist,
|
|
112
|
+
whitelist,
|
|
113
|
+
getServices,
|
|
114
|
+
maxConcurrentRequests = 6,
|
|
115
|
+
requestTimeout = 30 * 1000, // 30s
|
|
116
|
+
unhealthyTTL = 60 * 60 * 1000, // 1 hour
|
|
117
|
+
backupsTTL = 2 * 60 * 1000 // 2 min
|
|
118
|
+
}: ServiceSelectionConfig) {
|
|
119
|
+
// For Creator Node selection
|
|
120
|
+
this.blacklist = blacklist
|
|
121
|
+
this.whitelist = whitelist
|
|
122
|
+
this.getServices = getServices as GetServices
|
|
123
|
+
this.maxConcurrentRequests = maxConcurrentRequests
|
|
124
|
+
this.requestTimeout = requestTimeout
|
|
125
|
+
this.unhealthyTTL = unhealthyTTL
|
|
126
|
+
this.backupsTTL = backupsTTL
|
|
127
|
+
|
|
128
|
+
// Truly "unhealthy" services. Should not ever be picked.
|
|
129
|
+
this.unhealthy = new Set([])
|
|
130
|
+
|
|
131
|
+
// Selectable services but not optimal. Will be picked as a last resort.
|
|
132
|
+
this.backups = {}
|
|
133
|
+
|
|
134
|
+
// Total number of services attempted
|
|
135
|
+
this.totalAttempts = 0
|
|
136
|
+
|
|
137
|
+
// The decision tree path that was taken. Reset on each new selection.
|
|
138
|
+
this.decisionTree = []
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Selects a service
|
|
143
|
+
* @param reset if reset is true, clear the decision tree
|
|
144
|
+
*/
|
|
145
|
+
// we need any type here to allow sub-classes to more strictly type return type
|
|
146
|
+
async select(reset: any = true): Promise<any> {
|
|
147
|
+
if (reset) {
|
|
148
|
+
this.decisionTree = []
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// If a short circuit is provided, take it. Don't check it, just use it.
|
|
152
|
+
const shortcircuit = this.shortcircuit()
|
|
153
|
+
this.decisionTree.push({
|
|
154
|
+
stage: DECISION_TREE_STATE.CHECK_SHORT_CIRCUIT,
|
|
155
|
+
val: shortcircuit
|
|
156
|
+
})
|
|
157
|
+
// If there is a shortcircuit defined and we have not blacklisted it, pick it
|
|
158
|
+
if (shortcircuit && (!this.blacklist || !this.blacklist.has(shortcircuit)))
|
|
159
|
+
return shortcircuit
|
|
160
|
+
|
|
161
|
+
// Get all the services
|
|
162
|
+
let services = await this.getServices()
|
|
163
|
+
this.decisionTree.push({
|
|
164
|
+
stage: DECISION_TREE_STATE.GET_ALL_SERVICES,
|
|
165
|
+
val: services
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// If a whitelist is provided, filter down to it
|
|
169
|
+
if (this.whitelist) {
|
|
170
|
+
services = this.filterToWhitelist(services)
|
|
171
|
+
this.decisionTree.push({
|
|
172
|
+
stage: DECISION_TREE_STATE.FILTER_TO_WHITELIST,
|
|
173
|
+
val: services
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// if a blacklist is provided, filter out services in the list
|
|
178
|
+
if (this.blacklist) {
|
|
179
|
+
services = this.filterFromBlacklist(services)
|
|
180
|
+
this.decisionTree.push({
|
|
181
|
+
stage: DECISION_TREE_STATE.FILTER_FROM_BLACKLIST,
|
|
182
|
+
val: services
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Filter out anything we know is already unhealthy
|
|
187
|
+
const filteredServices = this.filterOutKnownUnhealthy(services)
|
|
188
|
+
this.decisionTree.push({
|
|
189
|
+
stage: DECISION_TREE_STATE.FILTER_OUT_KNOWN_UNHEALTHY,
|
|
190
|
+
val: filteredServices
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// Randomly sample a "round" to test
|
|
194
|
+
const round = this.getSelectionRound(filteredServices)
|
|
195
|
+
this.decisionTree.push({
|
|
196
|
+
stage: DECISION_TREE_STATE.GET_SELECTION_ROUND,
|
|
197
|
+
val: round
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
this.totalAttempts += round.length
|
|
201
|
+
|
|
202
|
+
// If there are no services left to try, either pick a backup or return null
|
|
203
|
+
if (filteredServices.length === 0) {
|
|
204
|
+
this.decisionTree.push({
|
|
205
|
+
stage: DECISION_TREE_STATE.NO_SERVICES_LEFT_TO_TRY
|
|
206
|
+
})
|
|
207
|
+
if (this.getBackupsSize() > 0) {
|
|
208
|
+
// Some backup exists
|
|
209
|
+
const backup = await this.selectFromBackups()
|
|
210
|
+
this.decisionTree.push({
|
|
211
|
+
stage: DECISION_TREE_STATE.SELECTED_FROM_BACKUP,
|
|
212
|
+
val: backup
|
|
213
|
+
})
|
|
214
|
+
return backup
|
|
215
|
+
} else {
|
|
216
|
+
// Nothing could be found that was healthy.
|
|
217
|
+
// Reset everything we know so that we might try again.
|
|
218
|
+
this.unhealthy = new Set([])
|
|
219
|
+
this.backups = {}
|
|
220
|
+
this.decisionTree.push({
|
|
221
|
+
stage: DECISION_TREE_STATE.FAILED_AND_RESETTING
|
|
222
|
+
})
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Race this "round" of services, getting the best and ones that errored
|
|
228
|
+
// Note: ones that did not error or were not the best just get canceled so
|
|
229
|
+
// we don't really know anything about them at this point.
|
|
230
|
+
const { best, errored } = await this.race(round)
|
|
231
|
+
|
|
232
|
+
// Mark all the errored ones as unhealthy
|
|
233
|
+
errored.forEach((e) => {
|
|
234
|
+
if (e) {
|
|
235
|
+
this.addUnhealthy(e)
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// Trigger a cleanup event for all of the unhealthy and backup services,
|
|
240
|
+
// so they can get retried in the future
|
|
241
|
+
this.triggerCleanup()
|
|
242
|
+
|
|
243
|
+
// Recursively try this selection function if we didn't find something
|
|
244
|
+
if (!best) {
|
|
245
|
+
this.decisionTree.push({ stage: DECISION_TREE_STATE.ROUND_FAILED_RETRY })
|
|
246
|
+
return await this.select(/* reset */ false)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this.decisionTree.push({
|
|
250
|
+
stage: DECISION_TREE_STATE.MADE_A_SELECTION,
|
|
251
|
+
val: best
|
|
252
|
+
})
|
|
253
|
+
// If we made it this far, we found the best service! (of the rounds we tried)
|
|
254
|
+
return best
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Finds all selectable services (respecting whitelist, health checks & timeouts).
|
|
259
|
+
* Note: this method is potentially slow.
|
|
260
|
+
* If you need just a single service, prefer calling `.select()`
|
|
261
|
+
* @param {boolean} verbose whether or not to return full services metadata
|
|
262
|
+
* @param {Set} whitelist a whitelist to override the set of endpoints
|
|
263
|
+
*/
|
|
264
|
+
async findAll({ verbose = false, whitelist = this.whitelist } = {}) {
|
|
265
|
+
// Get all the services
|
|
266
|
+
let services = await this.getServices({ verbose })
|
|
267
|
+
|
|
268
|
+
// If a whitelist is provided, filter down to it
|
|
269
|
+
if (whitelist) {
|
|
270
|
+
services = services.filter((service) =>
|
|
271
|
+
whitelist.has(isVerbose(service) ? service.endpoint : service)
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Key the services by their health check endpoint
|
|
276
|
+
const urlMap = services.reduce<Record<string, Service>>(
|
|
277
|
+
(urlMap, service) => {
|
|
278
|
+
urlMap[
|
|
279
|
+
ServiceSelection.getHealthCheckEndpoint(
|
|
280
|
+
isVerbose(service) ? service.endpoint : service
|
|
281
|
+
)
|
|
282
|
+
] = service
|
|
283
|
+
return urlMap
|
|
284
|
+
},
|
|
285
|
+
{}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
const results = await allRequests({
|
|
290
|
+
urlMap,
|
|
291
|
+
timeout: this.requestTimeout,
|
|
292
|
+
validationCheck: (resp) => this.isHealthy(resp, urlMap)
|
|
293
|
+
})
|
|
294
|
+
return results
|
|
295
|
+
} catch (e) {
|
|
296
|
+
console.error(e)
|
|
297
|
+
return []
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Triggers a clean up of unhealthy and backup services so they can be retried later */
|
|
302
|
+
triggerCleanup() {
|
|
303
|
+
if (this.unhealthyCleanupTimeout) {
|
|
304
|
+
clearTimeout(this.unhealthyCleanupTimeout)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (this.backupCleanupTimeout) {
|
|
308
|
+
clearTimeout(this.backupCleanupTimeout)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this.unhealthyCleanupTimeout = setTimeout(() => {
|
|
312
|
+
this.clearUnhealthy()
|
|
313
|
+
}, this.unhealthyTTL)
|
|
314
|
+
this.backupCleanupTimeout = setTimeout(() => {
|
|
315
|
+
this.clearBackups()
|
|
316
|
+
}, this.backupsTTL)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
clearUnhealthy() {
|
|
320
|
+
this.unhealthy = new Set([])
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
clearBackups() {
|
|
324
|
+
this.backups = {}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** A short-circuit. If overriden, can be used to skip selection (which could be slow) */
|
|
328
|
+
shortcircuit(): null | string {
|
|
329
|
+
return null
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Filter out services that are in the blacklist
|
|
334
|
+
* @param services endpoints
|
|
335
|
+
*/
|
|
336
|
+
filterFromBlacklist(services: string[]) {
|
|
337
|
+
return services.filter((s) => !this.blacklist?.has(s))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Filter down services to those in the whitelist */
|
|
341
|
+
filterToWhitelist(services: string[]) {
|
|
342
|
+
return services.filter((s) => this.whitelist?.has(s))
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Filter out known unhealthy services from the provided */
|
|
346
|
+
filterOutKnownUnhealthy(services: string[]) {
|
|
347
|
+
return services.filter((s) => !this.unhealthy.has(s))
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Given a list of services, samples maxConcurrentRequests from them */
|
|
351
|
+
getSelectionRound(services: string[]) {
|
|
352
|
+
return sampleSize(services, this.maxConcurrentRequests)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** Gets the total number of attempts we've made this instantiation */
|
|
356
|
+
getTotalAttempts() {
|
|
357
|
+
return this.totalAttempts
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Where does the health check for this type of service live */
|
|
361
|
+
static getHealthCheckEndpoint(service: string) {
|
|
362
|
+
return `${service}/health_check`
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* What the criteria is for a healthy service
|
|
367
|
+
* @param response axios response
|
|
368
|
+
* @param {{ [key: string]: string}} urlMap health check urls mapped to their cannonical url
|
|
369
|
+
* e.g. https://discoveryprovider.audius.co/health_check => https://discoveryprovider.audius.co
|
|
370
|
+
*/
|
|
371
|
+
isHealthy(response: AxiosResponse, _urlMap: Record<string, Service>) {
|
|
372
|
+
return response.status === 200
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Races requests against each other with provided timeouts and health checks */
|
|
376
|
+
async race(services: string[]) {
|
|
377
|
+
// Key the services by their health check endpoint
|
|
378
|
+
const serviceMap = services.reduce<Record<string, string>>((acc, s) => {
|
|
379
|
+
acc[ServiceSelection.getHealthCheckEndpoint(s)] = s
|
|
380
|
+
return acc
|
|
381
|
+
}, {})
|
|
382
|
+
|
|
383
|
+
let best: string | null = null
|
|
384
|
+
try {
|
|
385
|
+
const { errored } = await raceRequests(
|
|
386
|
+
Object.keys(serviceMap),
|
|
387
|
+
(url) => {
|
|
388
|
+
best = serviceMap[url] as string
|
|
389
|
+
},
|
|
390
|
+
{},
|
|
391
|
+
/* timeout */ this.requestTimeout,
|
|
392
|
+
/* timeBetweenRequests */ 0,
|
|
393
|
+
/* validationCheck */ (resp) => this.isHealthy(resp, serviceMap)
|
|
394
|
+
)
|
|
395
|
+
this.decisionTree.push({
|
|
396
|
+
stage: DECISION_TREE_STATE.RACED_AND_FOUND_BEST,
|
|
397
|
+
val: best
|
|
398
|
+
})
|
|
399
|
+
return {
|
|
400
|
+
best,
|
|
401
|
+
errored: errored.map((e) => serviceMap[e.config.url ?? ''])
|
|
402
|
+
}
|
|
403
|
+
} catch (e) {
|
|
404
|
+
return { best: null, errored: [] }
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** Adds a service to the unhealthy set */
|
|
409
|
+
addUnhealthy(service: ServiceName) {
|
|
410
|
+
this.unhealthy.add(service)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Gets unhealthy set size */
|
|
414
|
+
getUnhealthySize() {
|
|
415
|
+
return this.unhealthy.size
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Removes from unhealthy set
|
|
420
|
+
* @param key service endpoint
|
|
421
|
+
*/
|
|
422
|
+
removeFromUnhealthy(key: string) {
|
|
423
|
+
if (this.unhealthy.has(key)) this.unhealthy.delete(key)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Adds a service to the list of backups
|
|
428
|
+
* @param service the service to add
|
|
429
|
+
* @param response the services response. This can be used to weigh various
|
|
430
|
+
* backups against eachother
|
|
431
|
+
*/
|
|
432
|
+
addBackup(service: string, response: Backup) {
|
|
433
|
+
this.backups[service] = response
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Controls how a backup is picked. Overriding methods may choose to use the backup's response.
|
|
438
|
+
* e.g. pick a backup that's the fewest versions behind
|
|
439
|
+
*/
|
|
440
|
+
async selectFromBackups() {
|
|
441
|
+
return Object.keys(this.backups)[0]
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Removes from backups
|
|
446
|
+
* @param key service endpoint
|
|
447
|
+
*/
|
|
448
|
+
removeFromBackups(key: string) {
|
|
449
|
+
if (Object.prototype.hasOwnProperty.call(this.backups, key))
|
|
450
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
451
|
+
delete this.backups[key]
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Returns the size of backups
|
|
456
|
+
*/
|
|
457
|
+
getBackupsSize() {
|
|
458
|
+
return Object.keys(this.backups).length
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export enum DECISION_TREE_STATE {
|
|
2
|
+
CHECK_SHORT_CIRCUIT = 'Check Short Circuit',
|
|
3
|
+
GET_ALL_SERVICES = 'Get All Services',
|
|
4
|
+
FILTER_TO_WHITELIST = 'Filter To Whitelist',
|
|
5
|
+
FILTER_FROM_BLACKLIST = 'Filter From Blacklist',
|
|
6
|
+
FILTER_OUT_KNOWN_UNHEALTHY = 'Filter Out Known Unhealthy',
|
|
7
|
+
GET_SELECTION_ROUND = 'Get Selection Round',
|
|
8
|
+
NO_SERVICES_LEFT_TO_TRY = 'No Services Left To Try',
|
|
9
|
+
SELECTED_FROM_BACKUP = 'Selected From Backup',
|
|
10
|
+
FAILED_AND_RESETTING = 'Failed Everything -- Resetting',
|
|
11
|
+
ROUND_FAILED_RETRY = 'Round Failed Retry',
|
|
12
|
+
MADE_A_SELECTION = 'Made A Selection',
|
|
13
|
+
RACED_AND_FOUND_BEST = 'Raced And Found Best'
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ServiceSelection'
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import abiDecoder from 'abi-decoder'
|
|
2
|
+
import { Utils } from '../../utils'
|
|
3
|
+
import type { AbiItem, AbiInput } from 'web3-utils'
|
|
4
|
+
import type { Log } from 'web3-core'
|
|
5
|
+
|
|
6
|
+
const abiMap: Record<string, AbiItem[]> = {}
|
|
7
|
+
|
|
8
|
+
function loadABI(abiFile: string) {
|
|
9
|
+
const contract = Utils.importDataContractABI(abiFile)
|
|
10
|
+
abiDecoder.addABI(contract.abi)
|
|
11
|
+
abiMap[contract.contractName] = contract.abi
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
loadABI('Registry.json')
|
|
15
|
+
loadABI('UserFactory.json')
|
|
16
|
+
loadABI('TrackFactory.json')
|
|
17
|
+
loadABI('DiscoveryProviderFactory.json')
|
|
18
|
+
loadABI('SocialFeatureFactory.json')
|
|
19
|
+
loadABI('PlaylistFactory.json')
|
|
20
|
+
loadABI('UserLibraryFactory.json')
|
|
21
|
+
loadABI('UserReplicaSetManager.json')
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- should just use esm
|
|
24
|
+
export class AudiusABIDecoder {
|
|
25
|
+
static decodeMethod(contractName: string, encodedABI: string) {
|
|
26
|
+
const decoded = abiDecoder.decodeMethod(encodedABI)
|
|
27
|
+
if (!decoded) {
|
|
28
|
+
throw new Error('No Audius ABI matches given data')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// hack around abi-decoder's lack of contract-specific support (only one global
|
|
32
|
+
// namespace of functions)
|
|
33
|
+
const abi = abiMap[contractName]
|
|
34
|
+
if (!abi) {
|
|
35
|
+
throw new Error('Unrecognized contract name')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let foundFunction: AbiItem | undefined
|
|
39
|
+
abi.forEach((item) => {
|
|
40
|
+
if (item.type === 'function' && item.name === decoded.name) {
|
|
41
|
+
foundFunction = item
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
if (!foundFunction) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Unrecognized function ${decoded.name} for contract ${contractName}`
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const paramSpecs = foundFunction.inputs as AbiInput[]
|
|
52
|
+
decoded.params.forEach((param, idx) => {
|
|
53
|
+
if (idx >= paramSpecs.length) {
|
|
54
|
+
throw new Error('Extra parameter')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const paramSpec = paramSpecs[idx]
|
|
58
|
+
if (paramSpec?.name !== param.name || paramSpec.type !== param.type) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Invalid name or value for param ${paramSpec?.name}: ${paramSpec?.type}`
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return decoded
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static decodeLogs(_: string, logs: Log[]) {
|
|
69
|
+
return abiDecoder.decodeLogs(logs)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AudiusABIDecoder'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import axios, { AxiosError, AxiosRequestConfig } from 'axios'
|
|
2
|
+
|
|
3
|
+
export class Comstock {
|
|
4
|
+
comstockEndpoint: string
|
|
5
|
+
|
|
6
|
+
constructor(comstockEndpoint: string) {
|
|
7
|
+
this.comstockEndpoint = comstockEndpoint
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async getComstock(obj: AxiosRequestConfig) {
|
|
11
|
+
const result = await this._makeRequest({
|
|
12
|
+
url: '/wallet_lookup',
|
|
13
|
+
method: 'get',
|
|
14
|
+
params: obj
|
|
15
|
+
})
|
|
16
|
+
return result
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* ------- INTERNAL FUNCTIONS ------- */
|
|
20
|
+
|
|
21
|
+
async _makeRequest(axiosRequestObj: AxiosRequestConfig) {
|
|
22
|
+
axiosRequestObj.baseURL = this.comstockEndpoint
|
|
23
|
+
// Axios throws for non-200 responses
|
|
24
|
+
try {
|
|
25
|
+
const resp = await axios(axiosRequestObj)
|
|
26
|
+
return resp.data
|
|
27
|
+
} catch (e) {
|
|
28
|
+
const error = e as AxiosError
|
|
29
|
+
if (error.response?.data?.error) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Server returned error: [${error.response.status.toString()}] ${
|
|
32
|
+
error.response.data.error
|
|
33
|
+
}`
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
throw error
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Comstock'
|