// // Copyright 2021 Vulcanize, Inc. // import assert from 'assert'; import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner, FindManyOptions, EntityTarget } from 'typeorm'; import path from 'path'; import { Database as BaseDatabase, DatabaseInterface, QueryOptions, StateKind, Where } from '@cerc-io/util'; import { Contract } from './entity/Contract'; import { Event } from './entity/Event'; import { SyncStatus } from './entity/SyncStatus'; import { StateSyncStatus } from './entity/StateSyncStatus'; import { BlockProgress } from './entity/BlockProgress'; import { State } from './entity/State'; import { WithdrawLimit } from './entity/WithdrawLimit'; import { VerifyBalance } from './entity/VerifyBalance'; import { GetBatches } from './entity/GetBatches'; import { GetBatch } from './entity/GetBatch'; import { GetWithdrawn } from './entity/GetWithdrawn'; import { GetWithdrawnFromBatch } from './entity/GetWithdrawnFromBatch'; import { GetForfeited } from './entity/GetForfeited'; import { HasForfeitedBatch } from './entity/HasForfeitedBatch'; import { GetRemainingStars } from './entity/GetRemainingStars'; import { GetConditionsState } from './entity/GetConditionsState'; export const ENTITIES = [WithdrawLimit, VerifyBalance, GetBatches, GetBatch, GetWithdrawn, GetWithdrawnFromBatch, GetForfeited, HasForfeitedBatch, GetRemainingStars, GetConditionsState]; export class Database implements DatabaseInterface { _config: ConnectionOptions; _conn!: Connection; _baseDatabase: BaseDatabase; _propColMaps: { [key: string]: Map; }; constructor (config: ConnectionOptions) { assert(config); this._config = { ...config, entities: [path.join(__dirname, 'entity/*')] }; this._baseDatabase = new BaseDatabase(this._config); this._propColMaps = {}; } get baseDatabase (): BaseDatabase { return this._baseDatabase; } async init (): Promise { this._conn = await this._baseDatabase.init(); this._setPropColMaps(); } async close (): Promise { return this._baseDatabase.close(); } async getWithdrawLimit ({ blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }): Promise { return this._conn.getRepository(WithdrawLimit) .findOne({ blockHash, contractAddress, _participant, _batch }); } async getVerifyBalance ({ blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }): Promise { return this._conn.getRepository(VerifyBalance) .findOne({ blockHash, contractAddress, _participant }); } async getGetBatches ({ blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }): Promise { return this._conn.getRepository(GetBatches) .findOne({ blockHash, contractAddress, _participant }); } async getGetBatch ({ blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }): Promise { return this._conn.getRepository(GetBatch) .findOne({ blockHash, contractAddress, _participant, _batch }); } async getGetWithdrawn ({ blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }): Promise { return this._conn.getRepository(GetWithdrawn) .findOne({ blockHash, contractAddress, _participant }); } async getGetWithdrawnFromBatch ({ blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }): Promise { return this._conn.getRepository(GetWithdrawnFromBatch) .findOne({ blockHash, contractAddress, _participant, _batch }); } async getGetForfeited ({ blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }): Promise { return this._conn.getRepository(GetForfeited) .findOne({ blockHash, contractAddress, _participant }); } async getHasForfeitedBatch ({ blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }): Promise { return this._conn.getRepository(HasForfeitedBatch) .findOne({ blockHash, contractAddress, _participant, _batch }); } async getGetRemainingStars ({ blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }): Promise { return this._conn.getRepository(GetRemainingStars) .findOne({ blockHash, contractAddress, _participant }); } async getGetConditionsState ({ blockHash, contractAddress }: { blockHash: string, contractAddress: string }): Promise { return this._conn.getRepository(GetConditionsState) .findOne({ blockHash, contractAddress }); } async saveWithdrawLimit ({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(WithdrawLimit); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }); return repo.save(entity); } async saveVerifyBalance ({ blockHash, blockNumber, contractAddress, _participant, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(VerifyBalance); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, value, proof }); return repo.save(entity); } async saveGetBatches ({ blockHash, blockNumber, contractAddress, _participant, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetBatches); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, value, proof }); return repo.save(entity); } async saveGetBatch ({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetBatch); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }); return repo.save(entity); } async saveGetWithdrawn ({ blockHash, blockNumber, contractAddress, _participant, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetWithdrawn); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, value, proof }); return repo.save(entity); } async saveGetWithdrawnFromBatch ({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetWithdrawnFromBatch); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }); return repo.save(entity); } async saveGetForfeited ({ blockHash, blockNumber, contractAddress, _participant, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetForfeited); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, value, proof }); return repo.save(entity); } async saveHasForfeitedBatch ({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(HasForfeitedBatch); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, _batch, value, proof }); return repo.save(entity); } async saveGetRemainingStars ({ blockHash, blockNumber, contractAddress, _participant, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetRemainingStars); const entity = repo.create({ blockHash, blockNumber, contractAddress, _participant, value, proof }); return repo.save(entity); } async saveGetConditionsState ({ blockHash, blockNumber, contractAddress, value0, value1, value2, value3, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetConditionsState); const entity = repo.create({ blockHash, blockNumber, contractAddress, value0, value1, value2, value3, proof }); return repo.save(entity); } getNewState (): State { return new State(); } async getStates (where: FindConditions): Promise { const repo = this._conn.getRepository(State); return this._baseDatabase.getStates(repo, where); } async getLatestState (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise { const repo = this._conn.getRepository(State); return this._baseDatabase.getLatestState(repo, contractAddress, kind, blockNumber); } async getPrevState (blockHash: string, contractAddress: string, kind?: string): Promise { const repo = this._conn.getRepository(State); return this._baseDatabase.getPrevState(repo, blockHash, contractAddress, kind); } // Fetch all diff States after the specified block number. async getDiffStatesInRange (contractAddress: string, startblock: number, endBlock: number): Promise { const repo = this._conn.getRepository(State); return this._baseDatabase.getDiffStatesInRange(repo, contractAddress, startblock, endBlock); } async saveOrUpdateState (dbTx: QueryRunner, state: State): Promise { const repo = dbTx.manager.getRepository(State); return this._baseDatabase.saveOrUpdateState(repo, state); } async removeStates (dbTx: QueryRunner, blockNumber: number, kind: string): Promise { const repo = dbTx.manager.getRepository(State); await this._baseDatabase.removeStates(repo, blockNumber, kind); } async removeStatesAfterBlock (dbTx: QueryRunner, blockNumber: number): Promise { const repo = dbTx.manager.getRepository(State); await this._baseDatabase.removeStatesAfterBlock(repo, blockNumber); } async getStateSyncStatus (): Promise { const repo = this._conn.getRepository(StateSyncStatus); return this._baseDatabase.getStateSyncStatus(repo); } async updateStateSyncStatusIndexedBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise { const repo = queryRunner.manager.getRepository(StateSyncStatus); return this._baseDatabase.updateStateSyncStatusIndexedBlock(repo, blockNumber, force); } async updateStateSyncStatusCheckpointBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise { const repo = queryRunner.manager.getRepository(StateSyncStatus); return this._baseDatabase.updateStateSyncStatusCheckpointBlock(repo, blockNumber, force); } async getContracts (): Promise { const repo = this._conn.getRepository(Contract); return this._baseDatabase.getContracts(repo); } async createTransactionRunner (): Promise { return this._baseDatabase.createTransactionRunner(); } async getProcessedBlockCountForRange (fromBlockNumber: number, toBlockNumber: number): Promise<{ expected: number, actual: number }> { const repo = this._conn.getRepository(BlockProgress); return this._baseDatabase.getProcessedBlockCountForRange(repo, fromBlockNumber, toBlockNumber); } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { const repo = this._conn.getRepository(Event); return this._baseDatabase.getEventsInRange(repo, fromBlockNumber, toBlockNumber); } async saveEventEntity (queryRunner: QueryRunner, entity: Event): Promise { const repo = queryRunner.manager.getRepository(Event); return this._baseDatabase.saveEventEntity(repo, entity); } async getBlockEvents (blockHash: string, where: Where, queryOptions: QueryOptions): Promise { const repo = this._conn.getRepository(Event); return this._baseDatabase.getBlockEvents(repo, blockHash, where, queryOptions); } async saveBlockWithEvents (queryRunner: QueryRunner, block: DeepPartial, events: DeepPartial[]): Promise { const blockRepo = queryRunner.manager.getRepository(BlockProgress); const eventRepo = queryRunner.manager.getRepository(Event); return this._baseDatabase.saveBlockWithEvents(blockRepo, eventRepo, block, events); } async saveEvents (queryRunner: QueryRunner, events: Event[]): Promise { const eventRepo = queryRunner.manager.getRepository(Event); return this._baseDatabase.saveEvents(eventRepo, events); } async saveBlockProgress (queryRunner: QueryRunner, block: DeepPartial): Promise { const repo = queryRunner.manager.getRepository(BlockProgress); return this._baseDatabase.saveBlockProgress(repo, block); } async saveContract (queryRunner: QueryRunner, address: string, kind: string, checkpoint: boolean, startingBlock: number): Promise { const repo = queryRunner.manager.getRepository(Contract); return this._baseDatabase.saveContract(repo, address, kind, checkpoint, startingBlock); } async updateSyncStatusIndexedBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.updateSyncStatusIndexedBlock(repo, blockHash, blockNumber, force); } async updateSyncStatusCanonicalBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.updateSyncStatusCanonicalBlock(repo, blockHash, blockNumber, force); } async updateSyncStatusChainHead (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.updateSyncStatusChainHead(repo, blockHash, blockNumber, force); } async getSyncStatus (queryRunner: QueryRunner): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.getSyncStatus(repo); } async getEvent (id: string): Promise { const repo = this._conn.getRepository(Event); return this._baseDatabase.getEvent(repo, id); } async getBlocksAtHeight (height: number, isPruned: boolean): Promise { const repo = this._conn.getRepository(BlockProgress); return this._baseDatabase.getBlocksAtHeight(repo, height, isPruned); } async markBlocksAsPruned (queryRunner: QueryRunner, blocks: BlockProgress[]): Promise { const repo = queryRunner.manager.getRepository(BlockProgress); return this._baseDatabase.markBlocksAsPruned(repo, blocks); } async getBlockProgress (blockHash: string): Promise { const repo = this._conn.getRepository(BlockProgress); return this._baseDatabase.getBlockProgress(repo, blockHash); } async getBlockProgressEntities (where: FindConditions, options: FindManyOptions): Promise { const repo = this._conn.getRepository(BlockProgress); return this._baseDatabase.getBlockProgressEntities(repo, where, options); } async getEntitiesForBlock (blockHash: string, tableName: string): Promise { return this._baseDatabase.getEntitiesForBlock(blockHash, tableName); } async updateBlockProgress (queryRunner: QueryRunner, block: BlockProgress, lastProcessedEventIndex: number): Promise { const repo = queryRunner.manager.getRepository(BlockProgress); return this._baseDatabase.updateBlockProgress(repo, block, lastProcessedEventIndex); } async removeEntities (queryRunner: QueryRunner, entity: new () => Entity, findConditions?: FindManyOptions | FindConditions): Promise { return this._baseDatabase.removeEntities(queryRunner, entity, findConditions); } async deleteEntitiesByConditions (queryRunner: QueryRunner, entity: EntityTarget, findConditions: FindConditions): Promise { await this._baseDatabase.deleteEntitiesByConditions(queryRunner, entity, findConditions); } async getAncestorAtDepth (blockHash: string, depth: number): Promise { return this._baseDatabase.getAncestorAtDepth(blockHash, depth); } _getPropertyColumnMapForEntity (entityName: string): Map { return this._conn.getMetadata(entityName).ownColumns.reduce((acc, curr) => { return acc.set(curr.propertyName, curr.databaseName); }, new Map()); } _setPropColMaps (): void { this._propColMaps.WithdrawLimit = this._getPropertyColumnMapForEntity('WithdrawLimit'); this._propColMaps.VerifyBalance = this._getPropertyColumnMapForEntity('VerifyBalance'); this._propColMaps.GetBatches = this._getPropertyColumnMapForEntity('GetBatches'); this._propColMaps.GetBatch = this._getPropertyColumnMapForEntity('GetBatch'); this._propColMaps.GetWithdrawn = this._getPropertyColumnMapForEntity('GetWithdrawn'); this._propColMaps.GetWithdrawnFromBatch = this._getPropertyColumnMapForEntity('GetWithdrawnFromBatch'); this._propColMaps.GetForfeited = this._getPropertyColumnMapForEntity('GetForfeited'); this._propColMaps.HasForfeitedBatch = this._getPropertyColumnMapForEntity('HasForfeitedBatch'); this._propColMaps.GetRemainingStars = this._getPropertyColumnMapForEntity('GetRemainingStars'); this._propColMaps.GetConditionsState = this._getPropertyColumnMapForEntity('GetConditionsState'); } }