Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 36x 36x 36x 36x 36x 36x 36x 36x 2x 2x 2x 36x 3x 3x 3x 3x 3x 1x 36x 3x 1x 2x 1x 1x 36x 4x 2x 2x 1x 1x 2x 1x 1x 36x | import { Cardano, CardanoNodeUtil, NotImplementedError, ProviderFailure } from '@cardano-sdk/core'; import { CustomError } from 'ts-custom-error'; import { DataSource, MoreThan } from 'typeorm'; import { Hash32ByteBase16 } from '@cardano-sdk/crypto'; import { PoolMetadataEntity, PoolRegistrationEntity, StakePoolMetadataJob } from '@cardano-sdk/projection-typeorm'; import { StakePoolMetadataFetchMode, checkProgramOptions } from '../Program/options'; import { WorkerHandlerFactory } from './types'; import { createHttpStakePoolMetadataService } from '../StakePool'; import { isErrorWithConstraint } from './util'; export const isUpdateOutdated = async (dataSource: DataSource, poolId: Cardano.PoolId, poolRegistrationId: string) => { const repos = dataSource.getRepository(PoolRegistrationEntity); // TODO: Improve this check to take in account stability window // Ref: LW-6492 const res = await repos.countBy({ id: MoreThan(poolRegistrationId as unknown as bigint), stakePool: { id: poolId } }); return res > 0; }; interface SavePoolMetadataArguments { dataSource: DataSource; hash: Hash32ByteBase16; metadata: Cardano.StakePoolMetadata; poolId: Cardano.PoolId; poolRegistrationId: string; } export const savePoolMetadata = async (args: SavePoolMetadataArguments) => { const { dataSource, hash, metadata, poolId, poolRegistrationId } = args; const repos = dataSource.getRepository(PoolMetadataEntity); const entity = repos.create({ ...metadata, hash, poolUpdate: { id: BigInt(poolRegistrationId) }, stakePool: { id: poolId } }); try { await repos.upsert(entity, ['poolUpdate']); } catch (error) { // If no poolRegistration record is present, it was rolled back: do nothing if (isErrorWithConstraint(error) && error.constraint === 'FK_pool_metadata_pool_update_id') return; throw error; } }; export const getUrlToFetch = ( metadataFetchMode: StakePoolMetadataFetchMode, smashUrl: string | undefined, directUrl: string, poolRegistrationId: string, metadataHash: string ) => { if (metadataFetchMode === StakePoolMetadataFetchMode.SMASH) { return `${smashUrl}/metadata/${poolRegistrationId}/${metadataHash}`; } else if (metadataFetchMode === StakePoolMetadataFetchMode.DIRECT) { return directUrl; } throw new NotImplementedError( `There is no implementation to handle the fetch mode (--metadata-fetch-mode): ${metadataFetchMode}` ); }; export const attachExtendedMetadata = ( metadataWithoutExt: Cardano.StakePoolMetadata, extMetadata: Cardano.ExtendedStakePoolMetadata | CustomError | undefined ): Cardano.StakePoolMetadata => { if (extMetadata instanceof CustomError) { const error = extMetadata; if (CardanoNodeUtil.isProviderError(error) && error.reason === ProviderFailure.NotFound) { return { ...metadataWithoutExt!, ext: null }; } return metadataWithoutExt; } else if (extMetadata === undefined) { return metadataWithoutExt; } return { ...metadataWithoutExt!, ext: extMetadata }; }; export const stakePoolMetadataHandlerFactory: WorkerHandlerFactory = (options) => { const { dataSource, logger, metadataFetchMode, smashUrl } = options; const service = createHttpStakePoolMetadataService(logger); checkProgramOptions(metadataFetchMode, smashUrl); return async (task: StakePoolMetadataJob) => { const { metadataJson, poolId, poolRegistrationId } = task; const { hash, url } = metadataJson; logger.info(`Checking if pool update ${poolRegistrationId} is outdated by a more recent update`); // If there is a newer pool update in the chain... Iif (await isUpdateOutdated(dataSource, poolId, poolRegistrationId)) { logger.info('Pool update is outdated, metadata no longer needed'); return; } const urlToFetch: string = getUrlToFetch(metadataFetchMode, smashUrl, url, poolId, hash); logger.info('Resolving stake pool metadata...', { metadataFetchMode, poolId, poolRegistrationId }); const metadataResponse: Cardano.StakePoolMetadata | CustomError = await service.getStakePoolMetadata( hash, urlToFetch ); if (metadataResponse instanceof CustomError) { logger.info('Stake pool metadata NOT resolved with errors', { metadataResponse, poolId, poolRegistrationId, url }); // In case of errors the handler throws in order to let pg-boss to retry the job. logger.info('StakePoolMetadataJob failed to fetch stake pool metadata.'); throw metadataResponse; } else { const metadataWithoutExt: Cardano.StakePoolMetadata = metadataResponse; logger.info('Stake pool metadata resolved successfully', { metadataWithoutExt, poolId, poolRegistrationId, url }); logger.info('Resolving extended stake pool metadata...', { metadataFetchMode, poolId, poolRegistrationId }); const extendedMetadata = await service.getValidateStakePoolExtendedMetadata(metadataWithoutExt); logger.info('Stake pool extended metadata resolved', { extendedMetadata, metadataFetchMode, poolId, poolRegistrationId }); const metadata: Cardano.StakePoolMetadata = attachExtendedMetadata(metadataWithoutExt, extendedMetadata); await savePoolMetadata({ dataSource, hash, metadata, poolId, poolRegistrationId }); logger.info('Stake pool metadata saved'); Iif (extendedMetadata instanceof CustomError) { logger.info('StakePoolMetadataJob failed to fetch extended stake pool metadata.'); throw extendedMetadata; } } }; // TODO: Store the error in a dedicated table // Ref: LW-6409 }; |