Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: indexing fees (DIPs) - WIP - rebased #1094

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions docs/action-queue.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ The action execution worker will only grab items from the action queue to execut

## Allocation management modes:
- `auto`: The indexer-agent will act similarly to the legacy paradigm. When it identifies allocation actions it will add them to the queue with ActionStatus = `approved`; the execution worker process will pick up the approved actions within 30 seconds and execute them.
- `manual`: The indexer-agent will not add any items to the action queue in this mode. It will spin up an indexer-management server which can be interacted with manually or integrated with 3rd party tools to add actions to the action queue and execute them.
- `oversight`: The indexer-agent will add run its reconciliation loop to make allocation decisions and when actions are identified it will queue them. These actions will then require approval before they can be executed.
- `manual`: The indexer-agent will not add any items to the action queue in this mode. It will spin up an indexer-management server which can be interacted with manually or integrated with 3rd party tools to add actions to the action queue and execute them. An exception to this is indexing agreements (DIPs), for which actions will be queued and executed even in this mode.
- `oversight`: The indexer-agent will add run its reconciliation loop to make allocation decisions and when actions are identified it will queue them. These actions will then require approval before they can be executed. An exception to this is indexing agreements (DIPs), for which actions will be queued as approved and executed even in this mode.

## Actions CLI
The indexer-cli provides an `actions` module for manually working with the action queue. It uses the #Graphql API hosted by the indexer management server to interact with the actions queue.
Expand Down
4 changes: 2 additions & 2 deletions docs/networks/arbitrum-sepolia.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ The Graph Network's testnet is on Arbitrum Sepolia (eip155:421614). Sepolia netw
| Component | Release |
| ------------------ | ------------------------------------------------------------------------------------ |
| contracts | [5.3.3](https://github.com/graphprotocol/contracts/releases/tag/v5.3.3) |
| indexer-agent | [0.22.0](https://github.com/graphprotocol/indexer/releases/tag/v0.22.0) |
| indexer-cli | [0.22.0](https://github.com/graphprotocol/indexer/releases/tag/v0.22.0) |
| indexer-agent | [0.23.3](https://github.com/graphprotocol/indexer/releases/tag/v0.23.3) |
| indexer-cli | [0.23.3](https://github.com/graphprotocol/indexer/releases/tag/v0.23.3) |
| indexer-service-rs | [1.0.0](https://github.com/graphprotocol/indexer-rs/releases/tag/v1.0.0) |
| tap-agent | [1.0.0](https://github.com/graphprotocol/indexer-rs/releases/tag/v1.0.0) |
| graph-node | [0.35.1](https://github.com/graphprotocol/graph-node/releases/tag/v0.35.1) |
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.22.0"
"version": "0.23.3"
}
5 changes: 2 additions & 3 deletions packages/indexer-agent/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@graphprotocol/indexer-agent",
"version": "0.22.0",
"version": "0.23.3",
"description": "Indexer agent",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand All @@ -10,7 +10,6 @@
],
"repository": "https://github.com/graphprotocol/indexer",
"author": "Graph Protocol",
"private": false,
"scripts": {
"format": "prettier --write 'src/**/*.ts'",
"lint": "eslint . --ext .ts,.tsx --fix",
Expand All @@ -30,7 +29,7 @@
},
"dependencies": {
"@graphprotocol/common-ts": "2.0.11",
"@graphprotocol/indexer-common": "^0.22.0",
"@graphprotocol/indexer-common": "^0.23.3",
"@thi.ng/heaps": "^1.3.1",
"axios": "0.26.1",
"bs58": "5.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/indexer-agent/src/__tests__/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const setup = async () => {
const network = await Network.create(
logger,
networkSpecification,
models,
queryFeeModels,
graphNode,
metrics,
Expand Down
99 changes: 81 additions & 18 deletions packages/indexer-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ export class Agent {
sequentialTimerMap(
{ logger, milliseconds: requestIntervalSmall },
async () => {
if (network.specification.indexerOptions.enableDips) {
// There should be a DipsManager in the operator
if (!operator.dipsManager) {
throw new Error('DipsManager is not available')
}
logger.trace('Ensuring indexing rules for DIPS', {
protocolNetwork: network.specification.networkIdentifier,
})
await operator.dipsManager.ensureAgreementRules()
}
logger.trace('Fetching indexing rules', {
protocolNetwork: network.specification.networkIdentifier,
})
Expand Down Expand Up @@ -252,14 +262,15 @@ export class Agent {
},
)

// Skip fetching active deployments if the deployment management mode is manual and POI tracking is disabled
// Skip fetching active deployments if the deployment management mode is manual, DIPs is disabled, and POI tracking is disabled
const activeDeployments: Eventual<SubgraphDeploymentID[]> =
sequentialTimerMap(
{ logger, milliseconds: requestIntervalLarge },
async () => {
if (
this.deploymentManagement === DeploymentManagementMode.AUTO ||
network.networkMonitor.poiDisputeMonitoringEnabled()
network.networkMonitor.poiDisputeMonitoringEnabled() ||
network.specification.indexerOptions.enableDips
) {
logger.trace('Fetching active deployments')
const assignments =
Expand Down Expand Up @@ -487,9 +498,40 @@ export class Agent {
}
break
case DeploymentManagementMode.MANUAL:
this.logger.debug(
`Skipping subgraph deployment reconciliation since DeploymentManagementMode = 'manual'`,
)
if (network.specification.indexerOptions.enableDips) {
// Reconcile DIPs deployments anyways
this.logger.warn(
`Deployment management is manual, but DIPs is enabled. Reconciling DIPs deployments anyways.`,
)
if (!operator.dipsManager) {
throw new Error('DipsManager is not available')
}
const dipsDeployments =
await operator.dipsManager.getActiveDipsDeployments()
const newTargetDeployments = new Set([
...activeDeployments,
...dipsDeployments,
])
try {
await this.reconcileDeployments(
activeDeployments,
Array.from(newTargetDeployments),
eligibleAllocations,
)
} catch (err) {
logger.warn(
`Exited early while reconciling deployments. Skipped reconciling actions.`,
{
err: indexerError(IndexerErrorCode.IE005, err),
},
)
return
}
} else {
this.logger.debug(
`Skipping subgraph deployment reconciliation since DeploymentManagementMode = 'manual'`,
)
}
break
default:
throw new Error(
Expand Down Expand Up @@ -669,7 +711,7 @@ export class Agent {
eligibleAllocations: Allocation[],
): Promise<void> {
const logger = this.logger.child({ function: 'reconcileDeployments' })
logger.debug('Reconcile deployments')
logger.debug('Reconcile deployments', { targetDeployments })
// ----------------------------------------------------------------------------------------
// Ensure the network subgraph deployment is _always_ indexed
// ----------------------------------------------------------------------------------------
Expand Down Expand Up @@ -810,6 +852,7 @@ export class Agent {
maxAllocationEpochs: number,
network: Network,
operator: Operator,
forceAction: boolean = false,
): Promise<void> {
const logger = this.logger.child({
deployment: deploymentAllocationDecision.deployment.ipfsHash,
Expand All @@ -831,6 +874,7 @@ export class Agent {
logger,
deploymentAllocationDecision,
activeDeploymentAllocations,
forceAction,
)
case true: {
// If no active allocations and subgraph health passes safety check, create one
Expand Down Expand Up @@ -867,6 +911,7 @@ export class Agent {
logger,
deploymentAllocationDecision,
mostRecentlyClosedAllocation,
forceAction,
)
}
} else if (activeDeploymentAllocations.length > 0) {
Expand All @@ -875,6 +920,7 @@ export class Agent {
logger,
deploymentAllocationDecision,
activeDeploymentAllocations,
forceAction,
)
} else {
// Refresh any expiring allocations
Expand All @@ -891,6 +937,7 @@ export class Agent {
logger,
deploymentAllocationDecision,
expiringAllocations,
forceAction,
)
}
}
Expand All @@ -910,21 +957,37 @@ export class Agent {
// --------------------------------------------------------------------------------
const { network, operator } = this.networkAndOperator
let validatedAllocationDecisions = [...allocationDecisions]
let dipsDeployments: SubgraphDeploymentID[] = []
if (network.specification.indexerOptions.enableDips) {
if (!operator.dipsManager) {
throw new Error('DipsManager is not available')
}
dipsDeployments = await operator.dipsManager.getActiveDipsDeployments()
}

if (
network.specification.indexerOptions.allocationManagementMode ===
AllocationManagementMode.MANUAL
) {
this.logger.trace(
`Skipping allocation reconciliation since AllocationManagementMode = 'manual'`,
{
protocolNetwork: network.specification.networkIdentifier,
targetDeployments: allocationDecisions
.filter(decision => decision.toAllocate)
.map(decision => decision.deployment.ipfsHash),
},
)
validatedAllocationDecisions = [] as AllocationDecision[]
if (network.specification.indexerOptions.enableDips) {
this.logger.warn(
`Allocation management is manual, but DIPs is enabled. Reconciling DIPs allocations anyways.`,
)
validatedAllocationDecisions = validatedAllocationDecisions.filter(
decision => dipsDeployments.includes(decision.deployment),
)
} else {
this.logger.trace(
`Skipping allocation reconciliation since AllocationManagementMode = 'manual'`,
{
protocolNetwork: network.specification.networkIdentifier,
targetDeployments: allocationDecisions
.filter(decision => decision.toAllocate)
.map(decision => decision.deployment.ipfsHash),
},
)
validatedAllocationDecisions = [] as AllocationDecision[]
}
} else {
const networkSubgraphDeployment = network.networkSubgraph.deployment
if (
Expand All @@ -948,7 +1011,6 @@ export class Agent {
// Do nothing if there are already approved actions in the queue awaiting execution
const approvedActions = await operator.fetchActions({
status: ActionStatus.APPROVED,
protocolNetwork: network.specification.networkIdentifier,
})
if (approvedActions.length > 0) {
this.logger.info(
Expand All @@ -964,7 +1026,7 @@ export class Agent {
const activeAllocations: Allocation[] =
await network.networkMonitor.allocations(AllocationStatus.ACTIVE)

this.logger.trace(`Reconcile allocation actions`, {
this.logger.debug(`Reconcile allocation actions`, {
protocolNetwork: network.specification.networkIdentifier,
epoch,
maxAllocationEpochs,
Expand All @@ -986,6 +1048,7 @@ export class Agent {
maxAllocationEpochs,
network,
operator,
dipsDeployments.includes(decision.deployment), // Force actions if this is a DIPs deployment
),
)
return
Expand Down
28 changes: 28 additions & 0 deletions packages/indexer-agent/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,26 @@ export const start = {
default: 1,
group: 'Indexer Infrastructure',
})
.option('enable-dips', {
description: 'Whether to enable Indexing Fees (DIPs)',
type: 'boolean',
default: false,
group: 'Indexing Fees ("DIPs")',
})
.option('dipper-endpoint', {
description: 'Gateway endpoint for DIPs receipts',
type: 'string',
array: false,
required: false,
group: 'Indexing Fees ("DIPs")',
})
.option('dips-allocation-amount', {
description: 'Amount of GRT to allocate for DIPs',
type: 'number',
default: 1,
required: false,
group: 'Indexing Fees ("DIPs")',
})
.check(argv => {
if (
!argv['network-subgraph-endpoint'] &&
Expand Down Expand Up @@ -335,6 +355,9 @@ export const start = {
) {
return 'Invalid --rebate-claim-max-batch-size provided. Must be > 0 and an integer.'
}
if (argv['enable-dips'] && !argv['dipper-endpoint']) {
return 'Invalid --dipper-endpoint provided. Must be provided when --enable-dips is true.'
}
return true
})
},
Expand Down Expand Up @@ -370,6 +393,10 @@ export async function createNetworkSpecification(
allocateOnNetworkSubgraph: argv.allocateOnNetworkSubgraph,
register: argv.register,
finalityTime: argv.chainFinalizeTime,
enableDips: argv.enableDips,
dipperEndpoint: argv.dipperEndpoint,
dipsAllocationAmount: argv.dipsAllocationAmount,
dipsEpochsMargin: argv.dipsEpochsMargin,
}

const transactionMonitoring = {
Expand Down Expand Up @@ -587,6 +614,7 @@ export async function run(
const network = await Network.create(
logger,
networkSpecification,
managementModels,
queryFeeModels,
graphNode,
metrics,
Expand Down
4 changes: 2 additions & 2 deletions packages/indexer-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@graphprotocol/indexer-cli",
"version": "0.22.0",
"version": "0.23.3",
"description": "Indexer CLI for The Graph Network",
"main": "./dist/cli.js",
"files": [
Expand All @@ -27,7 +27,7 @@
},
"dependencies": {
"@graphprotocol/common-ts": "2.0.11",
"@graphprotocol/indexer-common": "^0.22.0",
"@graphprotocol/indexer-common": "^0.23.3",
"@iarna/toml": "2.2.5",
"@thi.ng/iterators": "5.1.74",
"@urql/core": "3.1.0",
Expand Down
1 change: 1 addition & 0 deletions packages/indexer-cli/src/__tests__/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export const setup = async () => {
const network = await Network.create(
logger,
testNetworkSpecification,
models,
queryFeeModels,
graphNode,
metrics,
Expand Down
2 changes: 0 additions & 2 deletions packages/indexer-cli/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
nullPassThrough,
OrderDirection,
parseBoolean,
validateNetworkIdentifier,
} from '@graphprotocol/indexer-common'
import { validatePOI, validateRequiredParams } from './command-helpers'
import gql from 'graphql-tag'
Expand Down Expand Up @@ -207,7 +206,6 @@ const ACTION_PARAMS_PARSERS: Record<keyof ActionUpdateInput, (x: never) => any>
type: x => validateActionType(x),
status: x => validateActionStatus(x),
reason: nullPassThrough,
protocolNetwork: x => validateNetworkIdentifier(x),
}

/**
Expand Down
Loading
Loading