// // Copyright 2021 Vulcanize, Inc. // import assert from 'assert'; import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner, FindManyOptions, EntityTarget } from 'typeorm'; import path from 'path'; import { ENTITY_QUERY_TYPE, 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 { RouteProcessor } from './entity/RouteProcessor'; import { Route } from './entity/Route'; import { User } from './entity/User'; export const SUBGRAPH_ENTITIES = new Set([RouteProcessor, Route, User]); export const ENTITIES = [...SUBGRAPH_ENTITIES]; // Map: Entity to suitable query type. export const ENTITY_QUERY_TYPE_MAP = new Map any, ENTITY_QUERY_TYPE>([]); export const ENTITY_TO_LATEST_ENTITY_MAP: Map = new Map(); export class Database implements DatabaseInterface { _config: ConnectionOptions; _conn!: Connection; _baseDatabase: BaseDatabase; _propColMaps: { [key: string]: Map; }; constructor (config: ConnectionOptions) { assert(config); this._config = { ...config, subscribers: [path.join(__dirname, 'entity/Subscriber.*')], 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(); } async close (): Promise { return this._baseDatabase.close(); } 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, context?: any): Promise { const repo = queryRunner.manager.getRepository(Contract); return this._baseDatabase.saveContract(repo, address, kind, checkpoint, startingBlock, context); } 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 updateSyncStatusProcessedBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.updateSyncStatusProcessedBlock(repo, blockHash, blockNumber, force); } async updateSyncStatusIndexingError (queryRunner: QueryRunner, hasIndexingError: boolean): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.updateSyncStatusIndexingError(repo, hasIndexingError); } async updateSyncStatus (queryRunner: QueryRunner, syncStatus: DeepPartial): Promise { const repo = queryRunner.manager.getRepository(SyncStatus); return this._baseDatabase.updateSyncStatus(repo, syncStatus); } 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 getAncestorAtHeight (blockHash: string, height: number): Promise { return this._baseDatabase.getAncestorAtHeight(blockHash, height); } _getPropertyColumnMapForEntity (entityName: string): Map { return this._conn.getMetadata(entityName).ownColumns.reduce((acc, curr) => { return acc.set(curr.propertyName, curr.databaseName); }, new Map()); } }