Skip to content

Commit

Permalink
add and query EBO subgraph with logging
Browse files Browse the repository at this point in the history
  • Loading branch information
hopeyen committed Sep 8, 2022
1 parent ea132db commit 5f9af3f
Show file tree
Hide file tree
Showing 18 changed files with 400 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ Protocol
deployment [string] [default: "0.01"]
--register Whether to register the indexer on chain
[boolean] [default: true]
--block-oracle-endpoint Endpoint to query epoch start blocks from
[string] [required]
Cost Models
--inject-dai Inject the GRT to DAI/USDC conversion rate into cost model
Expand Down
12 changes: 12 additions & 0 deletions docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -854,4 +854,16 @@ Failed to resolve POI: User provided POI does not match reference fetched from t

Check sync and health status of the subgraph to access the issue. If needed, provide a POI or use `--force` to bypass POI checks.

## IE069

**Summary**

Failed to query Block Oracle

**Solution**

Check `block-oracle-endpoint` query endpoint for its syncing status and the EBO contract state.




7 changes: 7 additions & 0 deletions packages/indexer-agent/src/__tests__/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
POIDisputeAttributes,
} from '@graphprotocol/indexer-common'
import { BigNumber, Wallet } from 'ethers'
import { BlockOracleSubgraph } from '../../../indexer-common/src/block-oracle-subgraph'
import { Sequelize } from 'sequelize'
import { Indexer } from '../indexer'

Expand Down Expand Up @@ -137,6 +138,11 @@ const setup = async () => {
'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-testnet',
deployment: undefined,
})
const blockOracleSubgraph = await BlockOracleSubgraph.create({
logger,
endpoint:
'https://api.thegraph.com/subgraphs/name/juanmardefago/block-oracle',
})

