diff --git a/lerna.json b/lerna.json index a17f908d..6ecb27c2 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.2.88", + "version": "0.2.89", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/packages/cache/package.json b/packages/cache/package.json index af178ee9..fbbd4683 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/cache", - "version": "0.2.88", + "version": "0.2.89", "description": "Generic object cache", "main": "dist/index.js", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 3840e5f8..b837eca6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/cli", - "version": "0.2.88", + "version": "0.2.89", "main": "dist/index.js", "license": "AGPL-3.0", "scripts": { @@ -15,13 +15,13 @@ }, "dependencies": { "@apollo/client": "^3.7.1", - "@cerc-io/cache": "^0.2.88", - "@cerc-io/ipld-eth-client": "^0.2.88", + "@cerc-io/cache": "^0.2.89", + "@cerc-io/ipld-eth-client": "^0.2.89", "@cerc-io/libp2p": "^0.42.2-laconic-0.1.4", "@cerc-io/nitro-node": "^0.1.15", - "@cerc-io/peer": "^0.2.88", - "@cerc-io/rpc-eth-client": "^0.2.88", - "@cerc-io/util": "^0.2.88", + "@cerc-io/peer": "^0.2.89", + "@cerc-io/rpc-eth-client": "^0.2.89", + "@cerc-io/util": "^0.2.89", "@ethersproject/providers": "^5.4.4", "@graphql-tools/utils": "^9.1.1", "@ipld/dag-cbor": "^8.0.0", diff --git a/packages/cli/src/chain-head-exporter.ts b/packages/cli/src/chain-head-exporter.ts index ef115e9e..af2c9b90 100644 --- a/packages/cli/src/chain-head-exporter.ts +++ b/packages/cli/src/chain-head-exporter.ts @@ -8,8 +8,6 @@ import debug from 'debug'; import { ethers } from 'ethers'; import JsonRpcProvider = ethers.providers.JsonRpcProvider; -import { fetchLatestBlockNumber } from '@cerc-io/util'; - const log = debug('laconic:chain-head-exporter'); // Env overrides: @@ -57,16 +55,20 @@ async function main (): Promise { registers: [metricsRegister], labelNames: ['chain'] as const, async collect () { - const [ - latestEthBlockNumber, - latestFilBlockNumber - ] = await Promise.all([ - fetchLatestBlockNumber(ethProvider), - fetchLatestBlockNumber(filProvider) - ]); + try { + const [ + latestEthBlockNumber, + latestFilBlockNumber + ] = await Promise.all([ + ethProvider.getBlockNumber(), + filProvider.getBlockNumber() + ]); - this.set({ chain: 'ethereum' }, latestEthBlockNumber); - this.set({ chain: 'filecoin' }, latestFilBlockNumber); + this.set({ chain: 'ethereum' }, latestEthBlockNumber); + this.set({ chain: 'filecoin' }, latestFilBlockNumber); + } catch (err) { + log('Error fetching latest block number', err); + } } }); diff --git a/packages/codegen/package.json b/packages/codegen/package.json index 464d822a..63f75e2d 100644 --- a/packages/codegen/package.json +++ b/packages/codegen/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/codegen", - "version": "0.2.88", + "version": "0.2.89", "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.88", + "@cerc-io/util": "^0.2.89", "@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 b44b7eaa..a606a69b 100644 --- a/packages/codegen/src/templates/indexer-template.handlebars +++ b/packages/codegen/src/templates/indexer-template.handlebars @@ -10,7 +10,7 @@ import JSONbig from 'json-bigint'; {{/if}} import { ethers, constants } from 'ethers'; {{#if (subgraphPath)}} -import { SelectionNode } from 'graphql'; +import { GraphQLResolveInfo } from 'graphql'; {{/if}} import { JsonFragment } from '@ethersproject/abi'; @@ -458,9 +458,9 @@ export class Indexer implements IndexerInterface { entity: new () => Entity, id: string, block: BlockHeight, - selections: ReadonlyArray = [] + queryInfo: GraphQLResolveInfo ): Promise { - const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, selections); + const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, queryInfo); return data; } @@ -470,9 +470,9 @@ export class Indexer implements IndexerInterface { block: BlockHeight, where: { [key: string]: any } = {}, queryOptions: QueryOptions = {}, - selections: ReadonlyArray = [] + queryInfo: GraphQLResolveInfo ): Promise { - return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, selections); + return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, queryInfo); } {{/if}} diff --git a/packages/codegen/src/templates/package-template.handlebars b/packages/codegen/src/templates/package-template.handlebars index 34c65467..7793d1a6 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.88", - "@cerc-io/ipld-eth-client": "^0.2.88", - "@cerc-io/solidity-mapper": "^0.2.88", - "@cerc-io/util": "^0.2.88", + "@cerc-io/cli": "^0.2.89", + "@cerc-io/ipld-eth-client": "^0.2.89", + "@cerc-io/solidity-mapper": "^0.2.89", + "@cerc-io/util": "^0.2.89", {{#if (subgraphPath)}} - "@cerc-io/graph-node": "^0.2.88", + "@cerc-io/graph-node": "^0.2.89", {{/if}} "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", diff --git a/packages/codegen/src/templates/resolvers-template.handlebars b/packages/codegen/src/templates/resolvers-template.handlebars index fd4f99ae..f8808b54 100644 --- a/packages/codegen/src/templates/resolvers-template.handlebars +++ b/packages/codegen/src/templates/resolvers-template.handlebars @@ -106,12 +106,11 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher log('{{this.queryName}}', id, JSON.stringify(block, jsonBigIntStringReplacer)); gqlTotalQueryCount.inc(1); gqlQueryCount.labels('{{this.queryName}}').inc(1); - assert(info.fieldNodes[0].selectionSet); // Set cache-control hints // setGQLCacheHints(info, block, gqlCacheConfig); - return indexer.getSubgraphEntity({{this.entityName}}, id, block, info.fieldNodes[0].selectionSet.selections); + return indexer.getSubgraphEntity({{this.entityName}}, id, block, info); }, {{this.pluralQueryName}}: async ( @@ -123,7 +122,6 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher log('{{this.pluralQueryName}}', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); gqlTotalQueryCount.inc(1); gqlQueryCount.labels('{{this.pluralQueryName}}').inc(1); - assert(info.fieldNodes[0].selectionSet); // Set cache-control hints // setGQLCacheHints(info, block, gqlCacheConfig); @@ -133,7 +131,7 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher block, where, { limit: first, skip, orderBy, orderDirection }, - info.fieldNodes[0].selectionSet.selections + info ); }, diff --git a/packages/graph-node/package.json b/packages/graph-node/package.json index 4ca6c4fd..b00ce06b 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.88", + "version": "0.2.89", "main": "dist/index.js", "license": "AGPL-3.0", "devDependencies": { - "@cerc-io/solidity-mapper": "^0.2.88", + "@cerc-io/solidity-mapper": "^0.2.89", "@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.88", - "@cerc-io/ipld-eth-client": "^0.2.88", - "@cerc-io/util": "^0.2.88", + "@cerc-io/cache": "^0.2.89", + "@cerc-io/ipld-eth-client": "^0.2.89", + "@cerc-io/util": "^0.2.89", "@types/json-diff": "^0.5.2", "@types/yargs": "^17.0.0", "bn.js": "^4.11.9", diff --git a/packages/graph-node/src/watcher.ts b/packages/graph-node/src/watcher.ts index 595653ab..30d89141 100644 --- a/packages/graph-node/src/watcher.ts +++ b/packages/graph-node/src/watcher.ts @@ -8,7 +8,7 @@ import debug from 'debug'; import path from 'path'; import fs from 'fs'; import { ContractInterface, utils, providers } from 'ethers'; -import { SelectionNode } from 'graphql'; +import { GraphQLResolveInfo, SelectionNode } from 'graphql'; import { ResultObject } from '@cerc-io/assemblyscript/lib/loader'; import { @@ -321,13 +321,15 @@ export class GraphWatcher { id: string, relationsMap: Map, block: BlockHeight, - selections: ReadonlyArray = [] + queryInfo: GraphQLResolveInfo ): Promise { const dbTx = await this._database.createTransactionRunner(); try { + const selections = this._getSelectionsFromGQLInfo(queryInfo); + // Get entity from the database. - const result = await this._database.getEntityWithRelations(dbTx, entity, id, relationsMap, block, selections); + const result = await this._database.getEntityWithRelations(dbTx, entity, id, relationsMap, block, selections, queryInfo); await dbTx.commitTransaction(); // Resolve any field name conflicts in the entity result. @@ -346,7 +348,7 @@ export class GraphWatcher { block: BlockHeight, where: { [key: string]: any } = {}, queryOptions: QueryOptions, - selections: ReadonlyArray = [] + queryInfo: GraphQLResolveInfo ): Promise { const dbTx = await this._database.createTransactionRunner(); @@ -357,8 +359,10 @@ export class GraphWatcher { queryOptions.limit = DEFAULT_LIMIT; } + const selections = this._getSelectionsFromGQLInfo(queryInfo); + // Get entities from the database. - const entities = await this._database.getEntities(dbTx, entity, relationsMap, block, where, queryOptions, selections); + const entities = await this._database.getEntities(dbTx, entity, relationsMap, block, where, queryOptions, selections, queryInfo); await dbTx.commitTransaction(); return entities; @@ -553,6 +557,14 @@ export class GraphWatcher { return acc; }, {}); } + + _getSelectionsFromGQLInfo (queryInfo: GraphQLResolveInfo): readonly SelectionNode[] { + const [fieldNode] = queryInfo.fieldNodes; + const selectionSet = fieldNode.selectionSet; + assert(selectionSet, `selectionSet not present in GQL fieldNode ${fieldNode.name}`); + + return selectionSet.selections; + } } export const getGraphDbAndWatcher = async ( diff --git a/packages/ipld-eth-client/package.json b/packages/ipld-eth-client/package.json index df3f1443..8317658b 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.88", + "version": "0.2.89", "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.88", - "@cerc-io/util": "^0.2.88", + "@cerc-io/cache": "^0.2.89", + "@cerc-io/util": "^0.2.89", "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 b974cd7e..9c42cca0 100644 --- a/packages/peer/package.json +++ b/packages/peer/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/peer", - "version": "0.2.88", + "version": "0.2.89", "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 4ef26823..2def3779 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.88", + "version": "0.2.89", "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.88", - "@cerc-io/ipld-eth-client": "^0.2.88", - "@cerc-io/util": "^0.2.88", + "@cerc-io/cache": "^0.2.89", + "@cerc-io/ipld-eth-client": "^0.2.89", + "@cerc-io/util": "^0.2.89", "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 fda240bf..6acf194e 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.88", + "version": "0.2.89", "main": "dist/index.js", "license": "AGPL-3.0", "devDependencies": { diff --git a/packages/test/package.json b/packages/test/package.json index b49c12c5..cdbce7b8 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/test", - "version": "0.2.88", + "version": "0.2.89", "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 98bd688e..384f5e8c 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.88", + "version": "0.2.89", "description": "ETH VM tracing client", "main": "dist/index.js", "scripts": { diff --git a/packages/util/package.json b/packages/util/package.json index 4e5cbc34..2a3ed160 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,13 +1,13 @@ { "name": "@cerc-io/util", - "version": "0.2.88", + "version": "0.2.89", "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.88", - "@cerc-io/solidity-mapper": "^0.2.88", + "@cerc-io/peer": "^0.2.89", + "@cerc-io/solidity-mapper": "^0.2.89", "@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1", "@ethersproject/properties": "^5.7.0", "@ethersproject/providers": "^5.4.4", @@ -52,7 +52,7 @@ "yargs": "^17.0.1" }, "devDependencies": { - "@cerc-io/cache": "^0.2.88", + "@cerc-io/cache": "^0.2.89", "@nomiclabs/hardhat-waffle": "^2.0.1", "@types/bunyan": "^1.8.8", "@types/express": "^4.17.14", diff --git a/packages/util/src/graph/database.ts b/packages/util/src/graph/database.ts index 33162bc4..94645d52 100644 --- a/packages/util/src/graph/database.ts +++ b/packages/util/src/graph/database.ts @@ -17,7 +17,7 @@ import { } from 'typeorm'; import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'; import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer'; -import { SelectionNode } from 'graphql'; +import { GraphQLResolveInfo, SelectionNode } from 'graphql'; import _ from 'lodash'; import debug from 'debug'; @@ -221,7 +221,8 @@ export class GraphDatabase { id: string, relationsMap: Map, block: CanonicalBlockHeight = {}, - selections: ReadonlyArray = [] + selections: ReadonlyArray = [], + queryInfo: GraphQLResolveInfo ): Promise { const { hash: blockHash, number: blockNumber } = block; const repo = queryRunner.manager.getRepository(entityType); @@ -248,7 +249,7 @@ export class GraphDatabase { // Get relational fields if (entityData) { - entityData = await this.loadEntityRelations(queryRunner, block, relationsMap, entityType, entityData, selections); + entityData = await this.loadEntityRelations(queryRunner, block, relationsMap, entityType, entityData, selections, queryInfo); } return entityData; @@ -259,7 +260,8 @@ export class GraphDatabase { block: CanonicalBlockHeight, relationsMap: Map, entityType: new () => Entity, entityData: any, - selections: ReadonlyArray = [] + selections: ReadonlyArray = [], + queryInfo: GraphQLResolveInfo ): Promise { const relations = relationsMap.get(entityType); if (relations === undefined) { @@ -292,7 +294,8 @@ export class GraphDatabase { block, where, { limit: DEFAULT_LIMIT }, - childSelections + childSelections, + queryInfo ); entityData[field] = relatedEntities; @@ -316,7 +319,8 @@ export class GraphDatabase { block, where, { limit: DEFAULT_LIMIT }, - childSelections + childSelections, + queryInfo ); entityData[field] = relatedEntities; @@ -331,7 +335,8 @@ export class GraphDatabase { entityData[field], relationsMap, block, - childSelections + childSelections, + queryInfo ); entityData[field] = relatedEntity; @@ -349,11 +354,14 @@ export class GraphDatabase { block: CanonicalBlockHeight = {}, where: Where = {}, queryOptions: QueryOptions = {}, - selections: ReadonlyArray = [] + selections: ReadonlyArray = [], + queryInfo: GraphQLResolveInfo ): Promise { let entities: Entity[] = []; const latestEntityType = this._entityToLatestEntityMap.get(entityType); + const defragmentedSelections = this._defragmentGQLQuerySelections(selections, queryInfo); + if (latestEntityType) { if (Object.keys(block).length) { // Use lateral query for entities with latest entity table. @@ -375,7 +383,7 @@ export class GraphDatabase { relationsMap, where, queryOptions, - selections + defragmentedSelections ); } } else { @@ -405,7 +413,7 @@ export class GraphDatabase { return []; } - entities = await this.loadEntitiesRelations(queryRunner, block, relationsMap, entityType, entities, selections); + entities = await this.loadEntitiesRelations(queryRunner, block, relationsMap, entityType, entities, defragmentedSelections, queryInfo); // Resolve any field name conflicts in the entity result. entities = entities.map(entity => resolveEntityFieldConflicts(entity)); @@ -744,7 +752,8 @@ export class GraphDatabase { relationsMap: Map, entity: new () => Entity, entities: Entity[], - selections: ReadonlyArray = [] + selections: ReadonlyArray = [], + queryInfo: GraphQLResolveInfo ): Promise { const relations = relationsMap.get(entity); if (relations === undefined) { @@ -755,11 +764,11 @@ export class GraphDatabase { if (this._serverConfig.loadRelationsSequential) { for (const selection of relationSelections) { - await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection); + await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection, queryInfo); } } else { const loadRelationPromises = relationSelections.map(async selection => { - await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection); + await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection, queryInfo); }); await Promise.all(loadRelationPromises); @@ -774,7 +783,8 @@ export class GraphDatabase { relationsMap: Map, relations: { [key: string]: any }, entities: Entity[], - selection: SelectionNode + selection: SelectionNode, + queryInfo: GraphQLResolveInfo ): Promise { assert(selection.kind === 'Field'); const field = selection.name.value; @@ -800,7 +810,8 @@ export class GraphDatabase { block, where, {}, - childSelections + childSelections, + queryInfo ); const relatedEntitiesMap = relatedEntities.reduce((acc: {[key:string]: any[]}, entity: any) => { @@ -851,7 +862,8 @@ export class GraphDatabase { block, where, {}, - childSelections + childSelections, + queryInfo ); entities.forEach((entity: any) => { @@ -902,7 +914,8 @@ export class GraphDatabase { block, where, {}, - childSelections + childSelections, + queryInfo ); const relatedEntitiesMap = relatedEntities.reduce((acc: {[key:string]: any}, entity: any) => { @@ -1321,4 +1334,16 @@ export class GraphDatabase { log(`Total entities in cachedEntities.latestPrunedEntities map: ${totalEntities}`); cachePrunedEntitiesCount.set(totalEntities); } + + _defragmentGQLQuerySelections (selections: ReadonlyArray, queryInfo: GraphQLResolveInfo): SelectionNode[] { + return selections.reduce((acc: SelectionNode[], selection) => { + if (selection.kind === 'FragmentSpread') { + const fragmentSelections = queryInfo.fragments[selection.name.value].selectionSet.selections; + + return [...acc, ...fragmentSelections]; + } + + return [...acc, selection]; + }, []); + } } diff --git a/packages/util/src/metrics.ts b/packages/util/src/metrics.ts index 9605d318..6842ca92 100644 --- a/packages/util/src/metrics.ts +++ b/packages/util/src/metrics.ts @@ -18,15 +18,6 @@ const DB_SIZE_QUERY = 'SELECT pg_database_size(current_database())'; const log = debug('vulcanize:metrics'); -export async function fetchLatestBlockNumber (provider: JsonRpcProvider): Promise { - try { - return await provider.getBlockNumber(); - } catch (err) { - log('Error fetching latest block number', err); - return -1; - } -} - // Create custom metrics export const lastJobCompletedOn = new client.Gauge({ @@ -212,8 +203,12 @@ const registerUpstreamChainHeadMetrics = async ({ upstream }: Config, rpcProvide name: 'latest_upstream_block_number', help: 'Latest upstream block number', async collect () { - const latestBlockNumber = await fetchLatestBlockNumber(ethRpcProvider); - this.set(latestBlockNumber); + try { + const blockNumber = await ethRpcProvider.getBlockNumber(); + this.set(blockNumber); + } catch (err) { + log('Error fetching latest block number', err); + } } }); };