Skip to content

Commit

Permalink
Merge pull request #1242 from input-output-hk/fix/lw-10173-utf8-decod…
Browse files Browse the repository at this point in the history
…ing-asset-name

[LW-10173] fix utf8 decoding asset name
  • Loading branch information
mchappell committed May 9, 2024
2 parents 5a51904 + 37c1887 commit 7b3254d
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 19 deletions.
4 changes: 4 additions & 0 deletions compose/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,19 @@ x-sdk-environment: &sdk-environment
POSTGRES_DB_FILE_HANDLE: /run/secrets/postgres_db_handle
POSTGRES_DB_FILE_STAKE_POOL: /run/secrets/postgres_db_stake_pool
POSTGRES_HOST_DB_SYNC: postgres
POSTGRES_HOST_ASSET: postgres
POSTGRES_HOST_HANDLE: postgres
POSTGRES_HOST_STAKE_POOL: postgres
POSTGRES_POOL_MAX_DB_SYNC: ${POSTGRES_POOL_MAX:-10}
POSTGRES_POOL_MAX_HANDLE: ${POSTGRES_POOL_MAX:-10}
POSTGRES_POOL_MAX_ASSET: ${POSTGRES_POOL_MAX:-10}
POSTGRES_POOL_MAX_STAKE_POOL: ${POSTGRES_POOL_MAX:-10}
POSTGRES_PASSWORD_FILE_ASSET: /run/secrets/postgres_password
POSTGRES_PASSWORD_FILE_DB_SYNC: /run/secrets/postgres_password
POSTGRES_PASSWORD_FILE_HANDLE: /run/secrets/postgres_password
POSTGRES_PASSWORD_FILE_STAKE_POOL: /run/secrets/postgres_password
POSTGRES_PORT_DB_SYNC: 5432
POSTGRES_PORT_ASSET: 5432
POSTGRES_PORT_HANDLE: 5432
POSTGRES_PORT_STAKE_POOL: 5432
POSTGRES_USER_FILE_ASSET: /run/secrets/postgres_user
Expand Down Expand Up @@ -341,6 +344,7 @@ services:
- *sdk-environment
- *provider-server-environment
SERVICE_NAMES: asset
USE_TYPEORM_ASSET_PROVIDER: true
ports:
- ${HANDLE_API_PORT:-4014}:3000

Expand Down
18 changes: 12 additions & 6 deletions packages/cardano-services/src/Asset/TypeOrmNftMetadataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@ export class TypeOrmNftMetadataService extends TypeormService implements NftMeta

