cli.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/env node
  2. const { RuntimeApi } = require('@joystream/runtime-api');
  3. const { encodeAddress } = require('@polkadot/keyring')
  4. const { discover } = require('@joystream/discovery');
  5. const axios = require('axios');
  6. const stripEndingSlash = require('@joystream/util/stripEndingSlash');
  7. (async function main () {
  8. const runtime = await RuntimeApi.create();
  9. const api = runtime.api;
  10. // get current blockheight
  11. const currentHeader = await api.rpc.chain.getHeader();
  12. const currentHeight = currentHeader.number.toBn();
  13. // get all providers
  14. const storageProviders = await api.query.actors.accountIdsByRole(0);
  15. const storageProviderAccountInfos = await Promise.all(storageProviders.map(async (account) => {
  16. return ({
  17. account,
  18. info: await runtime.discovery.getAccountInfo(account),
  19. joined: (await api.query.actors.actorByAccountId(account)).unwrap().joined_at
  20. });
  21. }));
  22. const liveProviders = storageProviderAccountInfos.filter(({account, info}) => {
  23. return info && info.expires_at.gte(currentHeight)
  24. });
  25. const downProviders = storageProviderAccountInfos.filter(({account, info}) => {
  26. return info == null
  27. });
  28. const expiredTtlProviders = storageProviderAccountInfos.filter(({account, info}) => {
  29. return info && currentHeight.gte(info.expires_at)
  30. });
  31. let providersStatuses = mapInfoToStatus(liveProviders, currentHeight);
  32. console.log('\n== Live Providers\n', providersStatuses);
  33. let expiredProviderStatuses = mapInfoToStatus(expiredTtlProviders, currentHeight)
  34. console.log('\n== Expired Providers\n', expiredProviderStatuses);
  35. // check when actor account was created consider grace period before removing
  36. console.log('\n== Down Providers!\n', downProviders.map(provider => {
  37. return ({
  38. account: provider.account.toString(),
  39. age: currentHeight.sub(provider.joined).toNumber()
  40. })
  41. }));
  42. // Resolve IPNS identities of providers
  43. console.log('\nResolving live provider API Endpoints...')
  44. //providersStatuses = providersStatuses.concat(expiredProviderStatuses);
  45. let endpoints = await Promise.all(providersStatuses.map(async (status) => {
  46. try {
  47. let serviceInfo = await discover.discover_over_joystream_discovery_service(status.address, runtime);
  48. let info = JSON.parse(serviceInfo.serialized);
  49. console.log(`${status.address} -> ${info.asset.endpoint}`);
  50. return { address: status.address, endpoint: info.asset.endpoint};
  51. } catch (err) {
  52. console.log('resolve failed', status.address, err.message);
  53. return { address: status.address, endpoint: null};
  54. }
  55. }));
  56. console.log('\nChecking API Endpoint is online')
  57. await Promise.all(endpoints.map(async (provider) => {
  58. if (!provider.endpoint) {
  59. console.log('skipping', provider.address);
  60. return
  61. }
  62. const swaggerUrl = `${stripEndingSlash(provider.endpoint)}/swagger.json`;
  63. let error;
  64. try {
  65. await axios.get(swaggerUrl)
  66. } catch (err) {error = err}
  67. console.log(`${provider.endpoint} - ${error ? error.message : 'OK'}`);
  68. }));
  69. // after resolving for each resolved provider, HTTP HEAD with axios all known content ids
  70. // report available/known
  71. let knownContentIds = await runtime.assets.getKnownContentIds()
  72. console.log(`\nContent Directory has ${knownContentIds.length} assets`);
  73. await Promise.all(knownContentIds.map(async (contentId) => {
  74. let [relationships, judgement] = await assetRelationshipState(api, contentId, storageProviders);
  75. console.log(`${encodeAddress(contentId)} replication ${relationships}/${storageProviders.length} - ${judgement}`);
  76. }));
  77. console.log('\nChecking available assets on providers...');
  78. endpoints.map(async ({address, endpoint}) => {
  79. if (!endpoint) { return }
  80. let { found, content } = await countContentAvailability(knownContentIds, endpoint);
  81. console.log(`${address}: has ${found} assets`);
  82. return content
  83. });
  84. // interesting disconnect doesn't work unless an explicit provider was created
  85. // for underlying api instance
  86. runtime.api.disconnect();
  87. })();
  88. function mapInfoToStatus(providers, currentHeight) {
  89. return providers.map(({account, info, joined}) => {
  90. if (info) {
  91. return {
  92. address: account.toString(),
  93. age: currentHeight.sub(joined).toNumber(),
  94. identity: info.identity.toString(),
  95. expiresIn: info.expires_at.sub(currentHeight).toNumber(),
  96. expired: currentHeight.gte(info.expires_at),
  97. }
  98. } else {
  99. return {
  100. address: account.toString(),
  101. identity: null,
  102. status: 'down'
  103. }
  104. }
  105. })
  106. }
  107. async function countContentAvailability(contentIds, source) {
  108. let content = {}
  109. let found = 0;
  110. for(let i = 0; i < contentIds.length; i++) {
  111. const assetUrl = makeAssetUrl(contentIds[i], source);
  112. try {
  113. let info = await axios.head(assetUrl)
  114. content[encodeAddress(contentIds[i])] = {
  115. type: info.headers['content-type'],
  116. bytes: info.headers['content-length']
  117. }
  118. found++
  119. } catch(err) { console.log(`${assetUrl} ${err.message}`); continue; }
  120. }
  121. console.log(content);
  122. return { found, content };
  123. }
  124. function makeAssetUrl(contentId, source) {
  125. source = stripEndingSlash(source);
  126. return `${source}/asset/v0/${encodeAddress(contentId)}`
  127. }
  128. async function assetRelationshipState(api, contentId, providers) {
  129. let dataObject = await api.query.dataDirectory.dataObjectByContentId(contentId);
  130. // how many relationships out of active providers?
  131. let relationshipIds = await api.query.dataObjectStorageRegistry.relationshipsByContentId(contentId);
  132. let activeRelationships = await Promise.all(relationshipIds.map(async (id) => {
  133. let relationship = await api.query.dataObjectStorageRegistry.relationships(id);
  134. relationship = relationship.unwrap()
  135. return providers.find((provider) => relationship.storage_provider.eq(provider))
  136. }));
  137. return [activeRelationships.filter(active => active).length, dataObject.unwrap().liaison_judgement]
  138. }