Skip to content

Rewrite of indexer-service in Rust with TAP payments implementation

License

Notifications You must be signed in to change notification settings

graphprotocol/indexer-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

indexer-service-rs

Introduction

A Rust impl for The Graph indexer service to provide data services as an Indexer, integrated with TAP which is a fast, efficient, and trustless unidirectional micro-payments system.

Features

  • Receive paid or free query requests and route to graph node
  • Route "meta" queries on indexing statuses and deployment health
  • Serve indexer information such as health, indexer version, and operator address
  • Monitor allocations, attestation signers, and manage receipts using TAP, store receipts in the indexer database
  • Record performance and service metrics

Quick start

$ cargo run -p indexer-service-rs -- --help

Usage: indexer-service-rs --config <FILE>

Options:
      --config <FILE>  Path to the configuration file.
                       See https://github.com/graphprotocol/indexer-rs/tree/main/config for examples.
  -h, --help           Print help

All the configuration is done through a TOML file. Please see up-to-date TOML configuration templates:

Upgrading

We follow conventional semantics for package versioning. An indexer may set a minor version specification for automatic patch updates while preventing breaking changes. To safely upgrading the package, we recommend the following steps:

  1. Review Release Notes: Before upgrading, check the release notes for the new version to understand what changes, fixes, or new features are included.
  2. Review Documentation: Check the up-to-date documentation for an accurate reflection of the changes made during the upgrade.
  3. Backup Configuration: Save your current configuration files and any local modifications you've made to the existing codebase.
  4. Deploy: Replace the old executable or docker image with the new one and restart the service to apply the upgrade.
  5. Monitor and Validate: After the upgrade, monitor system behavior and performance metrics to validate that the service is running as expected.

These steps should ensure a smooth transition to the latest version of indexer-service-rs, harnessing new capabilities while maintaining system integrity.

Contributing

Contributions guide

Supported request and response format examples

curl http://localhost:7600/
Service is up and running
curl http://localhost:7600/version
{ "version":"0.1.0", "dependencies": {..} }
curl http://localhost:7600/info
{ "publicKey": "0xacb05407d78129b5717bb51712d3e23a78a10929" }

Subgraph queries

Checks for receipts and authorization

curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer token-for-graph-node-query-endpoint' \
  --data '{"query": "{_meta{block{number}}}"}' \
  http://localhost:7600/subgraphs/id/QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB
{
    "attestable": true,
    "graphQLResponse": "{\"data\":{\"_meta\":{\"block\":{\"number\":10666745}}}}"
}

Takes hex representation for subgraphs deployment id aside from IPFS hash representation

curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer token-for-graph-node-query-endpoint' \
  --data '{"query": "{_meta{block{number}}}"}' \
  http://localhost:7600/subgraphs/id/0xb655ca6f49e73728a102219726ff678d61d8fb792874792e9f0d9887dc616600
{
    "attestable": true,
    "graphQLResponse": "{\"data\":{\"_meta\":{\"block\":{\"number\":10666745}}}}"
}

Free query auth token check failed

curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'Authorization: blah' \
  --data '{"query": "{_meta{block{number}}}"}' \
  http://localhost:7600/subgraphs/id/0xb655ca6f49e73728a102219726ff678d61d8fb792874792e9f0d9887dc616600
{
  "message":"No valid receipt or free query auth token provided"
}

Subgraph health check

curl http://localhost:7600/subgraphs/health/QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj
{
    "health": "healthy"
}

Unfound subgraph

curl http://localhost:7600/subgraphs/health/QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB
{
    "error": "Deployment not found"
}

Failed Subgraph

curl http://localhost:7600/subgraphs/health/QmVGSJyvjEjkk5U9EdxyyB78NCXK3EAoFhrzm6LV7SxxAm
{
    "fatalError": "transaction 21e77ed08fbc9df7be81101e9b03c2616494cee7cac2f6ad4f1ee387cf799e0c: error while executing at wasm backtrace:\t    0: 0x5972 - <unknown>!mappings/core/handleSwap: Mapping aborted at mappings/core.ts, line 73, column 16, with message: unexpected null in handler `handleSwap` at block #36654250 (5ab4d80c8e2cd628d5bf03abab4c302fd21d25d734e66afddff7a706b804fe13)",
    "health": "failed"
}

Network queries

Checks for auth and configuration to serve-network-subgraph

curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'Authorization: token-for-network-subgraph' \
  --data '{"query": "{_meta{block{number}}}"}' \
  http://localhost:7600/network
{ 
  "message":"No valid receipt or free query auth token provided" 
}

Indexing status resolver - Route supported root field queries to graph node status endpoint

curl -X POST \
  -H 'Content-Type: application/json' \
  --data '{"query": "{blockHashFromNumber(network:\"mainnet\", blockNumber: 21033)}"}' \
  http://localhost:7600/status
{
  "data": {
    "blockHashFromNumber": "0x6d8daae97a562b1fff22162515452acdd817c3d3c5cde1497b7d9eb6666a957e"
  }
}

Indexing status resolver

