// // Copyright 2021 Vulcanize, Inc. // import assert from 'assert'; import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner, FindManyOptions } 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 { GetMethod } from './entity/GetMethod'; import { _Test } from './entity/_Test'; 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 getGetMethod ({ blockHash, contractAddress }: { blockHash: string, contractAddress: string }): Promise { return this._conn.getRepository(GetMethod) .findOne({ blockHash, contractAddress }); } async _getTest ({ blockHash, contractAddress }: { blockHash: string, contractAddress: string }): Promise<_Test | undefined> { return this._conn.getRepository(_Test) .findOne({ blockHash, contractAddress }); } async saveGetMethod ({ blockHash, blockNumber, contractAddress, value, proof }: DeepPartial): Promise { const repo = this._conn.getRepository(GetMethod); const entity = repo.create({ blockHash, blockNumber, contractAddress, value, proof }); return repo.save(entity); } async _saveTest ({ blockHash, blockNumber, contractAddress, value, proof }: DeepPartial<_Test>): Promise<_Test> { const repo = this._conn.getRepository(_Test); const entity = repo.create({ blockHash, blockNumber, contractAddress, value, 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 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 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: new () => Entity, 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.GetMethod = this._getPropertyColumnMapForEntity('GetMethod'); this._propColMaps._Test = this._getPropertyColumnMapForEntity('_Test'); } }