Set up a Graph Indexer

Go through the following documentation for Docker set up:

Indexing - The Graph Docs

Here is the first set of instructions regarding the node:

  1. Clone Graph Node and navigate to the Docker directory:
git clone http://github.com/graphprotocol/graph-node
cd graph-node/docker
  1. For linux users only - Use the host IP address instead of host.docker.internal in the docker-compose.yaml using the included script:

./setup.sh

  1. Start a local Graph Node that will connect to your Ethereum endpoint:

docker-compose up

After this, I check

docker ps

and I get the following:

CONTAINER ID   IMAGE                      COMMAND                  CREATED       STATUS          PORTS                                                                                                      NAMES

b8a4319bf3ee   graphprotocol/graph-node   "start"                  4 weeks ago   Up 43 minutes   0.0.0.0:8000-8001->8000-8001/tcp, 0.0.0.0:8020->8020/tcp, 0.0.0.0:8030->8030/tcp, 0.0.0.0:8040->8040/tcp   docker-graph-node-1

e0cad25b6f41   postgres                   "docker-entrypoint.s…"   4 weeks ago   Up 43 minutes   0.0.0.0:5432->5432/tcp                                                                                     docker-postgres-1

4a4dad543078   ipfs/go-ipfs:v0.10.0       "/sbin/tini -- /usr/…"   4 weeks ago   Up 43 minutes   4001/tcp, 8080-8081/tcp, 4001/udp, 0.0.0.0:5001->5001/tcp                                                  docker-ipfs-1

So I have three containers running:

According to the docs, within this I have an Ethereum node.

I check the node at port 8000:

Access deployed subgraphs by deployment ID at /subgraphs/id/<ID> or by name at /subgraphs/name/<NAME>

I then pulled docker images for the indexer agent and indexer service

docker pull ghcr.io/graphprotocol/indexer-service:latest

docker pull ghcr.io/graphprotocol/indexer-agent:latest

I now checked the images pulled:
docker images

This is what I now see:

REPOSITORY                              TAG       IMAGE ID       CREATED         SIZE

ghcr.io/graphprotocol/indexer-service   latest    b09771b4daf3   5 weeks ago     2.16GB

ghcr.io/graphprotocol/indexer-agent     latest    5bc7f15f61d1   5 weeks ago     2.04GB

postgres                                latest    ceccf204404e   6 weeks ago     379MB

graphprotocol/graph-node                latest    37e8e652fc2f   3 months ago    227MB

ipfs/go-ipfs                            v0.10.0   3e1ffbe25944   20 months ago   77.2MB

The docs then say the following:

docker run -p 7600:7600 -it indexer-service:latest ...

docker run -p 18000:8000 -it indexer-agent:latest ...

NOTE: After starting the containers, the Indexer service should be accessible at http://localhost:7600 and the Indexer agent should be exposing the Indexer management API at http://localhost:18000/.

This seemed confusing because ... implies arguments are passed, but it immediately describes a running service.

The docs also say the following:

Ethereum node - By default, the docker compose setup will use mainnet: http://host.docker.internal:8545 to connect to the Ethereum node on your host machine. You can replace this network name and url by updating docker-compose.yaml.

This wasn't documented, but my understanding is this would mean installing a local node.

Since I wanted to also start in testnet, not mainnet despite what the documentation said, I would need to install Geth or Parity to run locally.

At this point, I wanted to just get something going, so I created a QuickNode Goerli endpoint.

I also found a separate page called The Graph Academy that had slightly different information:

Testnet - The Graph Academy

https://docs.thegraph.academy/the-graph-ecosystem/infrastructure/testnet

specifically, it included a table of environment variables for the indexer which are different from what I see in the other document.

That document also highlights some information not found on the other document.

First, it talks about Testnet.

To me, as a first time indexer, I would much prefer to get up and running on Testnet with a faucet of GRT than having to stake on my own right away, which is what the main documentation says to do.

So, here are the instructions:

The Graph Academy > Testnet

  1. join The Graph Discord,
  2. get the @testnetindexer role in the #roles channel,
  3. use the #testnet-goerli-faucet channel to obtain testnet GRT.

So to do this, I had to enable the networks in my Metamask, and the easiest way to do this is to go to the chainlist.org directory and search for goerli

However, I added both Ethereum and Arbitum because Arbitum network should be now live. I don't see documentation to that effect, but that's a good thing to check.

Anyway, once I got into the Discord channel I entered:

!grt <public wallet address>

So, once the wallet receives the tokens, you can stake by following the instructions here:

Meanwhile....if you're like me and haven't used the Goerli testnet, you'll want to go to a faucet to pay for gas.

https://faucet.quicknode.com/ethereum/goerli