curl -X POST \
  -H 'Content-Type: application/json' \
  --data '{"query": "{indexingStatuses {subgraph health}}"}' \
  http://localhost:7600/status
{
  "data": {
    "indexingStatuses": [
      {
        "subgraph": "QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj",
        "health": "healthy"
      },
      {
        "subgraph": "QmWVtsWk8Pqn3zY3czDjyoVreshRLmoz9jko3mQ4uvxQDj",
        "health": "healthy"
      },
      {
        "subgraph": "QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB",
        "health": "healthy"
      }
    ]
  }
}

Indexing status resolver - Filter out the unsupported queries

curl -X POST \
  -H 'Content-Type: application/json' \
  --data '{"query": "{_meta{block{number}}}"}' \
  http://localhost:7600/status
{
  "errors": [
    {
      "locations": [
        {
          "line": 1,
          "column": 2
        }
      ],
      "message": "Type `Query` has no field `_meta`"
    }
  ]
}

Cost server - read-only graphql query

curl -X GET \
  -H 'Content-Type: application/json' \
  --data '{"query": "{ costModels(deployments: [\"Qmb5Ysp5oCUXhLA8NmxmYKDAX2nCMnh7Vvb5uffb9n5vss\"]) { deployment model variables }} "}' \
  http://localhost:7300/cost
{
  "data": {
    "costModels": [
      {
        "deployment": "0xbd499f7673ca32ef4a642207a8bebdd0fb03888cf2678b298438e3a1ae5206ea",
        "model": "default => 0.00025;",
        "variables": null
      }
    ]
  }
}

Dependency choices

  • switching from actix-web to axum for the service server
  • App profiling should utilize perf, flamegraphs or cpu profilers, and benches to track and collect performance data. The typescript implementation uses gcloud-profile
  • Consider replacing and adding parts from TAP manager
  • postgres database connection required to indexer management server database, shared with the indexer agent
  • No migration in indexer service as it might introduce conflicts to the database; indexer agent is solely responsible for database management.

Indexer common components

Temporarily live inside the indexer-service package under src/common.

Simple indexer management client to track NetworkSubgraph and postgres connection.

  • NetworkSubgraph instance track both remote API endpoint and local deployment query endpoint.
    • TODO: query indexing status of local deployment, only use remote API as fallback.
  • Keeps cost model schema and resolvers with postgres and graphQL types: costModel(deployment) and costModels(deployments). If deployments is empty, all cost models are returned.
    • Global cost model fallback used when specific deployments are queried
  • No database migration in indexer service as it might introduce schema conflicts; indexer agent is solely responsible for database management.

Indexer native dependency

Linked dependency could not be linked directly with git url "https://github.com/graphprotocol/indexer" and path "packages/indexer-native/native" at the same time, and could not access it on crates.io. So copid the folder to local repo with the version at https://github.com/graphprotocol/indexer/blob/972658b3ce8c512ad7b4dc575d29cd9d5377e3fe/packages/indexer-native/native.

Since indexer-service will be written in Rust and no need for typescript, indexer-native's neon build and util has been removed.

Component NativeSignatureVerifier renamed to SignatureVerifier.

Separate package in the workspace under 'native'.

common-ts components

Temporarily live inside the indexer-service package under src/types

  • Address
  • readNumber

Components checklist (basic, not extensive)

  • Server path routing
    • basic structure
    • CORS
    • timeouts
    • Rate limiting levels
    • Logger stream
  • Query processor
    • graph node query endpoint at specific subgraph path
    • wrap request to and response from graph node
    • extract receipt header
    • Free query
      • Query struct
      • Free query auth token check
      • Query routes + responses
      • set graph-attestable in response header to true
    • Network subgraph query
      • Query struct
      • serve network subgraph boolean + auth token check
      • Query routes + responses
      • set graph-attestable in response header to false
    • Paid query
      • receipts graphQL schema
      • TAP manager to handle receipts logic
        • derive, cache, and look up attestation signers
          • contracts - connect by network chain id
            • network provider
        • validate receipt format (need unit tests)
        • parse receipt (need unit tests)
        • validate signature (need unit tests)
        • store
      • extract graph-attestable from graph node response header
      • monitor eligible allocations
        • network subgraph
        • operator wallet -> indexer address
    • subgraph health check
    • query timing logs
  • Deployment health server
    • query status endpoint and process result
  • Status server
    • indexing status resolver - to query indexingStatuses
    • Filter for unsupported queries
  • Cost server
    • Simple indexer management client to track postgres connection and network subgraph endpoint.
    • serve queries with defined graphQL schema and psql resolvers to database: costModel(deployment) and costModels(deployments). If deployments is empty, all cost models are returned.
    • Global cost model fallback used when specific deployments are queried
  • Constant service paths
    • health
    • ready to roll
    • versions
    • operator public key
      • validate mnemonics to public key
  • Import indexer native
  • Metrics
    • Metrics setup
    • serve basic indexer service metrics
    • Add cost model metrics
  • CLI args
  • App profiling
    • No gcloud profiling, can use perf to collect performance data.