import {
AddMetaDetailsParams,
AddMetahashParams,
GetMetaParams,
InvalidMetadataError,
InvalidParamsError,
logger,
MetaNotFoundError,
SailsIdlNotFoundError,
} from '@gear-js/common';
import { ProgramMetadata, MetadataVersion, HumanTypesRepr } from '@gear-js/api';
import { Repository } from 'typeorm';
import * as crypto from 'crypto';
import { Meta, AppDataSource, SailsIdl } from './database';
import { validateMetaHex } from './util/validate';
import { Code } from './database/entities/code.entity';
const getHash = (data: string) => crypto.createHash('sha256').update(data).digest('hex');
export class MetaService {
private metaRepo: Repository;
private sailsRepo: Repository;
private codeRepo: Repository;
constructor() {
this.metaRepo = AppDataSource.getRepository(Meta);
this.sailsRepo = AppDataSource.getRepository(SailsIdl);
this.codeRepo = AppDataSource.getRepository(Code);
}
async addMeta({ metahash }: AddMetahashParams): Promise {
logger.info('Adding meta', { metahash });
const meta = (await this.metaRepo.findOne({ where: { hash: metahash } })) || new Meta({ hash: metahash });
await this.metaRepo.save(meta);
return meta.hasState ? [metahash] : [];
}
async addMetaDetails(params: AddMetaDetailsParams): Promise> {
logger.info('Adding meta details', params);
if (!params.hash) {
throw new InvalidParamsError();
}
let meta = await this.metaRepo.findOneBy({ hash: params.hash });
if (!meta) {
meta = new Meta({ hash: params.hash });
}
if (meta.hex) {
return { hex: meta.hex, hash: meta.hash, hasState: meta.hasState };
}
validateMetaHex(params.hex, meta.hash);
meta.hex = params.hex;
let metadata: ProgramMetadata;
try {
metadata = ProgramMetadata.from(meta.hex);
} catch (error) {
throw new InvalidMetadataError('Invalid metadata hex');
}
if (metadata.version === MetadataVersion.V1Rust) {
if (metadata.types.state != null) {
meta.hasState = true;
}
} else {
if ((metadata.types.state as HumanTypesRepr).output != null) {
meta.hasState = true;
}
}
await this.metaRepo.save(meta);
return { hex: meta.hex, hash: meta.hash, hasState: meta.hasState };
}
async get({ hash }: GetMetaParams): Promise> {
if (!hash) {
throw new InvalidParamsError();
}
const meta = await this.metaRepo.findOne({ where: { hash } });
if (!meta) {
throw new MetaNotFoundError();
}
return meta;
}
async getAllWithState(): Promise {
const meta = await this.metaRepo.find({ where: { hasState: true }, select: { hash: true } });
return meta.map((m) => m.hash);
}
async addIdl({ codeId, data }) {
if (!codeId || !data) {
throw new InvalidParamsError();
}
const hash = getHash(data);
logger.info('Adding IDL', { codeId, hash });
let sails = await this.sailsRepo.findOne({ where: { id: hash } });
if (!sails) {
const code = await this.codeRepo.findOne({ where: { id: codeId } });
if (code) {
throw new InvalidParamsError('Code already has IDL');
}
sails = new SailsIdl({ id: hash, data });
}
const code = new Code({ id: codeId, sails });
await this.sailsRepo.save(sails);
await this.codeRepo.save(code);
return { status: 'Sails idl added' };
}
async getIdl({ codeId }) {
const code = await this.codeRepo.findOne({ where: { id: codeId }, relations: { sails: true } });
if (!code) {
throw new SailsIdlNotFoundError();
}
return code.sails.data;
}
}