Note, it's better to start the faucet drip earlier, because there can be delays.

Via Graph Explorer

The Graph Explorer provides an easy way to approve and stake your GRT as an indexer via a web GUI.

The Graph Academy > Testnet

    1. Navigate to the testnet explorer
    1. Login with Metamask and select the Goerli network
    1. Navigate to your profile (click your address/avatar at top right)
    1. Select the Indexing tab and hit the Stake button
    1. Follow the directions on the staking screen to stake the desired amount

I am still waiting for my wallet to receive the tokens so....meanwhile....

While you are in the Metamask Wallet, I recommend (again, not clearly documented where I would have expected it) a separate Account from your staking account which is an Operator Wallet from within Metamask.

You'll see why as we update the docker-compose file.

Now, there are a few things that need clarification (or it did to me, I'm not sure how everyone else figured it out based on these docs):

Also, in the docs and initial docker-compose.yaml file, the postgres DB names are different. I made them the same since I didn't see another postgres application as a docker image, just the one from the graph-node repo.

So, I ran docker-compose up and got some surprising errors:

For docker-indexer-service-1:

Missing required arguments: ethereum, mnemonic, indexer-address, graph-node-query-endpoint, graph-node-status-endpoint, postgres-host, postgres-database, network-subgraph-endpoint

So...while the Academy pages lists explicit environiment variables instead of arguments, it's incomplete on this page: https://docs.thegraph.academy/the-graph-ecosystem/infrastructure/testnet

Getting past the 'missing arguments' error for indexer-agent and indexer-service

How I am running these

Although not documented, I use the format that is consistent with the few examples that were documented:

INDEXER_AGENT_{argument in CAPS}

indexer-agent

docker run -p 7600:7600 -it --env-file ./indexer-agent-args.txt --name indexer-agent ghcr.io/graphprotocol/indexer-agent:latest

So I decided to just breakdown what is expected and what are the definitions.

argument | help | docs | academy | random
--- | ---| --- | --- | --- | ---
ethereum | Ethereum node or provider URL string required | | I |
mnemonic | Mnemonic for the operator wallet string required | | |
indexer-address | Ethereum address of the indexer | | |
graph-node-query-endpoint | Graph Node endpoint for querying subgraphs |http://localhost:8000/ | x |
graph-node-status-endpoint | Graph Node endpoint for indexing statuses | http://localhost:8030/graphql | x |
graph-node-admin-endpoint | Graph Node endpoint for applying and updating subgraph deployments | http://localhost:8020/ | x |
public-indexer-url | Indexer endpoint for receiving requests from the network | http://localhost:7600 | x |
index-node-ids | Node IDs of Graph nodes to use for indexing | default| x |
epoch-subgraph-endpoint | Endpoint to query the epoch block oracle subgraph | | x | https://api.thegraph.com/subgraphs/name/graphprotocol/mainnet-epoch-block-oracle ; https://api.thegraph.com/subgraphs/name/graphprotocol/goerli-epoch-block-oracle
postgres-host | Postgres host | localhost | x |
postgres-database | | indexer | x |

Indexing - The Graph Docs 2023-05-28 at 6.27.42 PM.jpg

Indexer-service

Similar exercise. Source from help can be found in Github:

docker run -p 7600:7600 -it --env-file ./indexer-service-args.txt --name indexer-service ghcr.io/graphprotocol/indexer-service:latest --mnemonic <add here> --indexer-address <add here>

argument help docs academy random
ethereum Ethereum node or provider URL x x
mnemonic Mnemonic for the operator wallet x x generate
indexer-address separate from operator wallet x x generate
graph-node-query-endpoint Graph Node endpoint to forward queries to http://localhost:8000/ x
graph-node-status-endpoint Graph Node endpoint for indexing statuses etc. http://localhost:8030/graphql x
postgres-host
network-subgraph-endpoint x https://gateway.thegraph.com/network
postgres-database is_staging x

Even though it isn't documented as such, I was able to create environment variables using the following format:

INDEXER_SERVICE_{argument in CAPS}

This is via the --env-file where the format is something like this:

INDEXER_SERVICE_GRAPH_NODE_QUERY_ENDPOINT= 'http://localhost:8000/'
INDEXER_SERVICE_GRAPH_NODE_STATUS_ENDPOINT= http://localhost:8030/graphql
INDEXER_SERVICE_POSTGRES_HOST= localhost

So using the same mnemonic as I used in agent, I am getting invalid mnemonic!!!

Create a Mnemonic

https://docs.thegraph.academy/official-docs/indexer/testnet/guide

You need a wallet with a seed phrase that is registered as your operator wallet. This wallet will be the one that makes transactions on behalf of your main wallet (which holds and stakes the GRT). The operator wallet has limited functionality, and it's recommended to be used for security reasons.

You need a 12-word, or 15-word mnemonic phrase in order for it to work.

To make yourself a mnemonic eth wallet you can go to this website and just press generate. You get a seed phrase in the input field labeled BIP39 Mnemonic. Scroll down a bit and find the select field labeled Coin. Select ETH as network in the dropdown and you find your address, public key and private key in the first row of the table if you scroll down the page in the section with the heading "Derived Addresses". You can import the wallet using the private key into Metamask.

Although this look good too: https://github.com/glonlas/offline-mnemonic-generator

Better Wallet description

https://github.com/StakeSquid/graphprotocol-testnet-docker/blob/master/docs/pre-requisites.md

Set your Operator

The Operator is a wallet address that is entirely separate from the address which you staked your GRT from. This Operator wallet will be filled with ETH, and will be used to send transactions (such as allocations) to the network, while keeping your Staked GRT safe in case of an attack on your infrastructure. It is highly recommended for you to use a new wallet, generated from a new mnemonic phrase.

For this, follow the instructions here first, then head back for the rest.

Okay, assuming that you followed the instructions and you have your new Operator wallet at hand, let's go and link it up with the wallet that you used to stake your GRT.

  1. Login with Metamask on the wallet that you used to stake your GRT
  2. Click the Profile dropdown button, and go to "Settings", and then to the "Operators" tab 
  3. Click the Plus (+) button and add your operator public address there
  4. Submit the transaction, then you're done

So I used the address generated by the mnemonic....turns out that is wrong after reading further. So.....

Got this msg:

"Indexer and operator are identical, operator status granted"}