const indexNodeIDs = ['node_1']
indexerManagementClient = await createIndexerManagementClient({
Expand All @@ -147,6 +153,7 @@ const setup = async () => {
indexNodeIDs,
deploymentManagementEndpoint: statusEndpoint,
networkSubgraph,
blockOracleSubgraph,
logger,
defaults: {
globalIndexingRule: {
Expand Down
67 changes: 67 additions & 0 deletions packages/indexer-agent/src/agent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import {
Eventual,
join,
Logger,
Metrics,
Expand Down Expand Up @@ -30,6 +31,8 @@ import {
COST_MODEL_GLOBAL,
evaluateDeployments,
AllocationDecision,
NetworkEpochBlock,
NetworkAliases,
} from '@graphprotocol/indexer-common'
import { Indexer } from './indexer'
import { AgentConfig } from './types'
Expand Down Expand Up @@ -198,6 +201,37 @@ class Agent {
},
)

// TODO: once EBO consistent and stable, this can later replace contract call to currentEpoch
const globalLatestValidEpoch: Eventual<number> = timer(600_000).tryMap(
async () => await this.networkMonitor.latestValidEpoch(),
{
onError: error =>
this.logger.warn(
`Failed to obtain global latest valid epoch, trying again later`,
{
error,
},
),
},
)

const networkLatestEpochBlocks: Eventual<NetworkEpochBlock[]> = timer(
600_000,
).tryMap(
async () => {
return await this.networkMonitor.networkLatestEpochBlocks()
},
{
onError: error =>
this.logger.warn(
`Failed to obtain supported networks' latest epoch blocks, trying again later`,
{
error,
},
),
},
)

const channelDisputeEpochs = timer(600_000).tryMap(
() => this.network.contracts.staking.channelDisputeEpochs(),
{
Expand Down Expand Up @@ -426,6 +460,8 @@ class Agent {
claimableAllocations,
disputableAllocations,
costModels,
globalLatestValidEpoch,
networkLatestEpochBlocks,
}).pipe(
async ({
paused,
Expand All @@ -441,9 +477,40 @@ class Agent {
claimableAllocations,
disputableAllocations,
costModels,
globalLatestValidEpoch,
networkLatestEpochBlocks,
}) => {
this.logger.info(`Reconcile with the network`)

//TODO: remove at the end, this is for testing easier using logs
const networkID = `eip155:${this.network.ethereum._network.chainId}`
const testnetLatest = networkLatestEpochBlocks.find(
networkBlock => networkBlock.network === networkID,
)
const getBlock = testnetLatest
? await this.network.ethereum.getBlock(testnetLatest.blockNumber)
: 'EBO not syncing goerli testnet'

// reconcile aliasing networks for network epoch block number
const allocNetworkEpochBlockNumbers = activeAllocations.map(
allocation =>
networkLatestEpochBlocks.find(
async b =>
b.network ===
(await this.networkMonitor.deploymentNetwork(
allocation.subgraphDeployment.id,
)),
)?.blockNumber ?? currentEpoch.toNumber(),
)
this.logger.debug(`agent logger checks allocas`, {
networkID,
epochManager: currentEpoch.toNumber(),
globalLatestValidEpoch,
networkLatestEpochBlocks,
allocNetworkEpochBlockNumbers,
getBlock,
})

// Do nothing else if the network is paused
if (paused) {
return this.logger.info(
Expand Down
20 changes: 16 additions & 4 deletions packages/indexer-agent/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,16 @@ import {
Network,
NetworkSubgraph,
registerIndexerErrorMetrics,
AllocationManagementMode,
NetworkMonitor,
} from '@graphprotocol/indexer-common'
import { startAgent } from '../agent'
import { Indexer } from '../indexer'
import { providers, Wallet } from 'ethers'
import { startCostModelAutomation } from '../cost'
import { createSyncingServer } from '../syncing-server'
import { monitorEthBalance } from '../utils'
import {
AllocationManagementMode,
NetworkMonitor,
} from '@graphprotocol/indexer-common'
import { BlockOracleSubgraph } from '../../../indexer-common/src/block-oracle-subgraph'

export default {
command: 'start',
Expand Down Expand Up @@ -165,6 +164,12 @@ export default {
default: false,
group: 'Network Subgraph',
})
.option('block-oracle-endpoint', {
description: 'Endpoint to query the epoch block oracle subgraph from',
type: 'string',
required: true,
group: 'Protocol',
})
.option('index-node-ids', {
description:
'Node IDs of Graph nodes to use for indexing (separated by commas)',
Expand Down Expand Up @@ -717,6 +722,11 @@ export default {
: undefined,
})

const blockOracleSubgraph = await BlockOracleSubgraph.create({
logger,
endpoint: argv.blockOracleEndpoint,
})

logger.info('Connect to network')
const maxGasFee = argv.baseFeeGasMax || argv.gasPriceMax
const network = await Network.create(
Expand Down Expand Up @@ -761,6 +771,7 @@ export default {
logger,
indexingStatusResolver,
networkSubgraph,
blockOracleSubgraph,
ethereumProvider,
)

Expand All @@ -777,6 +788,7 @@ export default {
indexNodeIDs: argv.indexNodeIds,
deploymentManagementEndpoint: argv.graphNodeAdminEndpoint,
networkSubgraph,
blockOracleSubgraph,
logger,
defaults: {
globalIndexingRule: {
Expand Down
6 changes: 6 additions & 0 deletions packages/indexer-cli/src/__tests__/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
parseGRT,
toAddress,
} from '@graphprotocol/common-ts'
import { BlockOracleSubgraph } from '../../../indexer-common/src/block-oracle-subgraph'

declare const __DATABASE__: never
declare const __LOG_LEVEL__: never
Expand Down Expand Up @@ -67,6 +68,10 @@ export const setup = async () => {
'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-testnet',
deployment: undefined,
})
const blockOracleSubgraph = await BlockOracleSubgraph.create({
logger,
endpoint: 'https://thegraph.com/hosted-service/subgraph/juanmardefago/block-oracle',
})
const indexNodeIDs = ['node_1']
indexerManagementClient = await createIndexerManagementClient({
models,
Expand All @@ -76,6 +81,7 @@ export const setup = async () => {
indexNodeIDs,
deploymentManagementEndpoint: statusEndpoint,
networkSubgraph,
blockOracleSubgraph,
logger,
defaults: {
globalIndexingRule: {
Expand Down
18 changes: 18 additions & 0 deletions packages/indexer-common/src/allocations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,21 @@ export const parseGraphQLEpochs = (epoch: any): Epoch => ({
totalIndexerRewards: epoch.totalIndexerRewards,
totalDelegatorRewards: epoch.totalDelegatorRewards,
})

export interface NetworkEpochBlock {
network: string
blockNumber: number
}

export const NetworkAliases: { [key: string]: string } = {
mainnet: 'eip155:1',
rinkeby: 'eip155:4',
goerli: 'eip155:5',
gnosis: 'eip155:100',
}

/* eslint-disable @typescript-eslint/no-explicit-any */
export const parseGraphQLNetworkEpochBlocks = (block: any): NetworkEpochBlock => ({
network: block.network.id,
blockNumber: +block.blockNumber,
})
79 changes: 79 additions & 0 deletions packages/indexer-common/src/block-oracle-subgraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import axios, { AxiosInstance, AxiosResponse } from 'axios'
import { Logger } from '@graphprotocol/common-ts'
import { DocumentNode, print } from 'graphql'
import { OperationResult, CombinedError } from '@urql/core'

export interface BlockOracleSubgraphCreateOptions {
logger: Logger
endpoint: string
}

interface BlockOracleSubgraphOptions {
logger: Logger
endpoint: string
}

export type QueryResult<Data> = Pick<
OperationResult<Data>,
'error' | 'data' | 'extensions'
>

export class BlockOracleSubgraph {
logger: Logger
endpointClient: AxiosInstance

private constructor(options: BlockOracleSubgraphOptions) {
this.logger = options.logger

this.endpointClient = axios.create({
baseURL: options.endpoint,
headers: { 'content-type': 'application/json' },

// Don't parse responses as JSON
responseType: 'text',

// Don't transform responses
transformResponse: (data) => data,
})
}

public static async create({
logger: parentLogger,
endpoint,
}: BlockOracleSubgraphCreateOptions): Promise<BlockOracleSubgraph> {
const logger = parentLogger.child({
component: 'BlockOracleSubgraph',
endpoint,
})

// Create the EpochBlock subgraph instance
const blockOracleSubgraph = new BlockOracleSubgraph({
logger,
endpoint,
})
// Any checks to be made after creating?

return blockOracleSubgraph
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
async query<Data = any>(
query: DocumentNode,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
variables?: Record<string, any>,
): Promise<QueryResult<Data>> {
const response = await this.endpointClient.post('', {
query: print(query),
variables,
})
const data = JSON.parse(response.data)
if (data.errors) {
return { error: new CombinedError({ graphQLErrors: data.errors }) }
}
return data
}

async queryRaw(body: string): Promise<AxiosResponse> {
return await this.endpointClient.post('', body)
}
}
2 changes: 2 additions & 0 deletions packages/indexer-common/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export enum IndexerErrorCode {
IE066 = 'IE066',
IE067 = 'IE067',
IE068 = 'IE068',
IE069 = 'IE069',
}

export const INDEXER_ERROR_MESSAGES: Record<IndexerErrorCode, string> = {
Expand Down Expand Up @@ -151,6 +152,7 @@ export const INDEXER_ERROR_MESSAGES: Record<IndexerErrorCode, string> = {
IE066: 'Failed to allocate: allocation ID already exists on chain',
IE067: 'Failed to query POI for current epoch start block',
IE068: 'User-provided POI did not match reference POI from graph-node',
IE069: 'Failed to query Epoch Block Oracle',
}

export type IndexerErrorCause = unknown
Expand Down
Loading

0 comments on commit 5f9af3f

Please sign in to comment.