diff --git a/lerna.json b/lerna.json index b91965a9..505cd75a 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.2.107", + "version": "0.2.108", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/packages/cache/package.json b/packages/cache/package.json index 02ee4738..afec33e5 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/cache", - "version": "0.2.107", + "version": "0.2.108", "description": "Generic object cache", "main": "dist/index.js", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 1ca51503..5596a701 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/cli", - "version": "0.2.107", + "version": "0.2.108", "main": "dist/index.js", "license": "AGPL-3.0", "scripts": { @@ -15,13 +15,13 @@ }, "dependencies": { "@apollo/client": "^3.7.1", - "@cerc-io/cache": "^0.2.107", - "@cerc-io/ipld-eth-client": "^0.2.107", + "@cerc-io/cache": "^0.2.108", + "@cerc-io/ipld-eth-client": "^0.2.108", "@cerc-io/libp2p": "^0.42.2-laconic-0.1.4", "@cerc-io/nitro-node": "^0.1.15", - "@cerc-io/peer": "^0.2.107", - "@cerc-io/rpc-eth-client": "^0.2.107", - "@cerc-io/util": "^0.2.107", + "@cerc-io/peer": "^0.2.108", + "@cerc-io/rpc-eth-client": "^0.2.108", + "@cerc-io/util": "^0.2.108", "@ethersproject/providers": "^5.4.4", "@graphql-tools/utils": "^9.1.1", "@ipld/dag-cbor": "^8.0.0", diff --git a/packages/codegen/package.json b/packages/codegen/package.json index 750f57fc..a0ad5db7 100644 --- a/packages/codegen/package.json +++ b/packages/codegen/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/codegen", - "version": "0.2.107", + "version": "0.2.108", "description": "Code generator", "private": true, "main": "index.js", @@ -20,7 +20,7 @@ }, "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { - "@cerc-io/util": "^0.2.107", + "@cerc-io/util": "^0.2.108", "@graphql-tools/load-files": "^6.5.2", "@npmcli/package-json": "^5.0.0", "@poanet/solidity-flattener": "https://github.com/vulcanize/solidity-flattener.git", 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/codegen/src/templates/package-template.handlebars b/packages/codegen/src/templates/package-template.handlebars index 8605251f..b0fefde3 100644 --- a/packages/codegen/src/templates/package-template.handlebars +++ b/packages/codegen/src/templates/package-template.handlebars @@ -41,12 +41,12 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.107", - "@cerc-io/ipld-eth-client": "^0.2.107", - "@cerc-io/solidity-mapper": "^0.2.107", - "@cerc-io/util": "^0.2.107", + "@cerc-io/cli": "^0.2.108", + "@cerc-io/ipld-eth-client": "^0.2.108", + "@cerc-io/solidity-mapper": "^0.2.108", + "@cerc-io/util": "^0.2.108", {{#if (subgraphPath)}} - "@cerc-io/graph-node": "^0.2.107", + "@cerc-io/graph-node": "^0.2.108", {{/if}} "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", diff --git a/packages/graph-node/package.json b/packages/graph-node/package.json index 4c59dab7..b9580456 100644 --- a/packages/graph-node/package.json +++ b/packages/graph-node/package.json @@ -1,10 +1,10 @@ { "name": "@cerc-io/graph-node", - "version": "0.2.107", + "version": "0.2.108", "main": "dist/index.js", "license": "AGPL-3.0", "devDependencies": { - "@cerc-io/solidity-mapper": "^0.2.107", + "@cerc-io/solidity-mapper": "^0.2.108", "@ethersproject/providers": "^5.4.4", "@graphprotocol/graph-ts": "^0.22.0", "@nomiclabs/hardhat-ethers": "^2.0.2", @@ -51,9 +51,9 @@ "dependencies": { "@apollo/client": "^3.3.19", "@cerc-io/assemblyscript": "0.19.10-watcher-ts-0.1.2", - "@cerc-io/cache": "^0.2.107", - "@cerc-io/ipld-eth-client": "^0.2.107", - "@cerc-io/util": "^0.2.107", + "@cerc-io/cache": "^0.2.108", + "@cerc-io/ipld-eth-client": "^0.2.108", + "@cerc-io/util": "^0.2.108", "@types/json-diff": "^0.5.2", "@types/yargs": "^17.0.0", "bn.js": "^4.11.9", 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/graph-node/test/utils/indexer.ts b/packages/graph-node/test/utils/indexer.ts index c26caf0c..e37e3b9c 100644 --- a/packages/graph-node/test/utils/indexer.ts +++ b/packages/graph-node/test/utils/indexer.ts @@ -276,6 +276,10 @@ export class Indexer implements IndexerInterface { return undefined; } + async removeContract (address: string, kind: string): Promise { + return undefined; + } + async processBlock (blockProgress: BlockProgressInterface): Promise { return undefined; } diff --git a/packages/ipld-eth-client/package.json b/packages/ipld-eth-client/package.json index 41803ac8..2d84ab4d 100644 --- a/packages/ipld-eth-client/package.json +++ b/packages/ipld-eth-client/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/ipld-eth-client", - "version": "0.2.107", + "version": "0.2.108", "description": "IPLD ETH Client", "main": "dist/index.js", "scripts": { @@ -20,8 +20,8 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.7.1", - "@cerc-io/cache": "^0.2.107", - "@cerc-io/util": "^0.2.107", + "@cerc-io/cache": "^0.2.108", + "@cerc-io/util": "^0.2.108", "cross-fetch": "^3.1.4", "debug": "^4.3.1", "ethers": "^5.4.4", diff --git a/packages/peer/package.json b/packages/peer/package.json index db89e39d..c2278ef7 100644 --- a/packages/peer/package.json +++ b/packages/peer/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/peer", - "version": "0.2.107", + "version": "0.2.108", "description": "libp2p module", "main": "dist/index.js", "exports": "./dist/index.js", diff --git a/packages/rpc-eth-client/package.json b/packages/rpc-eth-client/package.json index 0fa9b939..2c915b52 100644 --- a/packages/rpc-eth-client/package.json +++ b/packages/rpc-eth-client/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/rpc-eth-client", - "version": "0.2.107", + "version": "0.2.108", "description": "RPC ETH Client", "main": "dist/index.js", "scripts": { @@ -19,9 +19,9 @@ }, "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { - "@cerc-io/cache": "^0.2.107", - "@cerc-io/ipld-eth-client": "^0.2.107", - "@cerc-io/util": "^0.2.107", + "@cerc-io/cache": "^0.2.108", + "@cerc-io/ipld-eth-client": "^0.2.108", + "@cerc-io/util": "^0.2.108", "chai": "^4.3.4", "ethers": "^5.4.4", "left-pad": "^1.3.0", diff --git a/packages/solidity-mapper/package.json b/packages/solidity-mapper/package.json index ee2038a8..ae180933 100644 --- a/packages/solidity-mapper/package.json +++ b/packages/solidity-mapper/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/solidity-mapper", - "version": "0.2.107", + "version": "0.2.108", "main": "dist/index.js", "license": "AGPL-3.0", "devDependencies": { diff --git a/packages/test/package.json b/packages/test/package.json index 8c8d5248..787f9530 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/test", - "version": "0.2.107", + "version": "0.2.108", "main": "dist/index.js", "license": "AGPL-3.0", "private": true, diff --git a/packages/tracing-client/package.json b/packages/tracing-client/package.json index 6db7e6aa..ad71d7a2 100644 --- a/packages/tracing-client/package.json +++ b/packages/tracing-client/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/tracing-client", - "version": "0.2.107", + "version": "0.2.108", "description": "ETH VM tracing client", "main": "dist/index.js", "scripts": { diff --git a/packages/util/package.json b/packages/util/package.json index 7c60e7c9..3e47437f 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,13 +1,13 @@ { "name": "@cerc-io/util", - "version": "0.2.107", + "version": "0.2.108", "main": "dist/index.js", "license": "AGPL-3.0", "dependencies": { "@apollo/utils.keyvaluecache": "^1.0.1", "@cerc-io/nitro-node": "^0.1.15", - "@cerc-io/peer": "^0.2.107", - "@cerc-io/solidity-mapper": "^0.2.107", + "@cerc-io/peer": "^0.2.108", + "@cerc-io/solidity-mapper": "^0.2.108", "@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1", "@ethersproject/properties": "^5.7.0", "@ethersproject/providers": "^5.4.4", @@ -55,7 +55,7 @@ "yargs": "^17.0.1" }, "devDependencies": { - "@cerc-io/cache": "^0.2.107", + "@cerc-io/cache": "^0.2.108", "@nomiclabs/hardhat-waffle": "^2.0.1", "@types/bunyan": "^1.8.8", "@types/express": "^4.17.14", 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/server.ts b/packages/util/src/server.ts index c9e0f47b..03745226 100644 --- a/packages/util/src/server.ts +++ b/packages/util/src/server.ts @@ -97,23 +97,32 @@ export const createAndStartServer = async ( await server.start(); - server.applyMiddleware({ - app, - path: gqlPath - }); - const rpcPath = serverConfig.ethRPC?.path ?? DEFAULT_ETH_RPC_PATH; + const rpcEnabled = serverConfig.ethRPC?.enabled; + + // Apply GraphQL middleware + const applyGraphQLMiddleware = () => { + server.applyMiddleware({ + app, + path: gqlPath + }); + }; + + // Apply RPC middleware + const applyRPCMiddleware = () => { + if (!rpcEnabled) { + return; + } - if (serverConfig.ethRPC?.enabled) { // Create a JSON-RPC server to handle ETH RPC calls const rpcServer = jayson.Server(ethRPCHandlers); - // Mount the JSON-RPC server to ETH_RPC_PATH + // Mount the JSON-RPC server to rpcPath app.use( rpcPath, jsonParser(), (req: any, res: any, next: () => void) => { - // Convert all GET requests to POST to avoid getting rejected from jayson server middleware + // Convert all GET requests to POST to avoid getting rejected by jayson server middleware if (jayson.Utils.isMethod(req, 'GET')) { req.method = 'POST'; } @@ -121,15 +130,32 @@ export const createAndStartServer = async ( }, rpcServer.middleware() ); + }; + + // Apply middlewares based on path specificity + if (isPathMoreSpecific(rpcPath, gqlPath)) { + applyRPCMiddleware(); + applyGraphQLMiddleware(); + } else { + applyGraphQLMiddleware(); + applyRPCMiddleware(); } httpServer.listen(port, host, () => { - log(`GQL server is listening on http://${host}:${port}${server.graphqlPath}`); + log(`GQL server is listening on http://${host}:${port}${gqlPath}`); - if (serverConfig.ethRPC?.enabled) { + if (rpcEnabled) { log(`ETH JSON RPC server is listening on http://${host}:${port}${rpcPath}`); } }); return server; }; + +// Determine which path is more specific (more segments) +function isPathMoreSpecific (path1: string, path2: string) { + const path1Segments = path1.split('/').filter(segment => segment !== ''); + const path2Segments = path2.split('/').filter(segment => segment !== ''); + + return path1Segments.length > path2Segments.length; +} 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