https://docs.thegraph.academy/official-docs/indexer/testnet/graph-protocol-testnet-baremetal/4_deployandconfiguregraphindexerstack

This says:

Testnet prerequisites

You will need two Ethereum addresses in order to run your Indexer. One Indexer address and one Operator address.

Indexer = your main address with all privileges. Operator = the address used by the indexer, only has specific privileges to run the indexer software.

If you are deploying to testnet you will need testnet GRT in order to register your Indexing operation on-chain. Check the discord for info on how to register and receive testnet GRT.

I'm not sure what "main address with all privileges" actually means. Or why the operator address generates an error.

So I'll try another public address generated straight from Metamask and see if it still works.

Now I get the following:

TypeError [ERR_INVALID_URL]: Invalid URL

    at new NodeError (node:internal/errors:372:5)

    at URL.onParseError (node:internal/url:553:9)

    at new URL (node:internal/url:629:5)

    at new AllocationReceiptCollector (/usr/local/lib/node_modules/@graphprotocol/indexer-agent/node_modules/@graphprotocol/indexer-common/dist/allocations/query-fees.js:32:32)

    at /usr/local/lib/node_modules/@graphprotocol/indexer-agent/dist/commands/start.js:523:34

    at Generator.next (<anonymous>)

    at fulfilled (/usr/local/lib/node_modules/@graphprotocol/indexer-agent/dist/commands/start.js:5:58)

    at processTicksAndRejections (node:internal/process/task_queues:96:5) {

  input: 'undefined',

  code: 'ERR_INVALID_URL'

Mnemonic troubleshooting

I didn't see anything useful in the Discord's. Other people do seem to have the same error, but no clear resolution.

Current guess is that I need to register the Operator address as part of my profile. Perhaps the mnemonic is saying it is invalid because the Operator address that is generated doesn't match my profile.

Have no idea, though. But I have been blocked adding it because I need Goerli gas and have tried three times from the QuickNode faucet....still nothing:

https://faucet.quicknode.com/ethereum/goerli/?transactionHash=0xe30fcd6b5eba31085045f8c30beed3461bfc9487592abc53d2ea1457b0bf4b82

Alright, so I finally found a faucet which worked. Hassle. Had to go here:

https://faucetlink.to/goerli

And had to try a bunch till I could find one that worked.

Alchemy's did, so that says something about the service.

Now that I have it, I was able to:

  1. Add the operator address, the derived address from the generated mnemonic (which I did online for now, not the best, but for now....)
  2. Staked on the Testnet with $GRT

So cleared all the indexer-agent and indexer-service in docker (once I get something running which make sure there's a name for the service as with docker compose for the graph-node), and trying again.

Didn't make a difference adding it in.

According to the API, it's a failed checksum.....???

https://docs.ethers.org/v3/api-advanced.html

ethers . HDNode . mnemonicToEntropy ( mnemonic )
Convert a mnemonic to its binary entropy. (throws an error if the checksum is invalid)

Is the Environment variable wrong? I checked that the mnemonic is correct by comparing the private key generated to what is created after importing into metamask. But it's not the same as the checksum, itself, but I'm right now surprised if the website is generating the wrong mnemonic.

The error message:

Error: invalid mnemonic
    at mnemonicToEntropy (/opt/indexer/node_modules/@ethersproject/hdnode/lib/index.js:268:15)
    at Function.HDNode.fromMnemonic (/opt/indexer/node_modules/@ethersproject/hdnode/lib/index.js:217:38)
    at Function.Wallet.fromMnemonic (/opt/indexer/node_modules/@ethersproject/wallet/lib/index.js:235:43)
    at /opt/indexer/packages/indexer-agent/dist/commands/start.js:439:38
    at Generator.next (<anonymous>)
    at fulfilled (/opt/indexer/packages/indexer-agent/dist/commands/start.js:5:58)
docker run -it --entrypoint /bin/bash ghcr.io/graphprotocol/indexer-agent
cd /opt/indexer/node_modules/@ethersproject/hdnode/lib/
nl index.js
cd /opt/indexer/packages/indexer-agent/dist/commands/
nl start.js

start.js

https://gist.github.com/timfong888/62d037da405512c62a7b226f0272f664

try to pass argument as flag

So this time I passed the --mnemonic directly as a flag and it worked.

This is strange. It means the environment variable wasn't passing it correctly.

Okay, so now I have passed as arguments --mnemonic and --indexer-address and I no longer have the error.

So, once this is working, I now have this:

ConnectionRefusedError [SequelizeConnectionRefusedError]: connect ECONNREFUSED 127.0.0.1:5432
    at Client._connectionCallback (/opt/indexer/node_modules/sequelize/lib/dialects/postgres/connection-manager.js:130:24)
    at Client._handleErrorWhileConnecting (/opt/indexer/node_modules/pg/lib/client.js:305:19)
    at Client._handleErrorEvent (/opt/indexer/node_modules/pg/lib/client.js:315:19)
    at Connection.emit (node:events:527:28)
    at Connection.emit (node:domain:475:12)
    at Socket.reportStreamError (/opt/indexer/node_modules/pg/lib/connection.js:52:12)
    at Socket.emit (node:events:527:28)
    at Socket.emit (node:domain:475:12)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3) {
  parent: Error: connect ECONNREFUSED 127.0.0.1:5432
      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) {
    errno: -111,
    code: 'ECONNREFUSED',
    syscall: 'connect',
    address: '127.0.0.1',
    port: 5432
  },
  original: Error: connect ECONNREFUSED 127.0.0.1:5432
      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) {
    errno: -111,
    code: 'ECONNREFUSED',
    syscall: 'connect',
    address: '127.0.0.1',
    port: 5432
  }
}