async getNftMetadata(assetInfo: AssetPolicyIdAndName): Promise<Asset.NftMetadata | null> {
const assetId = Cardano.AssetId.fromParts(assetInfo.policyId, assetInfo.name);
const stringAssetName = Buffer.from(assetInfo.name, 'hex').toString('utf8');
return this.withDataSource(async (dataSource) => {
const queryRunner = dataSource.createQueryRunner();
const nftMetadataRepository = queryRunner.manager.getRepository(NftMetadataEntity);
let asset: NftMetadataEntity | null;

const asset = await nftMetadataRepository.findOneBy({
name: stringAssetName,
userTokenAsset: { id: assetId }
});
try {
const nftMetadataRepository = queryRunner.manager.getRepository(NftMetadataEntity);
asset = await nftMetadataRepository.findOneBy({
userTokenAsset: { id: assetId }
});
} catch (error) {
this.logger.error(error);
asset = null;
} finally {
await queryRunner.release();
}

if (!asset) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,15 @@ export class TypeormAssetProvider extends TypeormProvider implements AssetProvid

return this.withDataSource(async (dataSource) => {
const queryRunner = dataSource.createQueryRunner();
const assetRepository = queryRunner.manager.getRepository(AssetEntity);

const asset = await assetRepository.findOneBy({ id: assetId });

if (!asset) throw new ProviderError(ProviderFailure.NotFound, undefined, `Asset not found '${assetId}'`);
const supply = asset.supply!;
let supply: bigint;
try {
const assetRepository = queryRunner.manager.getRepository(AssetEntity);
const asset = await assetRepository.findOneBy({ id: assetId });
if (!asset) throw new ProviderError(ProviderFailure.NotFound, undefined, `Asset not found '${assetId}'`);
supply = asset.supply!;
} finally {
await queryRunner.release();
}

return {
assetId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ const serviceMapFactory = (options: ServiceMapFactoryOptions) => {
: new CardanoTokenRegistry({ logger }, args);

return new TypeormAssetProvider(
{ paginationPageSizeLimit: args.paginationPageSizeLimit! },
{ paginationPageSizeLimit: Math.min(args.paginationPageSizeLimit! * 10, PAGINATION_PAGE_SIZE_LIMIT_ASSETS) },
{
connectionConfig$,
entities: getEntities(['asset']),
Expand Down
12 changes: 9 additions & 3 deletions packages/core/src/Asset/NftMetadata/fromMetadatum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,18 @@ const getAssetMetadata = (policy: Cardano.MetadatumMap, assetName: Cardano.Asset
);

type AssetIdParts = Pick<AssetInfo, 'policyId' | 'name'>;
const getName = (assetMetadata: Cardano.MetadatumMap, version: string, asset: AssetIdParts, logger: Logger) => {
const getName = (
assetMetadata: Cardano.MetadatumMap,
version: string,
asset: AssetIdParts,
logger: Logger,
stripInvisibleCharacters = false
) => {
const name = asString(assetMetadata.get('name'));
if (name) return name;
if (version === '1.0') {
try {
return AssetName.toUTF8(asset.name);
return AssetName.toUTF8(asset.name, stripInvisibleCharacters);
} catch (error) {
logger.warn(error);
}
Expand Down Expand Up @@ -143,7 +149,7 @@ export const fromMetadatum = (
if (!version) return null;
const assetMetadata = getAssetMetadata(policy, asset.name);
if (!assetMetadata) return null;
const name = getName(assetMetadata, version, asset, logger);
const name = getName(assetMetadata, version, asset, logger, true);
const image = metadatumToString(assetMetadata.get('image'));
const assetId = Cardano.AssetId.fromParts(asset.policyId, asset.name);

Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/Cardano/types/Asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ export const AssetName = (value: string): AssetName => {
};

const utf8Decoder = new TextDecoder('utf8', { fatal: true });
AssetName.toUTF8 = (assetName: AssetName) => {
AssetName.toUTF8 = (assetName: AssetName, stripInvisibleCharacters = false) => {
const assetNameBuffer = Buffer.from(assetName, 'hex');
try {
return utf8Decoder.decode(Buffer.from(assetName, 'hex'));
if (stripInvisibleCharacters) {
// 'invisible' control characters are valid utf8, but we don't want to use them, strip them out
return utf8Decoder.decode(assetNameBuffer.filter((v) => v > 31));
}
return utf8Decoder.decode(assetNameBuffer);
} catch (error) {
throw new InvalidStringError(`Cannot convert AssetName '${assetName}' to UTF8`, error);
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/test/Cardano/types/Asset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,16 @@ describe('Cardano/types/Asset', () => {
expect(() => AssetName('0dbe461fb5f981c0d01615332b8666340eb1a692b3034f46bcb5f5e0dbe461abc')).toThrow();
});

describe('fromUTF8', () => {
describe('toUTF8', () => {
it('decodes hex string (bytes) to utf8 string', () => {
expect(AssetName.toUTF8(AssetName('737472'))).toEqual('str');
});
it('throws InvalidStringError when it cannot be decoded to utf8', () => {
expect(() => AssetName.toUTF8(AssetName('e8'))).toThrowError(InvalidStringError);
});
it('strips invisible characters when requested', () => {
expect(AssetName.toUTF8(AssetName('100000'), true)).toEqual('');
});
});
});

Expand Down

0 comments on commit 7b3254d

Please sign in to comment.