filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
121 lines • 6.2 kB
JavaScript
/**
* Helper: createStorageContextFromDataSetId
*
* Optimized helper to create a StorageContext for an existing dataset ID without
* triggering the expensive "scan all client datasets" hotpath.
*
* This is a workaround for https://github.com/FilOzone/synapse-sdk/issues/435
* until https://github.com/FilOzone/synapse-sdk/pull/438 is merged and published.
*
* Benefits:
* - Reuses Synapse's existing WarmStorageService (no extra initialization)
* - Only fetches data for the single dataset needed (~5-6 RPC calls vs 1500+)
* - Validates ownership, isLive, and isManaged
*
* TODO: Replace with synapse.storage.createContextFromDataSetId() once PR #438 is published
*/
import { StorageContext } from '@filoz/synapse-sdk';
import { SPRegistryService } from '@filoz/synapse-sdk/sp-registry';
import { DEFAULT_DATA_SET_METADATA, DEFAULT_STORAGE_CONTEXT_CONFIG } from './index.js';
/**
* Create a StorageContext for an existing dataSetId without scanning all datasets.
*/
export async function createStorageContextFromDataSetId(synapse, dataSetId, options) {
const { checkProviderApproval = false } = options ?? {};
// Access Synapse's internal WarmStorageService (avoids creating a new one)
// @ts-expect-error - Accessing private _warmStorageService temporarily until SDK is updated
const warmStorage = synapse.storage._warmStorageService;
if (!warmStorage) {
throw new Error('WarmStorageService not available on Synapse instance');
}
// Get basic dataset info and validate in parallel
const [dataSetInfo] = await Promise.all([warmStorage.getDataSet(dataSetId), warmStorage.validateDataSet(dataSetId)]);
// Verify ownership
const signerAddress = await synapse.getClient().getAddress();
if (dataSetInfo.payer.toLowerCase() !== signerAddress.toLowerCase()) {
throw new Error(`Data set ${dataSetId} is not owned by ${signerAddress} (owned by ${dataSetInfo.payer})`);
}
const registryAddress = warmStorage.getServiceProviderRegistryAddress();
const spRegistry = new SPRegistryService(synapse.getProvider(), registryAddress);
if (checkProviderApproval) {
/**
* If asked to check provider approval and it's not an approved provider, we're going to throw an error.
*/
const isProviderApproved = await warmStorage.isProviderIdApproved(dataSetInfo.providerId);
if (!isProviderApproved) {
throw new Error(`Data set ${dataSetId} is not owned by an approved provider`);
}
}
// Get provider info and metadata in parallel
const [providerInfo, dataSetMetadata] = await Promise.all([
spRegistry.getProvider(dataSetInfo.providerId),
warmStorage.getDataSetMetadata(dataSetId),
]);
if (providerInfo == null) {
throw new Error(`Unable to resolve provider info for data set ${dataSetId} and provider ${dataSetInfo.providerId}`);
}
// Construct storage context directly
const withCDN = dataSetInfo.cdnRailId > 0;
const storageContext = new StorageContext(synapse, warmStorage, providerInfo, dataSetId, { withCDN }, dataSetMetadata);
return {
storage: storageContext,
providerInfo,
};
}
/**
* Create a StorageContext configured for creating a brand new dataset without scanning all
* existing datasets. Provider selection favors explicit overrides and otherwise randomly selects
* an active provider that exposes a PDP endpoint.
*/
export async function createStorageContextForNewDataSet(synapse, options = {}) {
// @ts-expect-error - Accessing private _warmStorageService temporarily until SDK is updated
const warmStorage = options.warmStorage ?? synapse.storage._warmStorageService;
if (!warmStorage) {
throw new Error('WarmStorageService not available on Synapse instance');
}
const registryAddress = warmStorage.getServiceProviderRegistryAddress();
const spRegistry = options.spRegistry ?? new SPRegistryService(synapse.getProvider(), registryAddress);
const providerInfo = await getApprovedProviderInfo(warmStorage, spRegistry, options.providerId, options.logger);
const mergedMetadata = { ...DEFAULT_DATA_SET_METADATA };
const storageOptions = {
...DEFAULT_STORAGE_CONTEXT_CONFIG,
metadata: mergedMetadata,
};
const storageContext = new StorageContext(synapse, warmStorage, providerInfo, undefined, storageOptions, mergedMetadata);
return {
storage: storageContext,
providerInfo,
};
}
/**
* getApprovedProviderInfo should only be called from createStorageContextForNewDataSet, which is only called with a providerId if a user has explicitly provided one as a query parameter.
*/
async function getApprovedProviderInfo(warmStorage, spRegistry, providerId, logger) {
let providerInfo = null;
if (providerId != null) {
// if given a providerId, check if it is approved and log a warning if it's not an approved provider.
const isProviderApproved = await warmStorage.isProviderIdApproved(providerId);
if (!isProviderApproved) {
logger?.warn({ providerId }, `Presuming given providerId ${providerId} is a queryParam and allowing creation to continue with a non-approved provider`);
}
providerInfo = await spRegistry.getProvider(providerId);
}
else {
// otherwise, get all approved provider ids and randomly select one.
const approvedProviderIds = await warmStorage.getApprovedProviderIds();
// select a random approved provider id
const randomApprovedProviderId = pickRandom(approvedProviderIds);
providerInfo = await spRegistry.getProvider(randomApprovedProviderId);
}
if (providerInfo == null) {
throw new Error(`Unable to resolve an approved storage provider for new data set creation`);
}
return providerInfo;
}
function pickRandom(providerIds) {
if (providerIds.length === 0)
throw new Error('No approved storage providers available for new data set creation');
return providerIds[Math.floor(Math.random() * providerIds.length)];
}
export default createStorageContextFromDataSetId;
//# sourceMappingURL=storage-context-helper.js.map