This is out of my depth as to why the local host database isn't working.

The relevant parts of the env file are for the indexer agent, and then I ran the indexer service as well.

Indexer service:

INDEXER_SERVICE_POSTGRES_HOST=127.0.0.1
INDEXER_SERVICE_POSTGRES_PORT= 5432
INDEXER_SERVICE_POSTGRES_USERNAME= indexer-agent
INDEXER_SERVICE_POSTGRES_PASSWORD= let-me-in
INDEXER_SERVICE_POSTGRES_DATABASE= indexer-db
      INDEXER_AGENT_POSTGRES_HOST=127.0.0.1
      INDEXER_AGENT_POSTRES_PORT=5432
      INDEXER_AGENT_POSTGRES_USERNAME= indexer-agent
      INDEXER_AGENT_POSTGRES_PASSWORD= let-me-in
      INDEXER_AGENT_POSTGRES_DATABASE=indexer-db

Unlock the issue with mnemonic and indexer-address which did NOT work when passed through --env-file it does appear that the port and database are correct. But whether the usernames that I defined here are right is still unclear to me. I just used what is in the docs.

Blocked - try another image

I am blocked.

I am not sure why the mnemonic is invalid since I am following BIP39 from https://iancoleman.io/bip39/

But need to move on for now.

https://thegraph.academy/indexers/setting-up-a-testnet-indexer/

Hm, not making progress this way -- seems like I need to set up archive node spinning up infrastructure. Will look into this later. Back to Mnmonic.

List of Docs

2023-05-30
Today I at least got a little bit of progress, but it is frustration, by not using the environment variables passed through a file but just added the flags directly.

I went through an just used the entrypoint into the image to see what was going on, but then gave up and tried the argument since those were clearly defined. That worked.

Now I have a connection error, and I am unclear on the database.

Another indexer said to start the indexer agent first and that instantiates the database....and gave me his environment variables which showed two different db names.

So I used that, but have a connection error.

At least I am not stuck on the mnemonic, but that is very annoying..