@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
package/src/api/user.js
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
const { pick, isEqual } = require('lodash')
|
|
2
|
+
const { Base, Services } = require('./base')
|
|
3
|
+
const { Utils } = require('../utils')
|
|
4
|
+
const { CreatorNode, getSpIDForEndpoint, setSpIDForEndpoint } = require('../services/creatorNode')
|
|
5
|
+
|
|
6
|
+
// User metadata fields that are required on the metadata object and can have
|
|
7
|
+
// null or non-null values
|
|
8
|
+
const USER_PROPS = [
|
|
9
|
+
'is_creator',
|
|
10
|
+
'is_verified',
|
|
11
|
+
'is_deactivated',
|
|
12
|
+
'name',
|
|
13
|
+
'handle',
|
|
14
|
+
'profile_picture',
|
|
15
|
+
'profile_picture_sizes',
|
|
16
|
+
'cover_photo',
|
|
17
|
+
'cover_photo_sizes',
|
|
18
|
+
'bio',
|
|
19
|
+
'location',
|
|
20
|
+
'creator_node_endpoint',
|
|
21
|
+
'associated_wallets',
|
|
22
|
+
'associated_sol_wallets',
|
|
23
|
+
'collectibles',
|
|
24
|
+
'playlist_library',
|
|
25
|
+
'events'
|
|
26
|
+
]
|
|
27
|
+
// User metadata fields that are required on the metadata object and only can have
|
|
28
|
+
// non-null values
|
|
29
|
+
const USER_REQUIRED_PROPS = [
|
|
30
|
+
'name',
|
|
31
|
+
'handle'
|
|
32
|
+
]
|
|
33
|
+
// Constants for user metadata fields
|
|
34
|
+
const USER_PROP_NAME_CONSTANTS = Object.freeze({
|
|
35
|
+
NAME: 'name',
|
|
36
|
+
IS_CREATOR: 'is_creator',
|
|
37
|
+
BIO: 'bio',
|
|
38
|
+
LOCATION: 'location',
|
|
39
|
+
PROFILE_PICTURE_SIZES: 'profile_picture_sizes',
|
|
40
|
+
COVER_PHOTO_SIZES: 'cover_photo_sizes',
|
|
41
|
+
CREATOR_NODE_ENDPOINT: 'creator_node_endpoint'
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
class Users extends Base {
|
|
45
|
+
constructor (serviceProvider, preferHigherPatchForPrimary, preferHigherPatchForSecondaries, ...args) {
|
|
46
|
+
super(...args)
|
|
47
|
+
|
|
48
|
+
this.ServiceProvider = serviceProvider
|
|
49
|
+
this.preferHigherPatchForPrimary = preferHigherPatchForPrimary
|
|
50
|
+
this.preferHigherPatchForSecondaries = preferHigherPatchForSecondaries
|
|
51
|
+
|
|
52
|
+
this.getUsers = this.getUsers.bind(this)
|
|
53
|
+
this.getMutualFollowers = this.getMutualFollowers.bind(this)
|
|
54
|
+
this.getFollowersForUser = this.getFollowersForUser.bind(this)
|
|
55
|
+
this.getFolloweesForUser = this.getFolloweesForUser.bind(this)
|
|
56
|
+
this.getUserRepostFeed = this.getUserRepostFeed.bind(this)
|
|
57
|
+
this.getSocialFeed = this.getSocialFeed.bind(this)
|
|
58
|
+
this.getTopCreatorsByGenres = this.getTopCreatorsByGenres.bind(this)
|
|
59
|
+
this.uploadProfileImages = this.uploadProfileImages.bind(this)
|
|
60
|
+
this.addUser = this.addUser.bind(this)
|
|
61
|
+
this.updateUser = this.updateUser.bind(this)
|
|
62
|
+
this.updateCreator = this.updateCreator.bind(this)
|
|
63
|
+
this.upgradeToCreator = this.upgradeToCreator.bind(this)
|
|
64
|
+
this.updateIsVerified = this.updateIsVerified.bind(this)
|
|
65
|
+
this.addUserFollow = this.addUserFollow.bind(this)
|
|
66
|
+
this.deleteUserFollow = this.deleteUserFollow.bind(this)
|
|
67
|
+
|
|
68
|
+
// For adding replica set to users on sign up
|
|
69
|
+
this.assignReplicaSet = this.assignReplicaSet.bind(this)
|
|
70
|
+
|
|
71
|
+
this.getClockValuesFromReplicaSet = this.getClockValuesFromReplicaSet.bind(this)
|
|
72
|
+
this._waitForCreatorNodeEndpointIndexing = this._waitForCreatorNodeEndpointIndexing.bind(this)
|
|
73
|
+
this._addUserOperations = this._addUserOperations.bind(this)
|
|
74
|
+
this._updateUserOperations = this._updateUserOperations.bind(this)
|
|
75
|
+
this._validateUserMetadata = this._validateUserMetadata.bind(this)
|
|
76
|
+
this.cleanUserMetadata = this.cleanUserMetadata.bind(this)
|
|
77
|
+
|
|
78
|
+
// For adding a creator_node_endpoint for a user if null
|
|
79
|
+
this.assignReplicaSetIfNecessary = this.assignReplicaSetIfNecessary.bind(this)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ----------- GETTERS ---------- */
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* get users with all relevant user data
|
|
86
|
+
* can be filtered by providing an integer array of ids
|
|
87
|
+
* @param {number} limit
|
|
88
|
+
* @param {number} offset
|
|
89
|
+
* @param {Object} idsArray
|
|
90
|
+
* @param {String} walletAddress
|
|
91
|
+
* @param {String} handle
|
|
92
|
+
* @param {Boolean} isCreator null returns all users, true returns creators only, false returns users only
|
|
93
|
+
* @param {number} currentUserId the currently logged in user
|
|
94
|
+
* @returns {Object} {Array of User metadata Objects}
|
|
95
|
+
* additional metadata fields on user objects:
|
|
96
|
+
* {Integer} track_count - track count for given user
|
|
97
|
+
* {Integer} playlist_count - playlist count for given user
|
|
98
|
+
* {Integer} album_count - album count for given user
|
|
99
|
+
* {Integer} follower_count - follower count for given user
|
|
100
|
+
* {Integer} followee_count - followee count for given user
|
|
101
|
+
* {Integer} repost_count - repost count for given user
|
|
102
|
+
* {Integer} track_blocknumber - blocknumber of latest track for user
|
|
103
|
+
* {Boolean} does_current_user_follow - does current user follow given user
|
|
104
|
+
* {Array} followee_follows - followees of current user that follow given user
|
|
105
|
+
* @example
|
|
106
|
+
* await getUsers()
|
|
107
|
+
* await getUsers(100, 0, [3,2,6]) - Invalid user ids will not be accepted
|
|
108
|
+
*/
|
|
109
|
+
async getUsers (limit = 100, offset = 0, idsArray = null, walletAddress = null, handle = null, isCreator = null, minBlockNumber = null) {
|
|
110
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
111
|
+
return this.discoveryProvider.getUsers(limit, offset, idsArray, walletAddress, handle, isCreator, minBlockNumber)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* get intersection of users that follow followeeUserId and users that are followed by followerUserId
|
|
116
|
+
* @param {number} followeeUserId user that is followed
|
|
117
|
+
* @example
|
|
118
|
+
* getMutualFollowers(100, 0, 1, 1) - IDs must be valid
|
|
119
|
+
*/
|
|
120
|
+
async getMutualFollowers (limit = 100, offset = 0, followeeUserId) {
|
|
121
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
122
|
+
const followerUserId = this.userStateManager.getCurrentUserId()
|
|
123
|
+
if (followerUserId) {
|
|
124
|
+
return this.discoveryProvider.getFollowIntersectionUsers(limit, offset, followeeUserId, followerUserId)
|
|
125
|
+
}
|
|
126
|
+
return []
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* get users that follow followeeUserId, sorted by follower count descending
|
|
131
|
+
* @param {number} currentUserId the currently logged in user
|
|
132
|
+
* @param {number} followeeUserId user that is followed
|
|
133
|
+
* @return {Array} array of user objects with standard user metadata
|
|
134
|
+
*/
|
|
135
|
+
async getFollowersForUser (limit = 100, offset = 0, followeeUserId) {
|
|
136
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
137
|
+
return this.discoveryProvider.getFollowersForUser(limit, offset, followeeUserId)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* get users that are followed by followerUserId, sorted by follower count descending
|
|
142
|
+
* @param {number} currentUserId the currently logged in user
|
|
143
|
+
* @param {number} followerUserId user - i am the one who follows
|
|
144
|
+
* @return {Array} array of user objects with standard user metadata
|
|
145
|
+
*/
|
|
146
|
+
async getFolloweesForUser (limit = 100, offset = 0, followerUserId) {
|
|
147
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
148
|
+
return this.discoveryProvider.getFolloweesForUser(limit, offset, followerUserId)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Return repost feed for requested user
|
|
153
|
+
* @param {number} userId - requested user id
|
|
154
|
+
* @param {filter} string - filter by "all", "original", or "repost"
|
|
155
|
+
* @param {number} limit - max # of items to return (for pagination)
|
|
156
|
+
* @param {number} offset - offset into list to return from (for pagination)
|
|
157
|
+
* @returns {Object} {Array of track and playlist metadata objects}
|
|
158
|
+
* additional metadata fields on track and playlist objects:
|
|
159
|
+
* {String} activity_timestamp - timestamp of requested user's repost for given track or playlist,
|
|
160
|
+
* used for sorting feed
|
|
161
|
+
* {Integer} repost_count - repost count of given track/playlist
|
|
162
|
+
* {Integer} save_count - save count of given track/playlist
|
|
163
|
+
* {Boolean} has_current_user_reposted - has current user reposted given track/playlist
|
|
164
|
+
* {Array} followee_reposts - followees of current user that have reposted given track/playlist
|
|
165
|
+
*/
|
|
166
|
+
async getUserRepostFeed (userId, filter, limit = 100, offset = 0, withUsers = false) {
|
|
167
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
168
|
+
return this.discoveryProvider.getUserRepostFeed(userId, filter, limit, offset, withUsers)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Return social feed for current user
|
|
173
|
+
* @param {number} limit - max # of items to return
|
|
174
|
+
* @param {filter} string - filter by "all", "original", or "repost"
|
|
175
|
+
* @param {number} offset - offset into list to return from (for pagination)
|
|
176
|
+
* @returns {Object} {Array of track and playlist metadata objects}
|
|
177
|
+
* additional metadata fields on track and playlist objects:
|
|
178
|
+
* {String} activity_timestamp - timestamp of requested user's repost for given track or playlist,
|
|
179
|
+
* used for sorting feed
|
|
180
|
+
* {Integer} repost_count - repost count of given track/playlist
|
|
181
|
+
* {Integer} save_count - save count of given track/playlist
|
|
182
|
+
* {Boolean} has_current_user_reposted - has current user reposted given track/playlist
|
|
183
|
+
* {Array} followee_reposts - followees of current user that have reposted given track/playlist
|
|
184
|
+
*/
|
|
185
|
+
async getSocialFeed (filter, limit = 100, offset = 0, withUsers = false, tracksOnly = false) {
|
|
186
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
187
|
+
const owner = this.userStateManager.getCurrentUser()
|
|
188
|
+
if (owner) {
|
|
189
|
+
return this.discoveryProvider.getSocialFeed(filter, limit, offset, withUsers, tracksOnly)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return []
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Returns the top users for the specified genres
|
|
197
|
+
* @param {number} limit - max # of items to return
|
|
198
|
+
* @param {number} offset - offset into list to return from (for pagination)
|
|
199
|
+
* @param {Object} {Array of genres} - filter by genres ie. "Rock", "Alternative"
|
|
200
|
+
* @param {Boolean} with_users - If the userIds should be returned or the full user metadata
|
|
201
|
+
* @returns {Object} {Array of user objects if with_users set, else array of userIds}
|
|
202
|
+
*/
|
|
203
|
+
async getTopCreatorsByGenres (genres, limit = 30, offset = 0, withUsers = false) {
|
|
204
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
205
|
+
return this.discoveryProvider.getTopCreatorsByGenres(genres, limit, offset, withUsers)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* ------- SETTERS ------- */
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Assigns a replica set to the user's metadata and adds new metadata to chain.
|
|
212
|
+
* This creates a record for that user on the connected creator node.
|
|
213
|
+
* @param {Object} param
|
|
214
|
+
* @param {number} param.userId
|
|
215
|
+
*/
|
|
216
|
+
async assignReplicaSet ({
|
|
217
|
+
userId
|
|
218
|
+
}) {
|
|
219
|
+
this.REQUIRES(Services.CREATOR_NODE)
|
|
220
|
+
const phases = {
|
|
221
|
+
CLEAN_AND_VALIDATE_METADATA: 'CLEAN_AND_VALIDATE_METADATA',
|
|
222
|
+
AUTOSELECT_CONTENT_NODES: 'AUTOSELECT_CONTENT_NODES',
|
|
223
|
+
SYNC_ACROSS_CONTENT_NODES: 'SYNC_ACROSS_CONTENT_NODES',
|
|
224
|
+
SET_PRIMARY: 'SET_PRIMARY',
|
|
225
|
+
UPLOAD_METADATA_AND_UPDATE_ON_CHAIN: 'UPLOAD_METADATA_AND_UPDATE_ON_CHAIN'
|
|
226
|
+
}
|
|
227
|
+
let phase = ''
|
|
228
|
+
|
|
229
|
+
const logPrefix = `[User:assignReplicaSet()] [userId: ${userId}]`
|
|
230
|
+
const fnStartMs = Date.now()
|
|
231
|
+
let startMs = fnStartMs
|
|
232
|
+
|
|
233
|
+
const user = this.userStateManager.getCurrentUser()
|
|
234
|
+
// Failed the addUser() step
|
|
235
|
+
if (!user) { throw new Error('No current user') }
|
|
236
|
+
// No-op if the user already has a replica set assigned under creator_node_endpoint
|
|
237
|
+
if (user.creator_node_endpoint && user.creator_node_endpoint.length > 0) return
|
|
238
|
+
|
|
239
|
+
// The new metadata object that will contain the replica set
|
|
240
|
+
const newMetadata = { ...user }
|
|
241
|
+
try {
|
|
242
|
+
// Create starter metadata and validate
|
|
243
|
+
phase = phases.CLEAN_AND_VALIDATE_METADATA
|
|
244
|
+
|
|
245
|
+
// Autoselect a new replica set and update the metadata object with new content node endpoints
|
|
246
|
+
phase = phases.AUTOSELECT_CONTENT_NODES
|
|
247
|
+
const response = await this.ServiceProvider.autoSelectCreatorNodes({
|
|
248
|
+
performSyncCheck: false,
|
|
249
|
+
preferHigherPatchForPrimary: this.preferHigherPatchForPrimary,
|
|
250
|
+
preferHigherPatchForSecondaries: this.preferHigherPatchForSecondaries
|
|
251
|
+
})
|
|
252
|
+
console.log(`${logPrefix} [phase: ${phase}] ServiceProvider.autoSelectCreatorNodes() completed in ${Date.now() - startMs}ms`)
|
|
253
|
+
startMs = Date.now()
|
|
254
|
+
|
|
255
|
+
// Ideally, 1 primary and n-1 secondaries are chosen. The best-worst case scenario is that at least 1 primary
|
|
256
|
+
// is chosen. If a primary was not selected (which also implies that secondaries were not chosen), throw
|
|
257
|
+
// an error.
|
|
258
|
+
const { primary, secondaries } = response
|
|
259
|
+
if (!primary) {
|
|
260
|
+
throw new Error('Could not select a primary.')
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const newContentNodeEndpoints = CreatorNode.buildEndpoint(primary, secondaries)
|
|
264
|
+
newMetadata.creator_node_endpoint = newContentNodeEndpoints
|
|
265
|
+
|
|
266
|
+
// Update the new primary to the auto-selected primary
|
|
267
|
+
phase = phases.SET_PRIMARY
|
|
268
|
+
await this.creatorNode.setEndpoint(primary)
|
|
269
|
+
|
|
270
|
+
// Update metadata in CN and on chain of newly assigned replica set
|
|
271
|
+
phase = phases.UPLOAD_METADATA_AND_UPDATE_ON_CHAIN
|
|
272
|
+
await this.updateAndUploadMetadata({
|
|
273
|
+
newMetadata,
|
|
274
|
+
userId
|
|
275
|
+
})
|
|
276
|
+
console.log(`${logPrefix} [phase: ${phase}] updateAndUploadMetadata() completed in ${Date.now() - startMs}ms`)
|
|
277
|
+
|
|
278
|
+
console.log(`${logPrefix} completed in ${Date.now() - fnStartMs}ms`)
|
|
279
|
+
} catch (e) {
|
|
280
|
+
const errorMsg = `assignReplicaSet() Error -- Phase ${phase} in ${Date.now() - fnStartMs}ms: ${e}`
|
|
281
|
+
console.log(errorMsg)
|
|
282
|
+
throw new Error(errorMsg)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return newMetadata
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Util to upload profile picture and cover photo images and update
|
|
290
|
+
* a metadata object. This method inherently calls triggerSecondarySyncs().
|
|
291
|
+
* @param {?File} profilePictureFile an optional file to upload as the profile picture
|
|
292
|
+
* @param {?File} coverPhotoFile an optional file to upload as the cover photo
|
|
293
|
+
* @param {Object} metadata to update
|
|
294
|
+
* @returns {Object} the passed in metadata object with profile_picture_sizes and cover_photo_sizes fields added
|
|
295
|
+
*/
|
|
296
|
+
async uploadProfileImages (profilePictureFile, coverPhotoFile, metadata) {
|
|
297
|
+
let didMetadataUpdate = false
|
|
298
|
+
if (profilePictureFile) {
|
|
299
|
+
const resp = await this.creatorNode.uploadImage(profilePictureFile, true)
|
|
300
|
+
metadata.profile_picture_sizes = resp.dirCID
|
|
301
|
+
didMetadataUpdate = true
|
|
302
|
+
}
|
|
303
|
+
if (coverPhotoFile) {
|
|
304
|
+
const resp = await this.creatorNode.uploadImage(coverPhotoFile, false)
|
|
305
|
+
metadata.cover_photo_sizes = resp.dirCID
|
|
306
|
+
didMetadataUpdate = true
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (didMetadataUpdate) {
|
|
310
|
+
await this.updateAndUploadMetadata({
|
|
311
|
+
newMetadata: metadata,
|
|
312
|
+
userId: metadata.user_id
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return metadata
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Create an on-chain non-creator user. Some fields are restricted (ex.
|
|
321
|
+
* creator_node_endpoint); this should error if the metadata given attempts to set them.
|
|
322
|
+
* @param {Object} metadata metadata to associate with the user
|
|
323
|
+
*/
|
|
324
|
+
async addUser (metadata) {
|
|
325
|
+
this.IS_OBJECT(metadata)
|
|
326
|
+
const newMetadata = this.cleanUserMetadata(metadata)
|
|
327
|
+
this._validateUserMetadata(newMetadata)
|
|
328
|
+
|
|
329
|
+
let userId
|
|
330
|
+
const currentUser = this.userStateManager.getCurrentUser()
|
|
331
|
+
if (currentUser && currentUser.handle) {
|
|
332
|
+
userId = currentUser.user_id
|
|
333
|
+
} else {
|
|
334
|
+
userId = (await this.contracts.UserFactoryClient.addUser(newMetadata.handle)).userId
|
|
335
|
+
}
|
|
336
|
+
const { latestBlockHash: blockHash, latestBlockNumber: blockNumber } = await this._addUserOperations(
|
|
337
|
+
userId, newMetadata
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
newMetadata.wallet = this.web3Manager.getWalletAddress()
|
|
341
|
+
newMetadata.user_id = userId
|
|
342
|
+
|
|
343
|
+
this.userStateManager.setCurrentUser({
|
|
344
|
+
...newMetadata,
|
|
345
|
+
// Initialize counts to be 0. We don't want to write this data to backends ever really
|
|
346
|
+
// (hence the cleanUserMetadata above), but we do want to make sure clients
|
|
347
|
+
// can properly "do math" on these numbers.
|
|
348
|
+
followee_count: 0,
|
|
349
|
+
follower_count: 0,
|
|
350
|
+
repost_count: 0
|
|
351
|
+
})
|
|
352
|
+
return { blockHash, blockNumber, userId }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Updates a user
|
|
357
|
+
* @param {number} userId
|
|
358
|
+
* @param {Object} metadata
|
|
359
|
+
*/
|
|
360
|
+
async updateUser (userId, metadata) {
|
|
361
|
+
this.REQUIRES(Services.DISCOVERY_PROVIDER)
|
|
362
|
+
this.IS_OBJECT(metadata)
|
|
363
|
+
const newMetadata = this.cleanUserMetadata(metadata)
|
|
364
|
+
this._validateUserMetadata(newMetadata)
|
|
365
|
+
|
|
366
|
+
// Retrieve the current user metadata
|
|
367
|
+
const users = await this.discoveryProvider.getUsers(1, 0, [userId], null, null, false, null)
|
|
368
|
+
if (!users || !users[0]) throw new Error(`Cannot update user because no current record exists for user id ${userId}`)
|
|
369
|
+
|
|
370
|
+
const oldMetadata = users[0]
|
|
371
|
+
const { latestBlockHash: blockHash, latestBlockNumber: blockNumber } = await this._updateUserOperations(
|
|
372
|
+
newMetadata, oldMetadata, userId
|
|
373
|
+
)
|
|
374
|
+
this.userStateManager.setCurrentUser({ ...oldMetadata, ...newMetadata })
|
|
375
|
+
return { blockHash, blockNumber }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Updates a creator (updates their data on the creator node)
|
|
380
|
+
* @param {number} userId
|
|
381
|
+
* @param {Object} metadata
|
|
382
|
+
*/
|
|
383
|
+
async updateCreator (userId, metadata) {
|
|
384
|
+
this.REQUIRES(Services.CREATOR_NODE, Services.DISCOVERY_PROVIDER)
|
|
385
|
+
this.IS_OBJECT(metadata)
|
|
386
|
+
const newMetadata = this.cleanUserMetadata(metadata)
|
|
387
|
+
this._validateUserMetadata(newMetadata)
|
|
388
|
+
|
|
389
|
+
const logPrefix = `[User:updateCreator()] [userId: ${userId}]`
|
|
390
|
+
const fnStartMs = Date.now()
|
|
391
|
+
let startMs = fnStartMs
|
|
392
|
+
|
|
393
|
+
// Error if libs instance does not already have existing user state
|
|
394
|
+
const user = this.userStateManager.getCurrentUser()
|
|
395
|
+
if (!user) {
|
|
396
|
+
throw new Error('No current user')
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Ensure libs is connected to correct CN
|
|
400
|
+
if (this.creatorNode.getEndpoint() !== CreatorNode.getPrimary(newMetadata.creator_node_endpoint)) {
|
|
401
|
+
throw new Error(`Not connected to correct content node. Expected ${CreatorNode.getPrimary(newMetadata.creator_node_endpoint)}, got ${this.creatorNode.getEndpoint()}`)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Preserve old metadata object
|
|
405
|
+
const oldMetadata = { ...user }
|
|
406
|
+
|
|
407
|
+
// Update user creator_node_endpoint on chain if applicable
|
|
408
|
+
let updateEndpointTxBlockNumber = null
|
|
409
|
+
if (newMetadata.creator_node_endpoint !== oldMetadata.creator_node_endpoint) {
|
|
410
|
+
// Perform update to new contract
|
|
411
|
+
startMs = Date.now()
|
|
412
|
+
const {
|
|
413
|
+
txReceipt: updateEndpointTxReceipt, replicaSetSPIDs
|
|
414
|
+
} = await this._updateReplicaSetOnChain(userId, newMetadata.creator_node_endpoint)
|
|
415
|
+
updateEndpointTxBlockNumber = updateEndpointTxReceipt.blockNumber
|
|
416
|
+
console.log(`${logPrefix} _updateReplicaSetOnChain() completed in ${Date.now() - startMs}ms`)
|
|
417
|
+
startMs = Date.now()
|
|
418
|
+
|
|
419
|
+
await this._waitForURSMCreatorNodeEndpointIndexing(userId, replicaSetSPIDs)
|
|
420
|
+
console.log(`${logPrefix} _waitForURSMCreatorNodeEndpointIndexing() completed in ${Date.now() - startMs}ms`)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Upload new metadata object to CN
|
|
424
|
+
const { metadataMultihash, metadataFileUUID } = await this.creatorNode.uploadCreatorContent(newMetadata, updateEndpointTxBlockNumber)
|
|
425
|
+
|
|
426
|
+
// Write metadata multihash to chain
|
|
427
|
+
const updatedMultihashDecoded = Utils.decodeMultihash(metadataMultihash)
|
|
428
|
+
const { txReceipt } = await this.contracts.UserFactoryClient.updateMultihash(userId, updatedMultihashDecoded.digest)
|
|
429
|
+
|
|
430
|
+
// Write remaining metadata fields to chain
|
|
431
|
+
let { latestBlockHash, latestBlockNumber } = await this._updateUserOperations(
|
|
432
|
+
newMetadata, oldMetadata, userId
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
// Write to CN to associate blockchain user id with updated metadata and block number
|
|
436
|
+
await this.creatorNode.associateCreator(userId, metadataFileUUID, Math.max(txReceipt.blockNumber, latestBlockNumber))
|
|
437
|
+
|
|
438
|
+
// Update libs instance with new user metadata object
|
|
439
|
+
this.userStateManager.setCurrentUser({ ...oldMetadata, ...newMetadata })
|
|
440
|
+
|
|
441
|
+
if (!latestBlockHash || !latestBlockNumber) {
|
|
442
|
+
latestBlockHash = txReceipt.blockHash
|
|
443
|
+
latestBlockNumber = txReceipt.blockNumber
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return { blockHash: latestBlockHash, blockNumber: latestBlockNumber, userId }
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Upgrades a user to a creator using their metadata object.
|
|
451
|
+
* This creates a record for that user on the connected creator node.
|
|
452
|
+
* @param {string} existingEndpoint
|
|
453
|
+
* @param {string} newCreatorNodeEndpoint comma delineated
|
|
454
|
+
*/
|
|
455
|
+
async upgradeToCreator (existingEndpoint, newCreatorNodeEndpoint) {
|
|
456
|
+
this.REQUIRES(Services.CREATOR_NODE)
|
|
457
|
+
|
|
458
|
+
// Error if libs instance does not already have existing user state
|
|
459
|
+
const user = this.userStateManager.getCurrentUser()
|
|
460
|
+
if (!user) {
|
|
461
|
+
throw new Error('No current user')
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// No-op if the user is already a creator.
|
|
465
|
+
// Consider them a creator iff they have is_creator=true AND a creator node endpoint
|
|
466
|
+
if (user.is_creator && user.creator_node_endpoint) return
|
|
467
|
+
|
|
468
|
+
const userId = user.user_id
|
|
469
|
+
const oldMetadata = { ...user }
|
|
470
|
+
|
|
471
|
+
const logPrefix = `[User:upgradeToCreator()] [userId: ${userId}]`
|
|
472
|
+
const fnStartMs = Date.now()
|
|
473
|
+
let startMs = fnStartMs
|
|
474
|
+
|
|
475
|
+
// Clean and validate metadata
|
|
476
|
+
const newMetadata = this.cleanUserMetadata({ ...user })
|
|
477
|
+
this._validateUserMetadata(newMetadata)
|
|
478
|
+
|
|
479
|
+
// Populate metadata with required fields - wallet, is_creator, creator_node_endpoint
|
|
480
|
+
newMetadata.wallet = this.web3Manager.getWalletAddress()
|
|
481
|
+
newMetadata.is_creator = true
|
|
482
|
+
|
|
483
|
+
let updateEndpointTxBlockNumber = null
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* If there is no creator_node_endpoint field or if newCreatorNodeEndpoint is not the same as the existing
|
|
487
|
+
* metadata creator_node_endpoint field value, update the field with newCreatorNodeEndpoint.
|
|
488
|
+
* This is because new users on signup will now be assigned a replica set, and do not need to
|
|
489
|
+
* be assigned a new one via newCreatorNodeEndpoint.
|
|
490
|
+
*/
|
|
491
|
+
if (
|
|
492
|
+
!oldMetadata.creator_node_endpoint ||
|
|
493
|
+
oldMetadata.creator_node_endpoint !== newCreatorNodeEndpoint
|
|
494
|
+
) {
|
|
495
|
+
newMetadata.creator_node_endpoint = newCreatorNodeEndpoint
|
|
496
|
+
const newPrimary = CreatorNode.getPrimary(newCreatorNodeEndpoint)
|
|
497
|
+
|
|
498
|
+
// Sync user data from old primary to new endpoint
|
|
499
|
+
if (existingEndpoint) {
|
|
500
|
+
// Don't validate what we're syncing from because the user isn't
|
|
501
|
+
// a creator yet.
|
|
502
|
+
await this.creatorNode.syncSecondary(
|
|
503
|
+
newPrimary,
|
|
504
|
+
existingEndpoint,
|
|
505
|
+
/* immediate= */ true,
|
|
506
|
+
/* validate= */ false
|
|
507
|
+
)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Update local libs state with new CN endpoint
|
|
511
|
+
await this.creatorNode.setEndpoint(newPrimary)
|
|
512
|
+
|
|
513
|
+
// Update user creator_node_endpoint on chain if applicable
|
|
514
|
+
startMs = Date.now()
|
|
515
|
+
const {
|
|
516
|
+
txReceipt: updateEndpointTxReceipt, replicaSetSPIDs
|
|
517
|
+
} = await this._updateReplicaSetOnChain(userId, newMetadata.creator_node_endpoint)
|
|
518
|
+
updateEndpointTxBlockNumber = updateEndpointTxReceipt.blockNumber
|
|
519
|
+
console.log(`${logPrefix} _updateReplicaSetOnChain() completed in ${Date.now() - startMs}ms`)
|
|
520
|
+
startMs = Date.now()
|
|
521
|
+
|
|
522
|
+
await this._waitForURSMCreatorNodeEndpointIndexing(userId, replicaSetSPIDs)
|
|
523
|
+
console.log(`${logPrefix} _waitForURSMCreatorNodeEndpointIndexing() completed in ${Date.now() - startMs}ms`)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Upload new metadata object to CN
|
|
527
|
+
const { metadataMultihash, metadataFileUUID } = await this.creatorNode.uploadCreatorContent(newMetadata, updateEndpointTxBlockNumber)
|
|
528
|
+
|
|
529
|
+
// Write metadata multihash to chain
|
|
530
|
+
const updatedMultihashDecoded = Utils.decodeMultihash(metadataMultihash)
|
|
531
|
+
const { txReceipt } = await this.contracts.UserFactoryClient.updateMultihash(userId, updatedMultihashDecoded.digest)
|
|
532
|
+
|
|
533
|
+
// Write remaining metadata fields to chain
|
|
534
|
+
const { latestBlockNumber } = await this._updateUserOperations(newMetadata, oldMetadata, userId)
|
|
535
|
+
|
|
536
|
+
// Write to CN to associate blockchain user id with updated metadata and block number
|
|
537
|
+
await this.creatorNode.associateCreator(userId, metadataFileUUID, Math.max(txReceipt.blockNumber, latestBlockNumber))
|
|
538
|
+
|
|
539
|
+
// Update libs instance with new user metadata object
|
|
540
|
+
this.userStateManager.setCurrentUser({ ...oldMetadata, ...newMetadata })
|
|
541
|
+
|
|
542
|
+
return userId
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Updates a user on whether they are verified on Audius
|
|
547
|
+
* @param {number} userId
|
|
548
|
+
* @param {boolean} isVerified
|
|
549
|
+
*/
|
|
550
|
+
async updateIsVerified (userId, isVerified, privateKey) {
|
|
551
|
+
return this.contracts.UserFactoryClient.updateIsVerified(userId, isVerified, privateKey)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Adds a user follow for a given follower and followee
|
|
556
|
+
* @param {number} followerUserId who is following
|
|
557
|
+
* @param {number} followeeUserId who is being followed...
|
|
558
|
+
*/
|
|
559
|
+
async addUserFollow (followeeUserId) {
|
|
560
|
+
const followerUserId = this.userStateManager.getCurrentUserId()
|
|
561
|
+
return this.contracts.SocialFeatureFactoryClient.addUserFollow(followerUserId, followeeUserId)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Deletes a user follow for a given follower and followee
|
|
566
|
+
* @param {number} followerUserId who is no longer following
|
|
567
|
+
* @param {number} followeeUserId who is no longer being followed...
|
|
568
|
+
*/
|
|
569
|
+
async deleteUserFollow (followeeUserId) {
|
|
570
|
+
const followerUserId = this.userStateManager.getCurrentUserId()
|
|
571
|
+
return this.contracts.SocialFeatureFactoryClient.deleteUserFollow(followerUserId, followeeUserId)
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Gets the clock status for user in userStateManager across replica set.
|
|
576
|
+
*/
|
|
577
|
+
async getClockValuesFromReplicaSet () {
|
|
578
|
+
return this.creatorNode.getClockValuesFromReplicaSet()
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* ------- PRIVATE ------- */
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* 1. Uploads metadata to primary Content Node (which inherently calls a sync accross secondaries)
|
|
585
|
+
* 2. Updates metadata on chain
|
|
586
|
+
* @param {Object} param
|
|
587
|
+
* @param {Object} param.newMetadata new metadata object
|
|
588
|
+
* @param {number} param.userId
|
|
589
|
+
*/
|
|
590
|
+
async updateAndUploadMetadata ({ newMetadata, userId }) {
|
|
591
|
+
this.REQUIRES(Services.CREATOR_NODE, Services.DISCOVERY_PROVIDER)
|
|
592
|
+
this.IS_OBJECT(newMetadata)
|
|
593
|
+
const phases = {
|
|
594
|
+
UPDATE_CONTENT_NODE_ENDPOINT_ON_CHAIN: 'UPDATE_CONTENT_NODE_ENDPOINT_ON_CHAIN',
|
|
595
|
+
UPLOAD_METADATA: 'UPLOAD_METADATA',
|
|
596
|
+
UPDATE_METADATA_ON_CHAIN: 'UPDATE_METADATA_ON_CHAIN',
|
|
597
|
+
UPDATE_USER_ON_CHAIN_OPS: 'UPDATE_USER_ON_CHAIN_OPS',
|
|
598
|
+
ASSOCIATE_USER: 'ASSOCIATE_USER'
|
|
599
|
+
}
|
|
600
|
+
let phase = ''
|
|
601
|
+
|
|
602
|
+
const oldMetadata = this.userStateManager.getCurrentUser()
|
|
603
|
+
if (!oldMetadata) { throw new Error('No current user.') }
|
|
604
|
+
|
|
605
|
+
newMetadata = this.cleanUserMetadata(newMetadata)
|
|
606
|
+
this._validateUserMetadata(newMetadata)
|
|
607
|
+
|
|
608
|
+
const logPrefix = `[User:updateAndUploadMetadata()] [userId: ${userId}]`
|
|
609
|
+
const fnStartMs = Date.now()
|
|
610
|
+
let startMs = fnStartMs
|
|
611
|
+
|
|
612
|
+
try {
|
|
613
|
+
// Update user creator_node_endpoint on chain if applicable
|
|
614
|
+
if (newMetadata.creator_node_endpoint !== oldMetadata.creator_node_endpoint) {
|
|
615
|
+
phase = phases.UPDATE_CONTENT_NODE_ENDPOINT_ON_CHAIN
|
|
616
|
+
const { replicaSetSPIDs } = await this._updateReplicaSetOnChain(userId, newMetadata.creator_node_endpoint)
|
|
617
|
+
console.log(`${logPrefix} [phase: ${phase}] _updateReplicaSetOnChain() completed in ${Date.now() - startMs}ms`)
|
|
618
|
+
startMs = Date.now()
|
|
619
|
+
|
|
620
|
+
await this._waitForURSMCreatorNodeEndpointIndexing(userId, replicaSetSPIDs)
|
|
621
|
+
console.log(`${logPrefix} [phase: ${phase}] _waitForURSMCreatorNodeEndpointIndexing() completed in ${Date.now() - startMs}ms`)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Upload new metadata object to CN
|
|
625
|
+
phase = phases.UPLOAD_METADATA
|
|
626
|
+
const { metadataMultihash, metadataFileUUID } = await this.creatorNode.uploadCreatorContent(newMetadata)
|
|
627
|
+
console.log(`${logPrefix} [phase: ${phase}] creatorNode.uploadCreatorContent() completed in ${Date.now() - startMs}ms`)
|
|
628
|
+
startMs = Date.now()
|
|
629
|
+
|
|
630
|
+
// Write metadata multihash to chain
|
|
631
|
+
phase = phases.UPDATE_METADATA_ON_CHAIN
|
|
632
|
+
const updatedMultihashDecoded = Utils.decodeMultihash(metadataMultihash)
|
|
633
|
+
const { txReceipt } = await this.contracts.UserFactoryClient.updateMultihash(userId, updatedMultihashDecoded.digest)
|
|
634
|
+
console.log(`${logPrefix} [phase: ${phase}] UserFactoryClient.updateMultihash() completed in ${Date.now() - startMs}ms`)
|
|
635
|
+
startMs = Date.now()
|
|
636
|
+
|
|
637
|
+
// Write remaining metadata fields to chain
|
|
638
|
+
phase = phases.UPDATE_USER_ON_CHAIN_OPS
|
|
639
|
+
const { latestBlockNumber } = await this._updateUserOperations(newMetadata, oldMetadata, userId, ['creator_node_endpoint'])
|
|
640
|
+
console.log(`${logPrefix} [phase: ${phase}] _updateUserOperations() completed in ${Date.now() - startMs}ms`)
|
|
641
|
+
startMs = Date.now()
|
|
642
|
+
|
|
643
|
+
// Write to CN to associate blockchain user id with updated metadata and block number
|
|
644
|
+
phase = phases.ASSOCIATE_USER
|
|
645
|
+
await this.creatorNode.associateCreator(userId, metadataFileUUID, Math.max(txReceipt.blockNumber, latestBlockNumber))
|
|
646
|
+
console.log(`${logPrefix} [phase: ${phase}] creatorNode.associateCreator() completed in ${Date.now() - startMs}ms`)
|
|
647
|
+
startMs = Date.now()
|
|
648
|
+
|
|
649
|
+
// Update libs instance with new user metadata object
|
|
650
|
+
this.userStateManager.setCurrentUser({ ...oldMetadata, ...newMetadata })
|
|
651
|
+
|
|
652
|
+
console.log(`${logPrefix} completed in ${Date.now() - fnStartMs}ms`)
|
|
653
|
+
} catch (e) {
|
|
654
|
+
// TODO: think about handling the update metadata on chain and associating..
|
|
655
|
+
const errorMsg = `updateAndUploadMetadata() Error -- Phase ${phase} in ${Date.now() - fnStartMs}ms: ${e}`
|
|
656
|
+
console.log(errorMsg)
|
|
657
|
+
throw new Error(errorMsg)
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* If a user's creator_node_endpoint is null, assign a replica set.
|
|
663
|
+
* Used during the sanity check and in uploadImage() in files.js
|
|
664
|
+
*/
|
|
665
|
+
async assignReplicaSetIfNecessary () {
|
|
666
|
+
const user = this.userStateManager.getCurrentUser()
|
|
667
|
+
|
|
668
|
+
// If no user is logged in, or a creator node endpoint is already assigned,
|
|
669
|
+
// skip this call
|
|
670
|
+
if (!user || user.creator_node_endpoint) return
|
|
671
|
+
|
|
672
|
+
// Generate a replica set and assign to user
|
|
673
|
+
try {
|
|
674
|
+
await this.assignReplicaSet({ userId: user.user_id })
|
|
675
|
+
} catch (e) {
|
|
676
|
+
throw new Error(`assignReplicaSetIfNecessary error - ${e.toString()}`)
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/** Waits for a discovery provider to confirm that a creator node endpoint is updated. */
|
|
681
|
+
async _waitForCreatorNodeEndpointIndexing (userId, creatorNodeEndpoint) {
|
|
682
|
+
while (true) {
|
|
683
|
+
const userList = await this.discoveryProvider.getUsers(1, 0, [userId])
|
|
684
|
+
if (userList) {
|
|
685
|
+
const user = userList[0]
|
|
686
|
+
if (user && user.creator_node_endpoint === creatorNodeEndpoint) {
|
|
687
|
+
break
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
await Utils.wait(500)
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async _waitForURSMCreatorNodeEndpointIndexing (userId, replicaSetSPIDs, timeoutMs = 60000) {
|
|
696
|
+
const asyncFn = async () => {
|
|
697
|
+
while (true) {
|
|
698
|
+
const replicaSet = await this.contracts.UserReplicaSetManagerClient.getUserReplicaSet(userId)
|
|
699
|
+
if (
|
|
700
|
+
replicaSet &&
|
|
701
|
+
Object.prototype.hasOwnProperty.call(replicaSet, 'primaryId') &&
|
|
702
|
+
Object.prototype.hasOwnProperty.call(replicaSet, 'secondaryIds') &&
|
|
703
|
+
replicaSet.primaryId === replicaSetSPIDs[0] &&
|
|
704
|
+
isEqual(replicaSet.secondaryIds, replicaSetSPIDs.slice(1, 3))
|
|
705
|
+
) {
|
|
706
|
+
break
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
await Utils.wait(500)
|
|
710
|
+
}
|
|
711
|
+
await Utils.racePromiseWithTimeout(
|
|
712
|
+
asyncFn(),
|
|
713
|
+
timeoutMs,
|
|
714
|
+
`[User:_waitForURSMCreatorNodeEndpointIndexing()] Timeout error after ${timeoutMs}ms`
|
|
715
|
+
)
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
async _addUserOperations (userId, newMetadata, exclude = []) {
|
|
719
|
+
const addOps = []
|
|
720
|
+
|
|
721
|
+
// Remove excluded keys from metadata object
|
|
722
|
+
const metadata = { ...newMetadata }
|
|
723
|
+
exclude.map(excludedKey => delete metadata[excludedKey])
|
|
724
|
+
|
|
725
|
+
if (metadata[USER_PROP_NAME_CONSTANTS.NAME]) {
|
|
726
|
+
addOps.push(this.contracts.UserFactoryClient.updateName(userId, metadata[USER_PROP_NAME_CONSTANTS.NAME]))
|
|
727
|
+
}
|
|
728
|
+
if (metadata[USER_PROP_NAME_CONSTANTS.LOCATION]) {
|
|
729
|
+
addOps.push(this.contracts.UserFactoryClient.updateLocation(userId, metadata[USER_PROP_NAME_CONSTANTS.LOCATION]))
|
|
730
|
+
}
|
|
731
|
+
if (metadata[USER_PROP_NAME_CONSTANTS.BIO]) {
|
|
732
|
+
addOps.push(this.contracts.UserFactoryClient.updateBio(userId, metadata[USER_PROP_NAME_CONSTANTS.BIO]))
|
|
733
|
+
}
|
|
734
|
+
if (metadata[USER_PROP_NAME_CONSTANTS.PROFILE_PICTURE_SIZES]) {
|
|
735
|
+
addOps.push(this.contracts.UserFactoryClient.updateProfilePhoto(
|
|
736
|
+
userId,
|
|
737
|
+
Utils.decodeMultihash(metadata[USER_PROP_NAME_CONSTANTS.PROFILE_PICTURE_SIZES]).digest
|
|
738
|
+
))
|
|
739
|
+
}
|
|
740
|
+
if (metadata[USER_PROP_NAME_CONSTANTS.COVER_PHOTO_SIZES]) {
|
|
741
|
+
addOps.push(this.contracts.UserFactoryClient.updateCoverPhoto(
|
|
742
|
+
userId,
|
|
743
|
+
Utils.decodeMultihash(metadata[USER_PROP_NAME_CONSTANTS.COVER_PHOTO_SIZES]).digest
|
|
744
|
+
))
|
|
745
|
+
}
|
|
746
|
+
if (metadata[USER_PROP_NAME_CONSTANTS.IS_CREATOR]) {
|
|
747
|
+
addOps.push(this.contracts.UserFactoryClient.updateIsCreator(userId, metadata[USER_PROP_NAME_CONSTANTS.IS_CREATOR]))
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
let ops; let latestBlockNumber = -Infinity; let latestBlockHash
|
|
751
|
+
if (addOps.length > 0) {
|
|
752
|
+
// Execute update promises concurrently
|
|
753
|
+
// TODO - what if one or more of these fails?
|
|
754
|
+
// sort transactions by blocknumber and return most recent transaction
|
|
755
|
+
ops = await Promise.all(addOps)
|
|
756
|
+
const sortedOpsDesc = ops.sort((op1, op2) => op2.txReceipt.blockNumber - op1.txReceipt.blockNumber)
|
|
757
|
+
const latestTx = sortedOpsDesc[0].txReceipt
|
|
758
|
+
latestBlockNumber = latestTx.blockNumber
|
|
759
|
+
latestBlockHash = latestTx.blockHash
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return { ops, latestBlockNumber, latestBlockHash }
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
async _updateUserOperations (newMetadata, currentMetadata, userId, exclude = []) {
|
|
766
|
+
const updateOps = []
|
|
767
|
+
|
|
768
|
+
// Remove excluded keys from metadata object
|
|
769
|
+
const metadata = { ...newMetadata }
|
|
770
|
+
exclude.map(excludedKey => delete metadata[excludedKey])
|
|
771
|
+
// Compare the existing metadata with the new values and conditionally
|
|
772
|
+
// perform update operations
|
|
773
|
+
for (const key in metadata) {
|
|
774
|
+
if (Object.prototype.hasOwnProperty.call(metadata, key) && Object.prototype.hasOwnProperty.call(currentMetadata, key) && metadata[key] !== currentMetadata[key]) {
|
|
775
|
+
if (key === USER_PROP_NAME_CONSTANTS.NAME) {
|
|
776
|
+
updateOps.push(this.contracts.UserFactoryClient.updateName(userId, metadata[USER_PROP_NAME_CONSTANTS.NAME]))
|
|
777
|
+
}
|
|
778
|
+
if (key === USER_PROP_NAME_CONSTANTS.IS_CREATOR) {
|
|
779
|
+
updateOps.push(this.contracts.UserFactoryClient.updateIsCreator(userId, metadata[USER_PROP_NAME_CONSTANTS.IS_CREATOR]))
|
|
780
|
+
}
|
|
781
|
+
if (key === USER_PROP_NAME_CONSTANTS.BIO) {
|
|
782
|
+
updateOps.push(this.contracts.UserFactoryClient.updateBio(userId, metadata[USER_PROP_NAME_CONSTANTS.BIO]))
|
|
783
|
+
}
|
|
784
|
+
if (key === USER_PROP_NAME_CONSTANTS.LOCATION) {
|
|
785
|
+
updateOps.push(this.contracts.UserFactoryClient.updateLocation(userId, metadata[USER_PROP_NAME_CONSTANTS.LOCATION]))
|
|
786
|
+
}
|
|
787
|
+
if (key === USER_PROP_NAME_CONSTANTS.PROFILE_PICTURE_SIZES) {
|
|
788
|
+
updateOps.push(this.contracts.UserFactoryClient.updateProfilePhoto(
|
|
789
|
+
userId,
|
|
790
|
+
Utils.decodeMultihash(metadata[USER_PROP_NAME_CONSTANTS.PROFILE_PICTURE_SIZES]).digest
|
|
791
|
+
))
|
|
792
|
+
}
|
|
793
|
+
if (key === USER_PROP_NAME_CONSTANTS.COVER_PHOTO_SIZES) {
|
|
794
|
+
updateOps.push(this.contracts.UserFactoryClient.updateCoverPhoto(
|
|
795
|
+
userId,
|
|
796
|
+
Utils.decodeMultihash(metadata[USER_PROP_NAME_CONSTANTS.COVER_PHOTO_SIZES]).digest
|
|
797
|
+
))
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
let ops; let latestBlockNumber = -Infinity; let latestBlockHash
|
|
803
|
+
if (updateOps.length > 0) {
|
|
804
|
+
// sort transactions by blocknumber and return most recent transaction
|
|
805
|
+
ops = await Promise.all(updateOps)
|
|
806
|
+
const sortedOpsDesc = ops.sort((op1, op2) => op2.txReceipt.blockNumber - op1.txReceipt.blockNumber)
|
|
807
|
+
const latestTx = sortedOpsDesc[0].txReceipt
|
|
808
|
+
latestBlockNumber = latestTx.blockNumber
|
|
809
|
+
latestBlockHash = latestTx.blockHash
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
return { ops, latestBlockNumber, latestBlockHash }
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
_validateUserMetadata (metadata) {
|
|
816
|
+
this.OBJECT_HAS_PROPS(metadata, USER_PROPS, USER_REQUIRED_PROPS)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Metadata object may have extra fields.
|
|
821
|
+
* - Add what user props might be missing to normalize
|
|
822
|
+
* - Only keep core fields in USER_PROPS and 'user_id'.
|
|
823
|
+
*/
|
|
824
|
+
cleanUserMetadata (metadata) {
|
|
825
|
+
USER_PROPS.forEach(prop => {
|
|
826
|
+
if (!(prop in metadata)) { metadata[prop] = null }
|
|
827
|
+
})
|
|
828
|
+
return pick(metadata, USER_PROPS.concat('user_id'))
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Perform replica set update
|
|
832
|
+
// Conditionally write to UserFactory contract, else write to UserReplicaSetManager
|
|
833
|
+
// This behavior is to ensure backwards compatibility prior to contract deploy
|
|
834
|
+
async _updateReplicaSetOnChain (userId, creatorNodeEndpoint) {
|
|
835
|
+
// Attempt to update through UserReplicaSetManagerClient if present
|
|
836
|
+
if (!this.contracts.UserReplicaSetManagerClient) {
|
|
837
|
+
await this.contracts.initUserReplicaSetManagerClient()
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const primaryEndpoint = CreatorNode.getPrimary(creatorNodeEndpoint)
|
|
841
|
+
const secondaries = CreatorNode.getSecondaries(creatorNodeEndpoint)
|
|
842
|
+
|
|
843
|
+
if (secondaries.length < 2) {
|
|
844
|
+
throw new Error(`Invalid number of secondaries found - received ${secondaries}`)
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const [primarySpID, secondary1SpID, secondary2SpID] = await Promise.all([
|
|
848
|
+
this._retrieveSpIDFromEndpoint(primaryEndpoint),
|
|
849
|
+
this._retrieveSpIDFromEndpoint(secondaries[0]),
|
|
850
|
+
this._retrieveSpIDFromEndpoint(secondaries[1])
|
|
851
|
+
])
|
|
852
|
+
|
|
853
|
+
// Update in new contract
|
|
854
|
+
const txReceipt = await this.contracts.UserReplicaSetManagerClient.updateReplicaSet(
|
|
855
|
+
userId,
|
|
856
|
+
primarySpID,
|
|
857
|
+
[secondary1SpID, secondary2SpID]
|
|
858
|
+
)
|
|
859
|
+
const replicaSetSPIDs = [primarySpID, secondary1SpID, secondary2SpID]
|
|
860
|
+
return {
|
|
861
|
+
txReceipt,
|
|
862
|
+
replicaSetSPIDs
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Retrieve cached value for spID from endpoint if present, otherwise fetch from eth web3
|
|
867
|
+
// Any error in the web3 fetch will short circuit the entire operation as expected
|
|
868
|
+
async _retrieveSpIDFromEndpoint (endpoint) {
|
|
869
|
+
const cachedSpID = getSpIDForEndpoint(endpoint)
|
|
870
|
+
let spID = cachedSpID
|
|
871
|
+
if (!spID) {
|
|
872
|
+
const spEndpointInfo = await this.ethContracts.ServiceProviderFactoryClient.getServiceProviderInfoFromEndpoint(
|
|
873
|
+
endpoint
|
|
874
|
+
)
|
|
875
|
+
// Throw if this spID is 0, indicating invalid
|
|
876
|
+
spID = spEndpointInfo.spID
|
|
877
|
+
if (spID === 0) {
|
|
878
|
+
throw new Error(`Failed to find spID for ${endpoint}`)
|
|
879
|
+
}
|
|
880
|
+
// Cache value if it is valid
|
|
881
|
+
setSpIDForEndpoint(endpoint, spID)
|
|
882
|
+
}
|
|
883
|
+
return spID
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
module.exports = Users
|
|
888
|
+
module.exports.USER_PROP_NAME_CONSTANTS = USER_PROP_NAME_CONSTANTS
|