From 74a85039e662e9cae2f84ef33fd83a9615ae4140 Mon Sep 17 00:00:00 2001 From: Nabarun Date: Mon, 5 Aug 2024 16:02:37 +0530 Subject: [PATCH] Handle template create events processing order during reorgs --- .../src/templates/indexer-template.handlebars | 4 ++ packages/graph-node/src/loader.ts | 44 +++++++++++++------ packages/util/src/indexer.ts | 16 +++++++ packages/util/src/types.ts | 1 + 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/packages/codegen/src/templates/indexer-template.handlebars b/packages/codegen/src/templates/indexer-template.handlebars index 453d8fe9..73f546b9 100644 --- a/packages/codegen/src/templates/indexer-template.handlebars +++ b/packages/codegen/src/templates/indexer-template.handlebars @@ -635,6 +635,10 @@ export class Indexer implements IndexerInterface { return this._baseIndexer.watchContract(address, kind, checkpoint, startingBlock, context); } + async removeContract (address: string, kind: string): Promise { + return this._baseIndexer.removeContract(address, kind); + } + updateStateStatusMap (address: string, stateStatus: StateStatus): void { this._baseIndexer.updateStateStatusMap(address, stateStatus); } diff --git a/packages/graph-node/src/loader.ts b/packages/graph-node/src/loader.ts index ccc52c75..a2d16f36 100644 --- a/packages/graph-node/src/loader.ts +++ b/packages/graph-node/src/loader.ts @@ -734,25 +734,13 @@ export const instantiate = async ( return __newString(dataSource.network); }, 'dataSource.create': async (name: number, params: number) => { - const [addressStringPtr] = __getArray(params); - const addressString = __getString(addressStringPtr); - const contractKind = __getString(name); - - assert(indexer.watchContract); - assert(context.block); - await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber)); + await handleDataSourceCreate(name, params); }, 'dataSource.createWithContext': async (name: number, params: number, dataSourceContext: number) => { - const [addressStringPtr] = __getArray(params); - const addressString = __getString(addressStringPtr); - const contractKind = __getString(name); - const contextInstance = await Entity.wrap(dataSourceContext); const dbData = await database.fromGraphContext(instanceExports, contextInstance); - assert(indexer.watchContract); - assert(context.block); - await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber), dbData); + await handleDataSourceCreate(name, params, dbData); } }, json: { @@ -786,6 +774,34 @@ export const instantiate = async ( } }; + const handleDataSourceCreate = async (name: number, params: number, dbData?: {[key: string]: any}) => { + const [addressStringPtr] = __getArray(params); + const addressString = __getString(addressStringPtr); + const contractKind = __getString(name); + + assert(context.block); + const contractAddress = utils.getAddress(addressString); + const watchedContracts = indexer.isContractAddressWatched(contractAddress); + + // If template contract is already watched (incase of reorgs) + // Remove from watched contracts and throw error to reprocess block with correct order of template contract events + if ( + watchedContracts && + watchedContracts.some(watchedContract => watchedContract.kind === contractKind) + ) { + await indexer.removeContract(contractAddress, contractKind); + throw new Error(`Template contract ${contractAddress} of kind ${contractKind} already exists; removed from watched contracts`); + } + + await indexer.watchContract( + contractAddress, + contractKind, + true, + Number(context.block.blockNumber), + dbData + ); + }; + const instance = await loader.instantiate(source, imports); const { exports: instanceExports } = instance; diff --git a/packages/util/src/indexer.ts b/packages/util/src/indexer.ts index 4fb16e22..9241dc57 100644 --- a/packages/util/src/indexer.ts +++ b/packages/util/src/indexer.ts @@ -920,6 +920,22 @@ export class Indexer { } } + async removeContract (address: string, kind: string): Promise { + const dbTx = await this._db.createTransactionRunner(); + + try { + await this._db.deleteEntitiesByConditions(dbTx, 'contract', { kind, address }); + this._clearWatchedContracts( + watchedContract => watchedContract.kind === kind && watchedContract.address === address + ); + } catch (error) { + await dbTx.rollbackTransaction(); + throw error; + } finally { + await dbTx.release(); + } + } + cacheContract (contract: ContractInterface): void { if (!this._watchedContractsByAddressMap[contract.address]) { this._watchedContractsByAddressMap[contract.address] = []; diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index 4ab61c29..5fde2b58 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -217,6 +217,7 @@ export interface IndexerInterface { addContracts?: () => Promise cacheContract: (contract: ContractInterface) => void; watchContract: (address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any) => Promise + removeContract: (address: string, kind: string) => Promise; getEntityTypesMap?: () => Map getRelationsMap?: () => Map processInitialState: (contractAddress: string, blockHash: string) => Promise