From 32e08b045dcc36e2ca2312dfe76f37278d7b0bd7 Mon Sep 17 00:00:00 2001 From: Marina Fedyantceva Date: Tue, 4 Feb 2025 12:19:13 +0300 Subject: [PATCH 1/2] [SPT-818] Added filecoin --- packages/core/src/IPFSMetadataProvider.js | 196 ++++++++++++------ .../core/src/constants/StorageConstants.js | 5 + 2 files changed, 136 insertions(+), 65 deletions(-) create mode 100644 packages/core/src/constants/StorageConstants.js diff --git a/packages/core/src/IPFSMetadataProvider.js b/packages/core/src/IPFSMetadataProvider.js index e19ea6e..cfd84b1 100644 --- a/packages/core/src/IPFSMetadataProvider.js +++ b/packages/core/src/IPFSMetadataProvider.js @@ -3,85 +3,151 @@ import url from 'url'; import RegistryNetworks from 'singularitynet-platform-contracts/networks/Registry.json'; import RegistryAbi from 'singularitynet-platform-contracts/abi/Registry.json'; import { get } from 'axios'; +import { + LIGHTHOUSE_ENDPOINT, + STORAGE_TYPE_FILECOIN, + STORAGE_TYPE_IPFS, + STORAGE_URL_FILECOIN_PREFIX, + STORAGE_URL_IPFS_PREFIX, +} from './constants/StorageConstants'; import logger from './utils/logger'; export default class IPFSMetadataProvider { - constructor(web3, networkId, ipfsEndpoint) { - this._web3 = web3; - this._networkId = networkId; - this._ipfsEndpoint = ipfsEndpoint; - const registryAddress = RegistryNetworks[this._networkId].address; - this._registryContract = new this._web3.eth.Contract(RegistryAbi, registryAddress); - } + constructor(web3, networkId, ipfsEndpoint) { + this._web3 = web3; + this._networkId = networkId; + this._ipfsEndpoint = ipfsEndpoint; + const registryAddress = RegistryNetworks[this._networkId].address; + this._registryContract = new this._web3.eth.Contract( + RegistryAbi, + registryAddress + ); + this._lighthouseEndpoint = LIGHTHOUSE_ENDPOINT; + this._storageTypeIpfs = STORAGE_TYPE_IPFS; + this._storageTypeFilecoin = STORAGE_TYPE_FILECOIN; + this._storageUrlIpfsPrefix = STORAGE_URL_IPFS_PREFIX; + this._storageUrlFilecoinPrefix = STORAGE_URL_FILECOIN_PREFIX; + } + + /** + * @param {string} orgId + * @param {string} serviceId + * @returns {Promise.} + */ + async metadata(orgId, serviceId) { + logger.debug( + `Fetching service metadata [org: ${orgId} | service: ${serviceId}]` + ); + let orgIdBytes = this._web3.utils.fromAscii(orgId); + orgIdBytes = orgIdBytes.padEnd(66, '0'); // 66 = '0x' + 64 hex characters - /** - * @param {string} orgId - * @param {string} serviceId - * @returns {Promise.} - */ - async metadata(orgId, serviceId) { - logger.debug(`Fetching service metadata [org: ${orgId} | service: ${serviceId}]`); - let orgIdBytes = this._web3.utils.fromAscii(orgId); - orgIdBytes = orgIdBytes.padEnd(66, '0'); // 66 = '0x' + 64 hex characters + let serviceIdBytes = this._web3.utils.fromAscii(serviceId); + serviceIdBytes = serviceIdBytes.padEnd(66, '0'); // 66 = '0x' + 64 hex characters - let serviceIdBytes = this._web3.utils.fromAscii(serviceId); - serviceIdBytes = serviceIdBytes.padEnd(66, '0'); // 66 = '0x' + 64 hex characters + const orgMetadata = await this._fetchOrgMetadata(orgIdBytes); + const serviceMetadata = await this._fetchServiceMetadata( + orgIdBytes, + serviceIdBytes + ); + return Promise.resolve( + this._enhanceServiceGroupDetails(serviceMetadata, orgMetadata) + ); + } - const orgMetadata = await this._fetchOrgMetadata(orgIdBytes); - const serviceMetadata = await this._fetchServiceMetadata(orgIdBytes, serviceIdBytes); - return Promise.resolve(this._enhanceServiceGroupDetails(serviceMetadata, orgMetadata)); - } + async _fetchOrgMetadata(orgIdBytes) { + logger.debug('Fetching org metadata URI from registry contract'); + const { orgMetadataURI } = await this._registryContract.methods + .getOrganizationById(orgIdBytes) + .call(); - async _fetchOrgMetadata(orgIdBytes) { - logger.debug('Fetching org metadata URI from registry contract'); - const { orgMetadataURI } = await this._registryContract.methods.getOrganizationById(orgIdBytes).call(); + return this._fetchMetadataFromIpfs(orgMetadataURI); + } - return this._fetchMetadataFromIpfs(orgMetadataURI); - } + async _fetchServiceMetadata(orgIdBytes, serviceIdBytes) { + logger.debug('Fetching service metadata URI from registry contract'); + const { metadataURI: serviceMetadataURI } = + await this._registryContract.methods + .getServiceRegistrationById(orgIdBytes, serviceIdBytes) + .call(); + return this._fetchMetadataFromIpfs(serviceMetadataURI); + } - async _fetchServiceMetadata(orgIdBytes, serviceIdBytes) { - logger.debug('Fetching service metadata URI from registry contract'); - const { metadataURI: serviceMetadataURI } = await this._registryContract - .methods - .getServiceRegistrationById(orgIdBytes, serviceIdBytes) - .call(); - return this._fetchMetadataFromIpfs(serviceMetadataURI); - } + async _fetchMetadataFromIpfs(metadataURI) { + let storageInfo = this._getStorageInfoFromURI(metadataURI); + let storageTypeCID = storageInfo.uri; + storageTypeCID = storageTypeCID.replace(/\0/g, ''); + debug( + `Fetching metadata from ${storageInfo.type} [CID: ${storageTypeCID}]` + ); + try { + let fetchUrl; + if (storageInfo.type === this._storageTypeIpfs) { + fetchUrl = `${this._ipfsEndpoint}/api/v0/cat?arg=${storageTypeCID}`; + } else { + fetchUrl = `${this._lighthouseEndpoint}/${storageTypeCID}`; + } + const response = await fetch(fetchUrl); + if (!response.ok) { + throw response.error; + } + return response.json(); + } catch (error) { + debug(`Error fetching metadata from IPFS[CID: ${storageTypeCID}]`); + throw error; + } + } - async _fetchMetadataFromIpfs(metadataURI) { - let ipfsCID = `${this._web3.utils.hexToUtf8(metadataURI).substring(7)}`; - ipfsCID = ipfsCID.replace(/\0/g, ''); - logger.debug(`Fetching metadata from IPFS[CID: ${ipfsCID}]`); - try { - const fetchUrl = `${this._ipfsEndpoint}/api/v0/cat?arg=${ipfsCID}`; - const response = await get(fetchUrl); - return response.data; - } catch(error) { - logger.debug(`Error fetching metadata from IPFS[CID: ${ipfsCID}]`); - throw error; + _getStorageInfoFromURI(metadataURI) { + const decodedUri = this._web3.utils.hexToUtf8(metadataURI); + if (decodedUri.startsWith(STORAGE_URL_IPFS_PREFIX)) { + return { + type: this._storageTypeIpfs, + uri: decodedUri.replace(this._storageUrlIpfsPrefix, ''), + }; + } else if (decodedUri.startsWith(STORAGE_URL_FILECOIN_PREFIX)) { + return { + type: this._storageTypeFilecoin, + uri: decodedUri.replace(this._storageUrlFilecoinPrefix, ''), + }; + } else { + throw new Error( + `We support only ${this._storageTypeIpfs} and ${this._storageTypeFilecoin} uri in Registry` + ); + } } - } - _enhanceServiceGroupDetails(serviceMetadata, orgMetadata) { - const { groups: orgGroups } = orgMetadata; - const { groups: serviceGroups } = serviceMetadata; + _enhanceServiceGroupDetails(serviceMetadata, orgMetadata) { + const { groups: orgGroups } = orgMetadata; + const { groups: serviceGroups } = serviceMetadata; - const groups = map(serviceGroups, (group) => { - const { group_name: serviceGroupName } = group; - const orgGroup = find(orgGroups, ({ group_name: orgGroupName }) => orgGroupName === serviceGroupName); - return { - ...group, - payment: orgGroup.payment, - }; - }); + const groups = map(serviceGroups, (group) => { + const { group_name: serviceGroupName } = group; + const orgGroup = find( + orgGroups, + ({ group_name: orgGroupName }) => + orgGroupName === serviceGroupName + ); + return { + ...group, + payment: orgGroup.payment, + }; + }); - return { ...serviceMetadata, groups }; - } + return { ...serviceMetadata, groups }; + } - _constructIpfsClient() { - const { protocol = 'http', hostname: host, port = 5001 } = url.parse(this._ipfsEndpoint); - const ipfsHostOrMultiaddr = { protocol: protocol.replace(':', ''), host, port }; - return IPFSClient(ipfsHostOrMultiaddr); - } + _constructIpfsClient() { + const { + protocol = 'http', + hostname: host, + port = 5001, + } = url.parse(this._ipfsEndpoint); + const ipfsHostOrMultiaddr = { + protocol: protocol.replace(':', ''), + host, + port, + }; + return IPFSClient(ipfsHostOrMultiaddr); + } } diff --git a/packages/core/src/constants/StorageConstants.js b/packages/core/src/constants/StorageConstants.js new file mode 100644 index 0000000..841c3c5 --- /dev/null +++ b/packages/core/src/constants/StorageConstants.js @@ -0,0 +1,5 @@ +export const LIGHTHOUSE_ENDPOINT = "https://gateway.lighthouse.storage/ipfs"; +export const STORAGE_TYPE_IPFS = "ipfs"; +export const STORAGE_TYPE_FILECOIN = "filecoin"; +export const STORAGE_URL_IPFS_PREFIX = "ipfs://"; +export const STORAGE_URL_FILECOIN_PREFIX = "filecoin://"; \ No newline at end of file From c8c5a1f5e534c30ec9e2b3f7e30f96606aeb1a2c Mon Sep 17 00:00:00 2001 From: Marina Fedyantceva Date: Tue, 4 Feb 2025 12:53:24 +0300 Subject: [PATCH 2/2] web-sdk version 4.1.0 --- packages/web/package-lock.json | 4 ++-- packages/web/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/package-lock.json b/packages/web/package-lock.json index 0e54f74..fb4d2fe 100644 --- a/packages/web/package-lock.json +++ b/packages/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "snet-sdk-web", - "version": "4.0.4", + "version": "4.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "snet-sdk-web", - "version": "4.0.4", + "version": "4.1.0", "license": "MIT", "dependencies": { "@ethereumjs/tx": "^5.2.1", diff --git a/packages/web/package.json b/packages/web/package.json index 66d70a2..0b1f691 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "snet-sdk-web", - "version": "4.0.4", + "version": "4.1.0", "description": "SingularityNET SDK for Web", "main": "./dist/index.js", "files": [