From 99cd201880bccc9c548d70d15d5035f59eb11e30 Mon Sep 17 00:00:00 2001 From: axiomatic-aardvark Date: Tue, 18 Jul 2023 19:20:49 +0300 Subject: [PATCH] docs: update and rebrand --- docs/graphcast/design-principles.md | 26 +- docs/graphcast/radios/3la-monitor.md | 49 ++-- docs/graphcast/radios/one-shot.md | 32 +++ .../{poi-radio.md => subgraph-radio.md} | 192 +++++++------- docs/graphcast/sdk/radio-dev.md | 243 +++++++++--------- docs/graphcast/sdk/registry.md | 29 +-- 6 files changed, 312 insertions(+), 259 deletions(-) create mode 100644 docs/graphcast/radios/one-shot.md rename docs/graphcast/radios/{poi-radio.md => subgraph-radio.md} (65%) diff --git a/docs/graphcast/design-principles.md b/docs/graphcast/design-principles.md index 888b9f8a..c68accd9 100644 --- a/docs/graphcast/design-principles.md +++ b/docs/graphcast/design-principles.md @@ -19,22 +19,34 @@ The SDK is the base layer which is used to abstract all the necessary components - Checks message validity for past message injections, nonexistent blocks and expired timestamps. It also guarantees that messages are signed by an authorised operator address of an active on-chain Indexer (this can be used as a basis for a reputation system). - Supports a flexible and customizable configuration of the Graphcast gossip agent, enabling specification of network settings, peer discovery mechanisms, message encoding formats, and more. For detailed instructions on configuring Graphcast to suit your needs, refer to the configuration guide. - Topics in Graphcast represent different categories or subjects of information. Nodes can dynamically subscribe to specific topics to receive messages related to those topics. Topics enable efficient message routing and dissemination within the network. -- Provides comprehensive message handling structure to ensure that messages are reliably transmitted, received, and processed within the network. +- Provides comprehensive message handling structure to ensure that messages are reliably transmitted, received, and processed within the network. ## Radios General Radio components - Supports Radio for specific use cases. -- Controls topic subscriptions dynamically for interested topics. -- Provides radio type definition used to verify the integrity and authenticity of messages exchanged within the network. +- Controls topic subscriptions dynamically for interested topics. +- Provides Radio type definition used to verify the integrity and authenticity of messages exchanged within the network. - Collects Radio-specific information and incorporates it into Graphcast messages along with other relevant metadata. - Observes and handles relevant messages received from peers. - Provides performance metrics, logs, and API services. -Our first example Radio is built for real-time cross-checking of Indexer Proof of Indexing attestations (POIs). Indexers must generate valid POIs to earn indexing rewards. Indexers find it beneficial to alert each other on the health status of subgraphs in community discussions. To alleviate the manual workload, the POI Radio: +The first Radio built on top of Graphcast is the Subgraph Radio. It's designed to facilitate real-time information exchange among participants in The Graph network and serves as a tool for Indexers and other network participants to share valuable Subgraph data. + +With Subgraph Radio, Indexers can run a single Radio instance and track a wide variety of message types and data related to Subgraphs. Different use cases and message types form the different _functionalities_ of the Radio. + +### Functionalities + +#### Proof of Indexing (POI) cross-checking + +Indexers must generate valid POIs to earn indexing rewards. Indexers find it beneficial to alert each other on the health status of subgraphs in community discussions. To alleviate the manual workload, the POI functionality within Subgraph Radio: - Defines message types and topics -- Collects POIs from the Graph node and sends them inside of Graphcast messages along with other useful metadata -- Observes relevant messages and aggregates POIs sent from other Indexers, in order to compare _local_ POIs to _remote_ POIs -- Monitors the network for conflicts and takes certain actions if needed, for instance Indexers can configure an alert system to send messages to a custom channel in their Slack workspace (support for Discord and email notifications coming soon). +- Collects public POIs from the Graph node and sends them inside of Graphcast messages along with other useful metadata +- Observes relevant messages and aggregates public POIs sent from other Indexers, in order to compare _local_ POIs to _remote_ POIs +- Monitors the network for conflicts and takes certain actions if needed, for instance Indexers can configure an alert system to send messages to a custom channel in their Slack workspace, a Discord channel, or a Telegram chat. + +#### Subgraph Versioning + +The subgraph versioning functionality provides a way for Subgraph Developers to signal when they plan on releasing a new subgraph version, thereby allowing Indexers to start syncing the subgraph in advance. If the Radio operator has set up the notification system, they will get notified whenever a new subgraph versioning message is received. diff --git a/docs/graphcast/radios/3la-monitor.md b/docs/graphcast/radios/3la-monitor.md index 5f0fbabc..94de98dd 100644 --- a/docs/graphcast/radios/3la-monitor.md +++ b/docs/graphcast/radios/3la-monitor.md @@ -8,7 +8,7 @@ The source code for 3LA is available [on GitHub](https://github.com/graphops/gra ## Introduction -This Radio shall monitor Graphcast network by the pubsub topic of `graphcast-v[version]-[network]`. The Radio will not send messages to the network, but instead will record the messages and generate basic metrics for network monitoring. +This Radio shall monitor Graphcast network by the pubsub topic of `graphcast-v[version]-[network]`. The Radio will not send messages to the network, but instead will record the messages and generate basic metrics for network monitoring. Graphcast network is a complex system with numerous nodes and connections, and monitoring it is crucial for maintaining its performance, identifying potential issues, and ensuring its robustness and reliability. @@ -17,7 +17,6 @@ Graphcast network is a complex system with numerous nodes and connections, and m - Security: to immediately detect any unusual activity that might indicate a security breach. - Planning and Forecasting: Record valuable data that can be used for planning and forecasting purposes, helping us to make informed decisions about the network's future. - ## Quick Start - Ensure a running Postgres instance @@ -25,28 +24,26 @@ Graphcast network is a complex system with numerous nodes and connections, and m - Set general GraphcastAgent environmental variables shown in the below table - `cargo run` from source code (later should use Github actions to build source and dockerize - ### Basic Configuration You will need to prepare the following environment variables: -| Name | Description and examples | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `DATABASE_URL` | Postgres Database URL. The tool comes with automatic database migration, database url passed in must be exist and can be connected.
Example: `postgresql://[username]:[password]@[pg_host]:[pg_port]/[db_name]` | -| `PRIVATE_KEY` | Private key to the Graphcast ID wallet (Precendence over mnemonics).
Example: `0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef` | -| `GRAPH_NODE_STATUS_ENDPOINT` | URL to a Graph Node Indexing Status endpoint.
Example: `http://index-node:8030/graphql` | -| `REGISTRY_SUBGRAPH` | URL to the Graphcast Registry subgraph for your network. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | -| `NETWORK_SUBGRAPH` | URL to the Graph Network subgraph. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | -| `GRAPHCAST_NETWORK` | The Graphcast Messaging fleet and pubsub namespace to use.
Mainnet: `mainnet`
Goerli: `testnet` | - +| Name | Description and examples | +| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `DATABASE_URL` | Postgres Database URL. The tool comes with automatic database migration, database url passed in must be exist and can be connected.
Example: `postgresql://[username]:[password]@[pg_host]:[pg_port]/[db_name]` | +| `PRIVATE_KEY` | Private key to the Graphcast ID wallet (Precendence over mnemonics).
Example: `0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef` | +| `GRAPH_NODE_STATUS_ENDPOINT` | URL to a Graph Node Indexing Status endpoint.
Example: `http://index-node:8030/graphql` | +| `REGISTRY_SUBGRAPH` | URL to the Graphcast Registry subgraph for your network. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | +| `NETWORK_SUBGRAPH` | URL to the Graph Network subgraph. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | +| `GRAPHCAST_NETWORK` | The Graphcast Messaging fleet and pubsub namespace to use.
Mainnet: `mainnet`
Goerli: `testnet` | #### Example message table -| id | message | -|----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 1 | {"nonce": 1686182179, "network": "mainnet", "payload": {"content": "0x3f...", "identifier": "QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj"}, "signature": "dff1...", "block_hash": "276e...", "identifier": "QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj", "block_number": 17431860} | -| 2 | {"nonce": 1686182183, "network": "goerli", "payload": {"content": "0xc0...", "identifier": "QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB"}, "signature": "dbd2...", "block_hash": "0198...", "identifier": "QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB", "block_number": 9140860} | -| ...| ... +| id | message | +| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | {"nonce": 1686182179, "network": "mainnet", "payload": {"content": "0x3f...", "identifier": "QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj"}, "signature": "dff1...", "block_hash": "276e...", "identifier": "QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj", "block_number": 17431860} | +| 2 | {"nonce": 1686182183, "network": "goerli", "payload": {"content": "0xc0...", "identifier": "QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB"}, "signature": "dbd2...", "block_hash": "0198...", "identifier": "QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB", "block_number": 9140860} | +| ... | ... | ## Advanced Configuration @@ -57,7 +54,7 @@ See [Basic Configuration](#basic-configuration) above. The following environment | Name (Optional variables) | Description and examples | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MNEMONIC` | Mnemonic to the Graphcast ID wallet (first address of the wallet is used; Only one of `PRIVATE_KEY` or `MNEMONIC` is needed). Example: `claptrap armchair violin...` | -| `COLLECT_MESSAGE_DURATION` | Seconds that the POI Radio will wait to collect remote POI attestations before making a comparison with the local POI. Example: `120` for 2 minutes. | +| `COLLECT_MESSAGE_DURATION` | Seconds that the Subgraph Radio will wait to collect remote POI attestations before making a comparison with the local POI. Example: `120` for 2 minutes. | | `COVERAGE` | Toggle for topic coverage level. Possible values: "comprehensive", "on-chain", "minimal". Default is set to "on-chain" coverage. | | `TOPICS` | Comma separated static list of content topics (subgraphs) to subscribe to. Example: `QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz,QmUwCFhXM3f6qH9Ls9Y6gDNURBH7mxsn6JcectgxAz6CwU,QmQ1Lyh3U6YgVP6YX1RgRz6c8GmKkEpokLwPvEtJx6cF1y` | | `WAKU_HOST` | Interface onto which to bind the bundled Waku node. Example: `0.0.0.0` | @@ -69,7 +66,7 @@ See [Basic Configuration](#basic-configuration) above. The following environment | `TELEGRAM_CHAT_ID` | The ID of the Telegram chat to send messages to. Example: `-1001234567890` | | `SLACK_CHANNEL` | Name of Slack channel to send messages to (has to be a public channel). Example: `poir-notifications` | | `WAKU_LOG_LEVEL` | Waku node logging configuration. Example: `INFO` (is also the default) | -| `RUST_LOG` | Rust tracing configuration. Example: `graphcast_sdk=debug,poi_radio=debug`, defaults to `info` for everything | +| `RUST_LOG` | Rust tracing configuration. Example: `graphcast_sdk=debug,subgraph_radio=debug`, defaults to `info` for everything | | `DISCORD_WEBHOOK` | Discord webhook URL for notifications. Example: `https://discord.com/api/webhooks/123456789012345678/AbCDeFgHiJkLmNoPqRsTuVwXyZaBcDeFgHiJkLmN` | | `METRICS_PORT` | If set, the Radio will expose Prometheus metrics on this (off by default). Example: `3001` | | `METRICS_HOST` | If set, the Radio will expose Prometheus metrics on this (off by default). Example: `0.0.0.0` | @@ -79,24 +76,26 @@ See [Basic Configuration](#basic-configuration) above. The following environment | `PERSISTENCE_FILE_PATH` | Relative path. If set, the Radio will periodically store states of the program to the file in json format (off by default). | | `DISCV5_ENRS` | Comma separated ENRs for Waku Discv5 bootstrapping. Defaults to empty list. | | `DISCV5_PORT` | Discoverable UDP port. Default: `9000` | -| `ID_VALIDATION` | Defines the level of validation for message signers used during radio operation. Options include: `no-check`, `valid-address`, `graphcast-registered`, `graph-network-account`, `registered-indexer`, `indexer` | +| `ID_VALIDATION` | Defines the level of validation for message signers used during radio operation. Options include: `no-check`, `valid-address`, `graphcast-registered`, `graph-network-account`, `registered-indexer`, `indexer` | ### Configurations explained - #### COVERAGE (topic) + `COVERAGE` is used to specify the topic coverage level. It controls the range of topics (subgraph ipfs hashes) the Indexer subscribes to in order to process data and participate in the network. There are three coverage levels available: -- **comprehensive**: Subscribe to on-chain topics, user-defined static topics, and subgraph deployments syncing on graph node. This level is useful for Indexers who want to compare NPOIs for all deployments syncing on their graph node even if they don't have an active allocations open (their stake will not be taken into account in attestation). +- **comprehensive**: Subscribe to on-chain topics, user-defined static topics, and subgraph deployments syncing on graph node. This level is useful for Indexers who want to compare public POIs for all deployments syncing on their graph node even if they don't have an active allocations open (their stake will not be taken into account in attestation). - **on-chain**: Subscribe to on-chain topics and user-defined static topics. This is the default coverage level and is suitable for indexers who only want to compare data for deployments with active allocations. - **minimal**: Only subscribe to user-defined static topics. This level is for Indexers who want to limit their participation to specific topics of interest. #### Identity validaiton + `ID_VALIDATION` is used to define level of validation for message signers used during radio operation. Available Options: + - **no-check**: does not perform check on the message signature and does not verify the signer. - **valid-address**: checks the signer to be a valid Ethereum address. - **graphcast-registered**: checks the signer to be registered on Graphcast Registry. @@ -111,7 +110,7 @@ Available Options: If you want to customize the log level, you can toggle `RUST_LOG` environment variable. Here's an example configuration to get more verbose logging: ``` -RUST_LOG="warn,hyper=warn,graphcast_sdk=debug,poi_radio=debug" +RUST_LOG="warn,hyper=warn,graphcast_sdk=debug,subgraph_radio=debug" ``` `Discv5` is an ambient node discovery network for establishing a decentralized network of interconnected Graphcast Radios. Discv5, when used in Graphcast Radios, serves as a dedicated peer-to-peer discovery protocol that empowers Radios to form an efficient, decentralized network. Without Discv5, the traffic within the Graphcast network would largely rely on centrally hosted boot nodes, leading to a less distributed architecture. However, with Discv5, Radios are capable of directly routing messages among themselves, significantly enhancing network decentralization and reducing reliance on the central nodes. If you want to learn more about Discv5, check out the [official spec](https://rfc.vac.dev/spec/33/). @@ -131,7 +130,6 @@ The Radio spins up an HTTP server with a GraphQL API when `SERVER_HOST` and `SER The GraphQL API now includes: - Below are an example query: ```graphql @@ -146,7 +144,7 @@ Query { } } } - + messages{ identifier nonce @@ -163,6 +161,7 @@ Query { ``` example mutation: + ``` mutation{ deleteMessage(id:1) diff --git a/docs/graphcast/radios/one-shot.md b/docs/graphcast/radios/one-shot.md new file mode 100644 index 00000000..537b5ba3 --- /dev/null +++ b/docs/graphcast/radios/one-shot.md @@ -0,0 +1,32 @@ +--- +sidebar_position: 3 +--- + +# 📟 One-shot CLI + +The source code for the one-shot CLI is available [on GitHub](https://github.com/graphops/subgraph-radio/tree/dev/one-shot) as a member of the Subgraph Radio workspace. + +## Introduction + +The one-shot CLI enables sending one-off messages. Currently, its default behaviour is to construct and send a message of type `VersionUpgradeMessage`, which is used for the [Subgraph Versioning functionality](graphcast/design-principles#subgraph-versioning) of Subgraph Radio. + +The one-shot CLI is configured using config variables. You will need to prepare the following config variables (either as env variables or passing CLI args when running the CLI): + +| Name | Description and Examples | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PRIVATE_KEY` | Private key to the Graphcast ID wallet (precendence over mnemonics).
Example: `PRIVATE_KEY=YOUR_PRIVATE_KEY` | +| `MNEMONIC` | Mnemonic to the Graphcast ID wallet (first address of the wallet is used; Only one of private key or mnemonic is needed).
Example: `MNEMONIC=YOUR_MNEMONIC` | +| `GRAPH_ACCOUNT` | Graph account corresponding to Graphcast operator.
Example: `GRAPH_ACCOUNT=YOUR_GRAPH_ACCOUNT` | +| `REGISTRY_SUBGRAPH` | Subgraph endpoint to the Graphcast Registry.
Default: `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli` | +| `NETWORK_SUBGRAPH` | Subgraph endpoint to The Graph network subgraph.
Default: `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli` | +| `GRAPHCAST_NETWORK` | Supported Graphcast networks: mainnet, testnet.
Default: `testnet` | +| `IDENTIFIER` | Subgraph deployment hash is used to be the message content identifier.
Example: `IDENTIFIER=YOUR_IDENTIFIER` | +| `NEW_HASH` | Subgraph deployment hash for the upgrade version of the subgraph.
Example: `NEW_HASH=YOUR_NEW_HASH` | +| `SUBGRAPH_ID` | Subgraph id shared by the old and new deployment.
Example: `SUBGRAPH_ID=YOUR_SUBGRAPH_ID` | +| `INDEX_NETWORK` | Subgraph id shared by the old and new deployment.
Example: `INDEX_NETWORK=YOUR_INDEX_NETWORK` | +| `MIGRATION_TIME` | UNIX timestamp that the developer plan on migrating the usage.
Example: `MIGRATION_TIME=YOUR_MIGRATION_TIME` | +| `ID_VALIDATION` | Identity validation mechanism for message signers (options: no-check, valid-address, graphcast-registered, graph-network-account, registered-indexer, indexer).
Example: `ID_VALIDATION=valid-address` | +| `LOG_LEVEL` | Logging configuration to set as RUST_LOG.
Default: `info` | +| `LOG_FORMAT` | Support logging formats: pretty, json, full, compact.
Default: `pretty` | + +The one-shot CLI code is very extensible and could be altered to send any kind of Graphcast-compatible message to the network. diff --git a/docs/graphcast/radios/poi-radio.md b/docs/graphcast/radios/subgraph-radio.md similarity index 65% rename from docs/graphcast/radios/poi-radio.md rename to docs/graphcast/radios/subgraph-radio.md index 0e250829..523ac55b 100644 --- a/docs/graphcast/radios/poi-radio.md +++ b/docs/graphcast/radios/subgraph-radio.md @@ -2,45 +2,39 @@ sidebar_position: 1 --- -# 📟 POI Radio +# 📟 Subgraph Radio -The source code for the POI Radio is available [on GitHub](https://github.com/graphops/poi-radio) and Docker builds are automatically published as [GitHub Packages](https://github.com/graphops/poi-radio/pkgs/container/poi-radio). POI Radio is also published as a Crate [on Crates.io](https://crates.io/crates/poi-radio). +The source code for the Subgraph Radio is available [on GitHub](https://github.com/graphops/subgraph-radio) and Docker builds are automatically published as [GitHub Packages](https://github.com/graphops/subgraph-radio/pkgs/container/subgraph-radio). Subgraph Radio is also published as a crate [on crates.io](https://crates.io/crates/subgraph-radio). ## Introduction -The POI Radio is an optional component of the Graph Protocol Indexer Stack that leverages the Graphcast Network to cross-check subgraph data integrity with other participating Indexers. +Subgraph Radio is an optional component of the Graph Protocol Indexer Stack. It uses the Graphcast Network to facilitate the exchange of Subgraph data and information among Indexers and other participants in the network. -In order for an Indexer to earn indexing rewards from the Protocol, valid Proofs of Indexing (POIs) must be posted on-chain regularly, proving that the Indexer has the correct data. Posting an invalid POI exposes the Indexer to the liability of a [Dispute](https://thegraph.com/docs/en/network/indexing/#what-are-disputes-and-where-can-i-view-them) and being slashed by the Protocol. This Radio provides Indexers peace of mind that their POIs are being continuously cross-checked against other participating Indexers. In the event that there is a POI discrepancy, the Radio acts as an early warning system, notifying the Indexer within minutes of the discrepancy occuring. +An essential aspect of earning indexing rewards as an Indexer is the generation of valid Proof of Indexing hashes (POIs). These POIs provide evidence of the Indexer's possession of correct data. Submitting invalid POIs could lead to a [Dispute](https://thegraph.com/docs/en/network/indexing/#what-are-disputes-and-where-can-i-view-them) and possible slashing by the protocol. With Subgraph Radio's POI functionality, Indexers gain confidence knowing that their POIs are continually cross-verified against those of other participating Indexers. Should there be a discrepancy in POIs, Subgraph Radio functions as an early warning system, alerting the Indexer within minutes. -All POIs that are generated by the Radio are normalized, meaning they are hashed with a `0x0` Indexer Address and can be compared between Indexers, all without being valid POIs that could be submitted on-chain for rewards. The Radio will group and weight all normalized POIs by the aggregate stake in GRT that is attesting to each. The normalized POI with the largest aggregate attesting stake is considered canonical and will be used for comparisons with your local Indexer POIs. +All POIs generated through Subgraph Radio are public (normalized), meaning they are hashed with a `0x0` Indexer Address and can be compared between Indexers. However, these public POIs are not valid for on-chain reward submission. Subgraph Radio groups and weighs public POIs according to the aggregate stake in GRT attesting to each. The normalized POI with the most substantial aggregate attesting stake is deemed canonical and used for comparisons with your local Indexer POIs. -For security reasons, POI Radio needs an independent Graphcast ID linked to your Indexer account to operate. Your Graphcast ID is an Ethereum account that has been authorized to sign POI attestations on behalf of your Indexer. Follow instructions for registering your Graphcast ID below. - -## Getting Started - -Before you can run any Radio on Graphcast, you need to register a Graphcast ID for your on-chain Indexer address. You can learn what a Graphcast ID is and how to register one [here](https://docs.graphops.xyz/graphcast/sdk/registry#register-a-graphcast-id). - -Once you complete those steps you will have a Graphcast ID that is authorized to sign messages on behalf of your Indexer. You can then use that Graphcast ID to run a POI Radio instance. +For enhanced security, we recommend running Subgraph Radio with an independent Graphcast ID linked to your Indexer account. This Graphcast ID is an Ethereum account authorized to sign POI attestations on behalf of your Indexer. By default, Subgraph Radio only validates messages received from peers with a registered Graphcast ID (though this behavior can be altered by setting the ID_VALIDATION config variable). Learn how to register a Graphcast ID [here](https://docs.graphops.xyz/graphcast/sdk/registry#register-a-graphcast-id). ### Basic Configuration -The POI Radio is configured using environment variables. You will need to prepare the following environment variables: +The Subgraph Radio is configured using environment variables. You will need to prepare the following environment variables: -| Name | Description and examples | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PRIVATE_KEY` | Private key to the Graphcast ID wallet (Precendence over mnemonics).
Example: `0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef` | -| `INDEXER_ADDRESS` | Indexer address for Graphcast message verification. all lowercase
Example: `0xabcdcabdabcdabcdcabdabcdabcdcabdabcdabcd` | -| `GRAPH_NODE_STATUS_ENDPOINT` | URL to a Graph Node Indexing Status endpoint.
Example: `http://index-node:8030/graphql` | -| `REGISTRY_SUBGRAPH` | URL to the Graphcast Registry subgraph for your network. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | -| `NETWORK_SUBGRAPH` | URL to the Graph Network subgraph. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | | -| `GRAPHCAST_NETWORK` | The Graphcast Messaging fleet and pubsub namespace to use.
Mainnet: `mainnet`
Goerli: `testnet` | +| Name | Description and examples | +| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | +| `PRIVATE_KEY` | Private key of the Graphcast ID wallet or the Indexer Operator wallet (precendence over `MNEMONICS`).
Example: `0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef` | +| `INDEXER_ADDRESS` | Indexer address for Graphcast message verification, in all lowercase.
Example: `0xabcdcabdabcdabcdcabdabcdabcdcabdabcdabcd` | +| `GRAPH_NODE_STATUS_ENDPOINT` | URL to a Graph Node Indexing Status endpoint.
Example: `http://index-node:8030/graphql` | +| `REGISTRY_SUBGRAPH` | URL to the Graphcast Registry subgraph for your network. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | +| `NETWORK_SUBGRAPH` | URL to the Graph Network subgraph. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | | +| `GRAPHCAST_NETWORK` | The Graphcast Messaging fleet and pubsub namespace to use.
Mainnet: `mainnet`
Goerli: `testnet` | ### Run with Docker -1. Pull the POI Radio image +1. Pull the Subgraph Radio image ```bash -docker pull ghcr.io/graphops/poi-radio:latest +docker pull ghcr.io/graphops/subgraph-radio:latest ``` 2. Run the image, providing the required environment variables. Here's a sample mainnet configuration: @@ -52,8 +46,8 @@ docker run \ -e NETWORK_SUBGRAPH="https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet" \ -e PRIVATE_KEY="GRAPHCAST_ID_PRIVATE_KEY" \ -e GRAPH_NODE_STATUS_ENDPOINT="http://graph-node:8030/graphql" \ - -e RUST_LOG="warn,hyper=warn,graphcast_sdk=info,poi_radio=info" \ - ghcr.io/graphops/poi-radio:latest + -e RUST_LOG="warn,hyper=warn,graphcast_sdk=info,subgraph_radio=info" \ + ghcr.io/graphops/subgraph-radio:latest ``` ### (or) Run with docker-compose @@ -63,9 +57,9 @@ You can append this service definition to your `docker-compose` manifest and cus ```yaml services: # ... your other service definitions - poi-radio: - image: ghcr.io/graphops/poi-radio:latest - container_name: poi-radio + subgraph-radio: + image: ghcr.io/graphops/subgraph-radio:latest + container_name: subgraph-radio restart: unless-stopped environment: GRAPHCAST_NETWORK: "mainnet" @@ -73,18 +67,18 @@ services: NETWORK_SUBGRAPH: "https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet" PRIVATE_KEY: "GRAPHCAST_ID_PRIVATE_KEY" GRAPH_NODE_STATUS_ENDPOINT: "http://graph-node:8030/graphql" - RUST_LOG: "warn,hyper=warn,graphcast_sdk=info,poi_radio=info" + RUST_LOG: "warn,hyper=warn,graphcast_sdk=info,subgraph_radio=info" logging: driver: local ``` ### (or) Run as part of [StakeSquid](https://github.com/StakeSquid)'s docker-compose setup -POI Radio is included as an optional component in both the [mainnet](https://github.com/StakeSquid/graphprotocol-mainnet-docker) and [testnet](https://github.com/StakeSquid/graphprotocol-testnet-docker) versions of StakeSquid's guide. The only prerequisite is to have a [Graphcast ID registered](#registering-your-graphcast-id). To enable the POI Radio, you can edit the `.env` file as follows - uncomment the `GRAPHCAST_ID_PRIVATE_KEY` line and replace the placeholder value with the private key of your Graphcast ID wallet. +Subgraph Radio is included as an optional component in both the [mainnet](https://github.com/StakeSquid/graphprotocol-mainnet-docker) and [testnet](https://github.com/StakeSquid/graphprotocol-testnet-docker) versions of StakeSquid's guide. ### (or) Run using a pre-built binary -We also provide pre-built binaries for Ubuntu and MacOS, which you can find in the `Assets` section on each release in the [releases page](https://github.com/graphops/poi-radio/releases) on Github. Simply download the binary, make it executable (`chmod a+x ./poi-radio-{TAG}-{SYSTEM}`) and then run it (using `./poi-radio-{TAG}-{SYSTEM}`). +We also provide pre-built binaries for Ubuntu and MacOS, which you can find in the `Assets` section on each release in the [releases page](https://github.com/graphops/subgraph-radio/releases) on Github. Simply download the binary, make it executable (`chmod a+x ./subgraph-radio-{TAG}-{SYSTEM}`) and then run it (using `./subgraph-radio-{TAG}-{SYSTEM}`). ## Advanced Configuration @@ -92,49 +86,51 @@ In the configuration table below is the full list of environment variables you c See [Basic Configuration](#basic-configuration) above. The following environment variables are optional: -| Name (Optional variables) | Description and examples | -| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `MNEMONIC` | Mnemonic to the Graphcast ID wallet (first address of the wallet is used; Only one of `PRIVATE_KEY` or `MNEMONIC` is needed). Example: `claptrap armchair violin...` | -| `COLLECT_MESSAGE_DURATION` | Seconds that the POI Radio will wait to collect remote POI attestations before making a comparison with the local POI. Example: `120` for 2 minutes. | -| `COVERAGE` | Toggle for topic coverage level. Possible values: "comprehensive", "on-chain", "minimal". Default is set to "on-chain" coverage. | -| `TOPICS` | Comma separated static list of content topics (subgraphs) to subscribe to. Example: `QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz,QmUwCFhXM3f6qH9Ls9Y6gDNURBH7mxsn6JcectgxAz6CwU,QmQ1Lyh3U6YgVP6YX1RgRz6c8GmKkEpokLwPvEtJx6cF1y` | -| `WAKU_HOST` | Interface onto which to bind the bundled Waku node. Example: `0.0.0.0` | -| `WAKU_PORT` | P2P port on which the bundled Waku node will operate. Example: `60000` | -| `WAKU_NODE_KEY` | Static Waku Node Key. | -| `BOOT_NODE_ADDRESSES` | Peer addresses to use as Waku boot nodes. Example: `"addr1, addr2, addr3"` | -| `SLACK_TOKEN` | Slack Token to use for notifications. Example: `xoxp-0123456789-0123456789-0123456789-0123456789` | -| `TELEGRAM_TOKEN` | Telegram Bot Token to use for notifications. Example: `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11` | -| `TELEGRAM_CHAT_ID` | The ID of the Telegram chat to send messages to. Example: `-1001234567890` | -| `SLACK_CHANNEL` | Name of Slack channel to send messages to (has to be a public channel). Example: `poir-notifications` | -| `WAKU_LOG_LEVEL` | Waku node logging configuration. Example: `INFO` (is also the default) | -| `RUST_LOG` | Rust tracing configuration. Example: `graphcast_sdk=debug,poi_radio=debug`, defaults to `info` for everything | -| `DISCORD_WEBHOOK` | Discord webhook URL for notifications. Example: `https://discord.com/api/webhooks/123456789012345678/AbCDeFgHiJkLmNoPqRsTuVwXyZaBcDeFgHiJkLmN` | -| `METRICS_PORT` | If set, the Radio will expose Prometheus metrics on this (off by default). Example: `3001` | -| `METRICS_HOST` | If set, the Radio will expose Prometheus metrics on this (off by default). Example: `0.0.0.0` | -| `SERVER_HOST` | If `SERVER_PORT` is set, the Radio will expose an API service on the given host and port. Default: `0.0.0.0` | -| `SERVER_PORT` | If set, the Radio will expose an API service on the given port (off by default). Example: `8080` | -| `LOG_FORMAT` | Options: `pretty` - verbose and human readable; `json` - not verbose and parsable; `compact` - not verbose and not parsable; `full` - verbose and not parsible. Default value: `pretty`. | -| `PERSISTENCE_FILE_PATH` | Relative path. If set, the Radio will periodically store states of the program to the file in json format (off by default). | -| `DISCV5_ENRS` | Comma separated ENRs for Waku Discv5 bootstrapping. Defaults to empty list. | -| `DISCV5_PORT` | Discoverable UDP port. Default: `9000` | +| Name (Optional variables) | Description and examples | +| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MNEMONIC` | Mnemonic to the Graphcast ID wallet or the Indexer Operator wallet (first address of the wallet is used; Only one of `PRIVATE_KEY` or `MNEMONIC` is needed). Example: `claptrap armchair violin...` | +| `COLLECT_MESSAGE_DURATION` | Seconds that the Subgraph Radio will wait to collect remote POI attestations before making a comparison with the local POI. Example: `120` for 2 minutes. | +| `COVERAGE` | Toggle for topic coverage level. Possible values: "comprehensive", "on-chain", "minimal". Default is set to "on-chain" coverage. | +| `TOPICS` | Comma separated static list of content topics (subgraphs) to subscribe to. Example: `QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz,QmUwCFhXM3f6qH9Ls9Y6gDNURBH7mxsn6JcectgxAz6CwU,QmQ1Lyh3U6YgVP6YX1RgRz6c8GmKkEpokLwPvEtJx6cF1y` | +| `WAKU_HOST` | Interface onto which to bind the bundled Waku node. Example: `0.0.0.0` | +| `WAKU_PORT` | P2P port on which the bundled Waku node will operate. Example: `60000` | +| `WAKU_NODE_KEY` | Static Waku Node Key. | +| `BOOT_NODE_ADDRESSES` | Peer addresses to use as Waku boot nodes. Example: `"addr1, addr2, addr3"` | +| `SLACK_TOKEN` | Slack Token to use for notifications. Example: `xoxp-0123456789-0123456789-0123456789-0123456789` | +| `TELEGRAM_TOKEN` | Telegram Bot Token to use for notifications. Example: `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11` | +| `TELEGRAM_CHAT_ID` | The ID of the Telegram chat to send messages to. Example: `-1001234567890` | +| `SLACK_CHANNEL` | Name of Slack channel to send messages to (has to be a public channel). Example: `poir-notifications` | +| `WAKU_LOG_LEVEL` | Waku node logging configuration. Example: `INFO` (is also the default) | +| `RUST_LOG` | Rust tracing configuration. Example: `graphcast_sdk=debug,subgraph_radio=debug`, defaults to `info` for everything | +| `DISCORD_WEBHOOK` | Discord webhook URL for notifications. Example: `https://discord.com/api/webhooks/123456789012345678/AbCDeFgHiJkLmNoPqRsTuVwXyZaBcDeFgHiJkLmN` | +| `METRICS_PORT` | If set, the Radio will expose Prometheus metrics on this (off by default). Example: `3001` | +| `METRICS_HOST` | If set, the Radio will expose Prometheus metrics on this (off by default). Example: `0.0.0.0` | +| `SERVER_HOST` | If `SERVER_PORT` is set, the Radio will expose an API service on the given host and port. Default: `0.0.0.0` | +| `SERVER_PORT` | If set, the Radio will expose an API service on the given port (off by default). Example: `8080` | +| `LOG_FORMAT` | Options: `pretty` - verbose and human readable; `json` - not verbose and parsable; `compact` - not verbose and not parsable; `full` - verbose and not parsible. Default value: `pretty`. | +| `PERSISTENCE_FILE_PATH` | Relative path. If set, the Radio will periodically store states of the program to the file in json format (off by default). | +| `DISCV5_ENRS` | Comma separated ENRs for Waku Discv5 bootstrapping. Defaults to empty list. | +| `DISCV5_PORT` | Discoverable UDP port. Default: `9000` | | `ID_VALIDATION` | Defines the level of validation for message signers used during radio operation. Options include: `no-check`, `valid-address`, `graphcast-registered`, `graph-network-account`, `registered-indexer`, `indexer`. Default: `registered-indexer` | ### Configurations explained - #### COVERAGE (topic) + `COVERAGE` is used to specify the topic coverage level. It controls the range of topics (subgraph ipfs hashes) the Indexer subscribes to in order to process data and participate in the network. There are three coverage levels available: -- **comprehensive**: Subscribe to on-chain topics, user-defined static topics, and subgraph deployments syncing on graph node. This level is useful for Indexers who want to compare NPOIs for all deployments syncing on their graph node even if they don't have an active allocations open (their stake will not be taken into account in attestation). +- **comprehensive**: Subscribe to on-chain topics, user-defined static topics, and subgraph deployments syncing on graph node. This level is useful for Indexers who want to compare public POIs for all deployments syncing on their graph node even if they don't have an active allocations open (their stake will not be taken into account in attestation). - **on-chain**: Subscribe to on-chain topics and user-defined static topics. This is the default coverage level and is suitable for indexers who only want to compare data for deployments with active allocations. - **minimal**: Only subscribe to user-defined static topics. This level is for Indexers who want to limit their participation to specific topics of interest. #### Identity validaiton -`ID_VALIDATION` is used to define level of validation for message signers used during radio operation. We recommend `registered-indexer` for most strict identity validation, while `indexer` is a viable option for those who want to use the network before considering Grapchast ID registration. You can choose a sender identity validation mechanism for your radio, based on your use case and security preferences. + +`ID_VALIDATION` is used to define level of validation for message signers used during radio operation. We recommend `registered-indexer` for most strict identity validation, while `indexer` is a viable option for those who want to use the network before considering Grapchast ID registration. You can choose a sender identity validation mechanism for your radio, based on your use case and security preferences. Available Options: + - **no-check**: Does not perform check on the message signature and does not verify the signer. All messages should pass the sender check. - **valid-address**: Requires the signer to be a valid Ethereum address. Messages should be traceable to an Ethers wallet. - **graphcast-registered**: Requires the signer to be registered on the Graphcast Registry. @@ -149,14 +145,14 @@ Available Options: If you want to customize the log level, you can toggle `RUST_LOG` environment variable. Here's an example configuration to get more verbose logging: ``` -RUST_LOG="warn,hyper=warn,graphcast_sdk=debug,poi_radio=debug" +RUST_LOG="warn,hyper=warn,graphcast_sdk=debug,subgraph_radio=debug" ``` `Discv5` is an ambient node discovery network for establishing a decentralized network of interconnected Graphcast Radios. Discv5, when used in Graphcast Radios, serves as a dedicated peer-to-peer discovery protocol that empowers Radios to form an efficient, decentralized network. Without Discv5, the traffic within the Graphcast network would largely rely on centrally hosted boot nodes, leading to a less distributed architecture. However, with Discv5, Radios are capable of directly routing messages among themselves, significantly enhancing network decentralization and reducing reliance on the central nodes. If you want to learn more about Discv5, check out the [official spec](https://rfc.vac.dev/spec/33/). #### State management -`PERSISTENCE_FILE_PATH` configuration variable allows the Radio to maintain operational continuity across sessions. When the file path is set, it triggers the Radio to periodically store its state, including local attestations and remote messages, in a JSON-formatted file at the specified path. This facilitates seamless session transitions and minimizes data loss. In the event of a system disruption, the state can be reloaded from this file, ensuring the Radio can resume operation effectively. +`PERSISTENCE_FILE_PATH` configuration variable allows the Radio to maintain operational continuity across sessions. When the file path is set, it triggers the Radio to periodically store its state, including local attestations, remote messages and POI comparison results in a JSON-formatted file at the specified path. This facilitates seamless session transitions and minimizes data loss. In the event of a system disruption, the state can be reloaded from this file, ensuring the Radio can resume operation effectively. ## Monitoring the Radio @@ -166,7 +162,7 @@ If the Radio operator has set up a Slack, Discord and/or Telegram bot integratio ### Prometheus & Grafana -The POI Radio exposes metrics that can then be scraped by a Prometheus server and displayed in Grafana. In order to use them you have to have a local Prometheus server running and scraping metrics on the provided port. You can specify the metrics host and port by using the environment variables `METRICS_PORT` and `METRICS_HOST`. We also provide a [Grafana dashboard config JSON file](https://github.com/graphops/poi-radio/blob/main/poi-radio-grafana.json) which you can use to visualise the metrics in Grafana. +The Subgraph Radio exposes metrics that can then be scraped by a Prometheus server and displayed in Grafana. In order to use them you have to have a local Prometheus server running and scraping metrics on the provided port. You can specify the metrics host and port by using the environment variables `METRICS_PORT` and `METRICS_HOST`. We also provide a [Grafana dashboard config JSON file](https://github.com/graphops/subgraph-radio/blob/main/subgraph-radio-grafana.json) which you can use to visualise the metrics in Grafana. ## HTTP Server @@ -198,18 +194,18 @@ Query { deployment blockNumber attestation{ - npoi + ppoi } } - comparisonResults(deployment:"Qm...."){ + comparisonResults(identifier:"Qm...."){ deployment blockNumber resultType localAttestation{ - npoi + ppoi } attestations{ - npoi + ppoi } } stakeRatio(filter: {deployment: "__", blockNumber: "___"}){ @@ -248,12 +244,12 @@ Note: The `result_type` field of the filter corresponds to the `resultType` fiel The API also includes the `senderRatio` and `stakeRatio` endpoints, which return more detailed insights into the state of the Radio. -`senderRatio` provides an overview of the consensus status of the attestations from remote messages. It gives a ratio string that signifies the number of indexers with the same npoi as the local Radio. The results are presented as `x/y!/z` where: +`senderRatio` provides an overview of the consensus status of the attestations from remote messages. It gives a ratio string that signifies the number of indexers with the same public POI as the local Radio. The results are presented as `x/y!/z` where: - `x`, `y`, and `z` are sorted by descending stake weights - `!` indicates the entry that corresponds to the local result. -For example,` 2/0!` means there are two indexers attesting with a higher sum of stake weight and no other indexer shares the same nPOI as the local Radio. `8!` means there are eight other indexers agreeing with the local Radio. +For example,` 2/0!` means there are two indexers attesting with a higher sum of stake weight and no other indexer shares the same public POIs as the local Radio. `8!` means there are eight other indexers agreeing with the local Radio. `stakeRatio` offers similar functionality to senderRatio but the results are based on the stake weight. It orders the attestations by stake weight, then computes the ratio of unique senders. @@ -278,7 +274,7 @@ These queries provide a clear aggregation of the attestations from remote messag ### Fetching active allocations -The POI Radio is responsible for reading active allocations of the Radio operator's corresponding Indexer. It periodically polls the Graph Node for new blocks on all relevant networks and constructs Graphcast topics on each allocation identified by subgraph deployment IPFS hash. +The Subgraph Radio is responsible for reading active allocations of the Radio operator's corresponding Indexer. It periodically polls the Graph Node for new blocks on all relevant networks and constructs Graphcast topics on each allocation identified by subgraph deployment IPFS hash. :::tip The relevant networks are those corresponding to the subgraphs that have active allocations. @@ -290,35 +286,41 @@ The Radio fetches new active allocations at a regular interval to ensure that it sequenceDiagram participant Network Subgraph participant Graph Node - participant POI Radio + participant Subgraph Radio participant Graphcast Network actor Human loop Track allocated deployments - POI Radio->>+Network Subgraph: Get latest allocated deployments - Network Subgraph->>-POI Radio: Return allocated deployments + Subgraph Radio->>+Network Subgraph: Get latest allocated deployments + Network Subgraph->>-Subgraph Radio: Return allocated deployments loop Monitor allocated deployments and chain heads - POI Radio->>+Graph Node: Get indexing statuses for allocated deployments - Graph Node->>-POI Radio: Return matching indexing statuses - activate POI Radio - POI Radio->>POI Radio: Update chain heads - deactivate POI Radio + Subgraph Radio->>+Graph Node: Get indexing statuses for allocated deployments + Graph Node->>-Subgraph Radio: Return matching indexing statuses + activate Subgraph Radio + Subgraph Radio->>Subgraph Radio: Update chain heads + deactivate Subgraph Radio loop For each deployment that we are tracking opt If deployment reached trigger block is healthy - POI Radio->>+Graph Node: Fetch POI for deployment - Graph Node->>-POI Radio: Normalized POI - activate POI Radio - POI Radio->>POI Radio: Generate signed POI Attestation - deactivate POI Radio - POI Radio-->>Graphcast Network: Broadcast POI Attestation to Graphcast Network + Subgraph Radio->>+Graph Node: Fetch POI for deployment + Graph Node->>-Subgraph Radio: Normalized POI + activate Subgraph Radio + Subgraph Radio->>Subgraph Radio: Generate signed POI Attestation + deactivate Subgraph Radio + Subgraph Radio-->>Graphcast Network: Broadcast POI Attestation to Graphcast Network end opt If stored remote attestations and collect message duration passed - activate POI Radio - POI Radio->>POI Radio: Compute consensus remote POI - deactivate POI Radio + activate Subgraph Radio + Subgraph Radio->>Subgraph Radio: Compute consensus remote POI + deactivate Subgraph Radio opt If local POI mismatches consensus remote POI - POI Radio-->>Human: Send POI divergence warning notification + Subgraph Radio-->>Human: Send POI divergence warning notification end end + opt If VersionUpgradeMessage is received + Graphcast Network-->>Subgraph Radio: VersionUpgradeMessage + activate Subgraph Radio + Subgraph Radio-->>Human: Send Version Upgrade notification + deactivate Subgraph Radio + end end end end @@ -326,7 +328,7 @@ sequenceDiagram ### Gathering and comparing normalised POIs -At a given interval, the Radio fetches the normalised POI for each deployment. This interval is defined in blocks different for each network. It then saves those nPOIs, and as other Indexers running the Radio start doing the same, messages start propagating through the network. The Radio saves each message and processes them on a given interval. +At a given interval, the Radio fetches the normalised POI for each deployment. This interval is defined in blocks different for each network. It then saves those public POIs, and as other Indexers running the Radio start doing the same, messages start propagating through the network. The Radio saves each message and processes them on a given interval. The messages include a nonce (UNIX timestamp), block number, signature (used to derive the sender's on-chain Indexer address) and network. Before saving an entry to the map, the Radio operator verifies through the Graph network subgraph for the sender's on-chain identity and amount of tokens staked, which is used during comparisons later on. @@ -342,7 +344,7 @@ flowchart LR n --> x{End} ``` -At another interval, the Radio compares the local nPOIs with the collected remote ones. The remote nPOIs are sorted so that for each subgraph (on each block), the nPOI that is backed by the most on-chain stake is selected. This means that the combined stake of all Indexers that attested to it is considered, not just the highest staking Indexer. The top nPOI is then compared with the local nPOI for that subgraph at that block to determine consensus. +At another interval, the Radio compares the local public POIs with the collected remote ones. The remote POIs are sorted so that for each subgraph (on each block), the POI that is backed by the most on-chain stake is selected. This means that the combined stake of all Indexers that attested to it is considered, not just the highest staking Indexer. The top POI is then compared with the local POIs for that subgraph at that block to determine consensus. If there is a mismatch and if the Radio operator has set up a Slack, Discord and/or Telegram bot integration, the Radio will send alerts to the designated channels. @@ -362,26 +364,26 @@ flowchart LR l -->|Yes| d{End} ``` -## Developing the POI Radio +## Developing the Subgraph Radio #### Building the image using the Dockerfile locally -If you want to make any changes to the POI Radio codebase, you can use this option. +If you want to make any changes to the Subgraph Radio codebase, you can use this option. ##### Prerequisites 1. Clone this repo and `cd` into it 2. Create a `.env` file that includes at least the required environment variables. To see the full list of environment variables you can provide, check out the [Configuration](#configuration) section. -##### Running the POI Radio inside a Docker container +##### Running the Subgraph Radio inside a Docker container ```bash docker-compose up -d ``` -### Building POI Radio locally +### Building Subgraph Radio locally -To have full control over the POI Radio code and run it directly on your machine (without Docker) you can use this option. +To have full control over the Subgraph Radio code and run it directly on your machine (without Docker) you can use this option. #### Prerequisites @@ -398,7 +400,7 @@ To have full control over the POI Radio code and run it directly on your machine 3. You have **Graph Node** syncing your indexer's on-chain allocations. 4. You have created a `.env` file that includes at least the required environment variables. To see the full list of environment variables you can provide, check out the [Configuration](#configuration) section. -#### Running the POI Radio natively +#### Running the Subgraph Radio natively ``` cargo run diff --git a/docs/graphcast/sdk/radio-dev.md b/docs/graphcast/sdk/radio-dev.md index bd805e75..1c8ae81d 100644 --- a/docs/graphcast/sdk/radio-dev.md +++ b/docs/graphcast/sdk/radio-dev.md @@ -6,7 +6,7 @@ sidebar_position: 2 Do you want to build robust, peer-to-peer messaging apps that automatically exchange valuable data with other Indexers in real time? Do you have an idea for what data could be useful to share that could lead to greater communication efficiency in The Graph network as a whole? Then you want to build a Radio on top of the Graphcast network. -For a more complex and full example of the Graphcast SDK being used to create a POI Radio, take a look at this [implementation in the POC repo](https://github.com/graphops/poi-radio). +For a more complex and full example of the Graphcast SDK being used to create a Subgraph Radio, take a look at this [repo](https://github.com/graphops/subgraph-radio). ## A simple ping pong example @@ -14,21 +14,20 @@ Let's take a look at the simplest possible example of a Radio, built on top of G ### Register a Graphcast ID -First things first - before you can run any Radio on Graphcast, you need to register a Graphcast ID for your on-chain Indexer address. You can learn what a Graphcast ID is and how to register one [here](https://docs.graphops.xyz/graphcast/sdk/registry#register-a-graphcast-id). +We recommend that you register a Graphcast ID for your on-chain Indexer address. You can learn what a Graphcast ID is and how to register one [here](https://docs.graphops.xyz/graphcast/sdk/registry#register-a-graphcast-id). -Once you complete those steps you will have a Graphcast ID that is authorized to sign messages on behalf of your Indexer. You can then use that Graphcast ID to run a POI Radio instance. +Once you complete those steps you will have a Graphcast ID that is authorized to sign messages on behalf of your Indexer. ### Populate your `.env` file You now need to export a few environment variables: -| Name | Description and examples | -| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PRIVATE_KEY` | Private key to the Graphcast ID wallet (Precendence over mnemonics).
Example: `0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef` | -| `GRAPH_NODE_STATUS_ENDPOINT` | URL to a Graph Node Indexing Status endpoint.
Example: `http://index-node:8030/graphql` | -| `REGISTRY_SUBGRAPH` | URL to the Graphcast Registry subgraph for your network. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | -| `NETWORK_SUBGRAPH` | URL to the Graph Network subgraph. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | -| `GRAPHCAST_NETWORK` | The Graphcast Messaging fleet and pubsub namespace to use. For this example you should use `testnet` | +| Name | Description and examples | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PRIVATE_KEY` | Private key to the Graphcast ID wallet or Indexer Operator wallet (Precendence over `MNEMONICS`).
Example: `0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef` | +| `REGISTRY_SUBGRAPH` | URL to the Graphcast Registry subgraph for your network. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | +| `NETWORK_SUBGRAPH` | URL to the Graph Network subgraph. Check [APIs](../sdk/registry#subgraph-apis) for your preferred network | +| `GRAPHCAST_NETWORK` | The Graphcast Messaging fleet and pubsub namespace to use. For this example you should use `testnet` | ### A few dependencies @@ -45,7 +44,7 @@ Start off with a new Rust project (`cargo new ping-pong`). Then add the followin ```Rust [dependencies] -graphcast-sdk = "0.1.1" +graphcast-sdk = "0.4.0" once_cell = "1.15" tokio = { version = "1.1.1", features = ["full"] } anyhow = "1.0.39" @@ -65,26 +64,18 @@ serde_derive = "1.0.114" Open your `main.rs` file and add the following imports: ```Rust +// For date and time utils +use chrono::Utc; + // Load environment variables from .env file use dotenv::dotenv; -// Import Graphcast SDK types and functions for agent configuration, message handling, and more -use graphcast_sdk::{ - graphcast_agent::{ - message_typing::GraphcastMessage, waku_handling::WakuHandlingError, GraphcastAgent, - GraphcastAgentConfig, - }, - graphql::client_graph_node::update_network_chainheads, - networks::NetworkName, - BlockPointer, -}; - -// Import the OnceCell container for lazy initialization of global/static data -use once_cell::sync::OnceCell; - // Import Arc and Mutex for thread-safe sharing of data across threads use std::sync::{Arc, Mutex}; +// Import Graphcast SDK types and functions for agent configuration, message handling, and more +use graphcast_sdk::graphcast_agent::{GraphcastAgent, GraphcastAgentConfig}; + // Import sleep and Duration for handling time intervals and thread delays use std::{thread::sleep, time::Duration}; @@ -92,14 +83,16 @@ use std::{thread::sleep, time::Duration}; use tokio::sync::Mutex as AsyncMutex; // Import tracing macros for logging and diagnostic purposes -use tracing::{debug, error, info}; +use tracing::{debug, error, info, trace}; -// Import RadioPayloadMessage from the crate's types module -use types::RadioPayloadMessage; +// Import SimpleMessage from the crate's types module +use types::SimpleMessage; // Import Config from the crate's config module use config::Config; +use crate::types::{GRAPHCAST_AGENT, MESSAGES}; + // Include the local config and types modules mod config; mod types; @@ -125,9 +118,26 @@ use async_graphql::SimpleObject; use ethers_contract::EthAbiType; use ethers_core::types::transaction::eip712::Eip712; use ethers_derive_eip712::*; +use graphcast_sdk::graphcast_agent::GraphcastAgent; use prost::Message; use serde::{Deserialize, Serialize}; +// Import the OnceCell container for lazy initialization of global/static data +use once_cell::sync::OnceCell; +use std::sync::{Arc, Mutex}; + +/// A global static (singleton) instance of A GraphcastMessage vector. +/// It is used to save incoming messages after they've been validated, in order +/// defer their processing for later, because async code is required for the processing but +/// it is not allowed in the handler itself. +pub static MESSAGES: OnceCell>>> = OnceCell::new(); + +/// The Graphcast Agent instance must be a global static variable (for the time being). +/// This is because the Radio handler requires a static immutable context and +/// the handler itself is being passed into the Graphcast Agent, so it needs to be static as well. +pub static GRAPHCAST_AGENT: OnceCell = OnceCell::new(); + +/// Make a test radio type #[derive(Eip712, EthAbiType, Clone, Message, Serialize, Deserialize, SimpleObject)] #[eip712( name = "Graphcast Ping-Pong Radio", @@ -135,26 +145,36 @@ use serde::{Deserialize, Serialize}; chain_id = 1, verifying_contract = "0xc944e90c64b2c07662a292be6244bdf05cda44a7" )] -pub struct RadioPayloadMessage { +pub struct SimpleMessage { #[prost(string, tag = "1")] pub identifier: String, #[prost(string, tag = "2")] pub content: String, } -impl RadioPayloadMessage { +impl SimpleMessage { pub fn new(identifier: String, content: String) -> Self { - RadioPayloadMessage { + SimpleMessage { identifier, content, } } + + pub fn radio_handler(&self) { + MESSAGES + .get() + .expect("Could not retrieve messages") + .lock() + .expect("Could not get lock on messages") + .push(self.clone()); + } } + ``` -`RadioPayloadMessage` defines the the general structure that the Graphcast SDK expects from a type that will be used as a Radio payload. +`SimpleMessage` defines the structure that all messages for this Radio must follow. -`RadioPayloadMessage` is decorated with several macros - #[derive(Eip712, EthAbiType, Clone, Message, Serialize, Deserialize)], which automatically implement certain traits that are required in the SDK. +`RadioPayloadMessage` is decorated with several macros - #[derive(Eip712, EthAbiType, Clone, Message, Serialize, Deserialize)], which automatically implement certain traits that are required by the SDK. The `#[eip712]` macro is used to define information that is used in EIP-712, a standard for structuring typed data in Ethereum transactions. @@ -164,15 +184,16 @@ Now let's see the `config.rs` file: use clap::Parser; use ethers::signers::WalletError; use graphcast_sdk::build_wallet; -use graphcast_sdk::graphcast_id_address; +use graphcast_sdk::graphcast_agent::message_typing::IdentityValidation; use graphcast_sdk::init_tracing; +use graphcast_sdk::wallet_address; use serde::{Deserialize, Serialize}; use tracing::info; #[derive(Clone, Debug, Parser, Serialize, Deserialize)] #[clap( - name = "poi-radio", - about = "Cross-check POIs with other Indexer in real time", + name = "ping-pong-radio", + about = "A simple example for using the Graphcast SDK to build Radios", author = "GraphOps" )] pub struct Config { @@ -182,7 +203,7 @@ pub struct Config { env = "GRAPH_NODE_STATUS_ENDPOINT", help = "API endpoint to the Graph Node Status Endpoint" )] - pub graph_node_endpoint: String, + pub graph_node_endpoint: Option, #[clap( long, value_name = "KEY", @@ -209,12 +230,19 @@ pub struct Config { default_value = "https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli" )] pub registry_subgraph: String, + #[clap( + long, + value_name = "INDEXER_ADDRESS", + env = "INDEXER_ADDRESS", + help = "Graph account corresponding to Graphcast operator" + )] + pub indexer_address: String, #[clap( long, value_name = "SUBGRAPH", env = "NETWORK_SUBGRAPH", help = "Subgraph endpoint to The Graph network subgraph", - default_value = "https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli/network" + default_value = "https://gateway.testnet.thegraph.com/network" )] pub network_subgraph: String, #[clap( @@ -227,11 +255,28 @@ pub struct Config { default_value = "full" )] pub log_format: String, + #[clap( + long, + value_name = "ID_VALIDATION", + value_enum, + env = "ID_VALIDATION", + default_value = "valid-address", + help = "Identity validation mechanism for senders (message signers)", + long_help = "Identity validation mechanism for senders (message signers)\n + no-check: all messages signer is valid, \n + valid-address: signer needs to be an valid Eth address, \n + graphcast-registered: must be registered at Graphcast Registry, \n + graph-network-account: must be a Graph account, \n + registered-indexer: must be registered at Graphcast Registry, correspond to and Indexer statisfying indexer minimum stake requirement, \n + indexer: must be registered at Graphcast Registry or is a Graph Account, correspond to and Indexer statisfying indexer minimum stake requirement" + )] + pub id_validation: IdentityValidation, } impl Config { /// Parse config arguments pub fn args() -> Self { + // TODO: load config file before parse (maybe add new level of subcommands) let config = Config::parse(); init_tracing(config.log_format.clone()).expect("Could not set up global default subscriber for logger, check environmental variable `RUST_LOG` or the CLI input `log-level`"); config @@ -241,7 +286,7 @@ impl Config { fn parse_key(value: &str) -> Result { // The wallet can be stored instead of the original private key let wallet = build_wallet(value)?; - let addr = graphcast_id_address(&wallet); + let addr = wallet_address(&wallet); info!(address = addr, "Resolved Graphcast id"); Ok(String::from(value)) } @@ -275,18 +320,6 @@ let _parent_span = tracing::info_span!("main").entered(); Now let's instantiate a few variables that will do all the heavy lifting for us. ```Rust -/// A global static (singleton) instance of A GraphcastMessage vector. -/// It is used to save incoming messages after they've been validated, in order -/// defer their processing for later, because async code is required for the processing but -/// it is not allowed in the handler itself. -pub static MESSAGES: OnceCell>>>> = - OnceCell::new(); - -/// The Graphcast Agent instance must be a global static variable (for the time being). -/// This is because the Radio handler requires a static immutable context and -/// the handler itself is being passed into the Graphcast Agent, so it needs to be static as well. -pub static GRAPHCAST_AGENT: OnceCell = OnceCell::new(); - // Subtopics are optionally provided and used as the content topic identifier of the message subject, // if not provided then they are usually generated based on indexer allocations let subtopics: Vec = vec!["ping-pong-content-topic".to_string()]; @@ -294,20 +327,23 @@ let subtopics: Vec = vec!["ping-pong-content-topic".to_string()]; // GraphcastAgentConfig defines the configuration that the SDK expects from all Radios, regardless of their specific functionality let graphcast_agent_config = GraphcastAgentConfig::new( config.private_key.expect("No private key provided"), + config.indexer_address, radio_name, config.registry_subgraph, config.network_subgraph, - config.graph_node_endpoint.clone(), + config.id_validation.clone(), + config.graph_node_endpoint, None, Some("testnet".to_string()), Some(subtopics), None, None, None, + None, + Some(true), // Example ENR address Some(vec![String::from("enr:-JK4QBcfVXu2YDeSKdjF2xE5EDM5f5E_1Akpkv_yw_byn1adESxDXVLVjapjDvS_ujx6MgWDu9hqO_Az_CbKLJ8azbMBgmlkgnY0gmlwhAVOUWOJc2VjcDI1NmsxoQOUZIqKLk5xkiH0RAFaMGrziGeGxypJ03kOod1-7Pum3oN0Y3CCfJyDdWRwgiMohXdha3UyDQ")]), None, - None, ) .await .unwrap_or_else(|e| panic!("Could not create GraphcastAgentConfig: {e}")); @@ -318,7 +354,8 @@ let graphcast_agent_config = GraphcastAgentConfig::new( Next, we will instantiate a `GraphcastAgent`: ```Rust -let graphcast_agent = GraphcastAgent::new(graphcast_agent_config) +debug!("Initializing the Graphcast Agent"); +let (graphcast_agent, waku_msg_receiver) = GraphcastAgent::new(graphcast_agent_config) .await .expect("Could not create Graphcast agent"); ``` @@ -343,21 +380,16 @@ We'll define a helper function that holds the logic of sending messages to the G ```Rust // Helper function to reuse message sending code -async fn send_message( - payload: Option, - network: NetworkName, - block_number: u64, -) { +async fn send_message(payload: SimpleMessage) { if let Err(e) = GRAPHCAST_AGENT .get() .expect("Could not retrieve Graphcast agent") .send_message( // The identifier can be any string that suits your Radio logic // If it doesn't matter for your Radio logic (like in this case), you can just use a UUID or a hardcoded string - "ping-pong-content-topic".to_string(), - network, - block_number, + "ping-pong-content-topic", payload, + Utc::now().timestamp(), ) .await { @@ -378,30 +410,35 @@ Here is a simple handler that does just that: ```Rust // The handler specifies what to do with incoming messages. -// There cannot be any non-deterministic (this includes async) code inside the handler. -// That is why we're saving the message for later processing, where we will check its content and perform some action based on it. -let radio_handler = - |msg: Result, WakuHandlingError>| match msg { - Ok(msg) => { - MESSAGES - .get() - .expect("Could not retrieve messages") - .lock() - .expect("Could not get lock on messages") - .push(msg); - } - Err(err) => { - error!( - error = tracing::field::debug(&err), - "Failed to handle Waku signal" - ); - } - }; +// This is where you can define multiple message types and how they gets handled by the radio +// by chaining radio payload typed decode and handler functions +tokio::spawn(async move { + for msg in waku_msg_receiver { + trace!( + "Radio operator received a Waku message from Graphcast agent, now try to fit it to Graphcast Message with Radio specified payload" + ); + let _ = GRAPHCAST_AGENT + .get() + .expect("Could not retrieve Graphcast agent") + .decoder::(msg.payload()) + .await + .map(|msg| { + msg.payload.radio_handler(); + }) + .map_err(|err| { + error!( + error = tracing::field::debug(&err), + "Failed to handle Waku signal" + ); + err + }); + } +}); GRAPHCAST_AGENT .get() .expect("Could not retrieve Graphcast agent") - .register_handler(Arc::new(AsyncMutex::new(radio_handler))) + .register_handler() .expect("Could not register handler"); ``` @@ -412,41 +449,18 @@ Great, we're almost there! We have a way to pass messages back and forth 🏓. B We'll start listening to Ethereum blocks coming from the Graph Node and on each block we'll do a simple check - if the block number is even we'll send a "Ping" message, and if it's odd we'll process the messages we've received. After processing the messages we'll clear our store. ```Rust -let network = NetworkName::from_string("goerli"); +let mut block_number = 0; loop { - let mut network_chainhead_blocks = match GRAPHCAST_AGENT - .get() - .unwrap() - .callbook - .indexing_statuses() - .await - { - Ok(res) => update_network_chainheads(res), - Err(e) => { - error!( - err = tracing::field::debug(&e), - "Could not query indexing statuses, pull again later" - ); - continue; - } - }; - let block_number = network_chainhead_blocks - .entry(network) - .or_insert(BlockPointer { - number: 0, - hash: "temp".to_string(), - }) - .number; + block_number += 1; info!(block = block_number, "🔗 Block number"); - if block_number & 2 == 0 { // If block number is even, send ping message - let msg = RadioPayloadMessage::new( + let msg = SimpleMessage::new( "table".to_string(), std::env::args().nth(1).unwrap_or("Ping".to_string()), ); - send_message(Some(msg), network, block_number).await; + send_message(msg).await; } else { // If block number is odd, process received messages let messages = AsyncMutex::new( @@ -457,14 +471,9 @@ loop { .expect("Could not get lock on messages"), ); for msg in messages.lock().await.iter() { - let payload = msg - .payload - .as_ref() - .expect("Could not get radio payload payload"); - if *payload.content == *"Ping" { - let replay_msg = - RadioPayloadMessage::new("table".to_string(), "Pong".to_string()); - send_message(Some(replay_msg), network, block_number).await; + if msg.content == *"Ping" { + let replay_msg = SimpleMessage::new("table".to_string(), "Pong".to_string()); + send_message(replay_msg).await; }; } diff --git a/docs/graphcast/sdk/registry.md b/docs/graphcast/sdk/registry.md index dcf0698c..75a87803 100644 --- a/docs/graphcast/sdk/registry.md +++ b/docs/graphcast/sdk/registry.md @@ -10,7 +10,7 @@ There are also [subgraphs](https://github.com/graphops/graphcast-registry-subgra ### Register a Graphcast ID -The Graphcast Registry contract maps Graphcast IDs to Indexers in the Graph Protocol. You'll need to register a Graphcast ID that is authorized to sign messages on behalf of your Indexer in order to use Graphcast. +The Graphcast Registry contract maps Graphcast IDs to Indexers in the Graph Protocol. With a unique Graphcast ID, an Indexer can sign messages for the Radio, eliminating the need to expose their private Indexer (or Indexer Operator) key or mnemonic. This provides an added layer of security, protecting Indexers' sensitive information while enabling participation in the Graphcast Network. Here is a brief overview of the accounts you'll be interacting with: @@ -42,12 +42,12 @@ You can register multiple Operators for your Indexer in parallel. If you would p #### Registry endpoints -| Network | Registry Contract | Subgraph API | -| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| Ethereum-mainnet | [0x89f97698d6006f25570cd2e31737d3d22aedcbcf](https://etherscan.io/address/0x89f97698d6006f25570cd2e31737d3d22aedcbcf#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-mainnet](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-mainnet) | -| Ethereum-goerli | [0x26ebbA649FAa7b56FDB8DE9Ea17aF3504B76BFA0](https://goerli.etherscan.io/address/0x26ebbA649FAa7b56FDB8DE9Ea17aF3504B76BFA0#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli) | -| Arbitrum-one | [0xfae79e8cb8fbac2408e5baf89262bd92b6ca464a](https://arbiscan.io/address/0xfae79e8cb8fbac2408e5baf89262bd92b6ca464a#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arb-one](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arb-one) | -| Arbitrum-goerli | [0x50c2d70a41ecefe4cc54a331457ea204ecf97292](https://goerli.arbiscan.io/address/0x50c2d70a41ecefe4cc54a331457ea204ecf97292#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arbitrum-go](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arbitrum-go) | +| Network | Registry Contract | Subgraph API | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Ethereum-mainnet | [0x89f97698d6006f25570cd2e31737d3d22aedcbcf](https://etherscan.io/address/0x89f97698d6006f25570cd2e31737d3d22aedcbcf#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-mainnet](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-mainnet) | +| Ethereum-goerli | [0x26ebbA649FAa7b56FDB8DE9Ea17aF3504B76BFA0](https://goerli.etherscan.io/address/0x26ebbA649FAa7b56FDB8DE9Ea17aF3504B76BFA0#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli) | +| Arbitrum-one | [0xfae79e8cb8fbac2408e5baf89262bd92b6ca464a](https://arbiscan.io/address/0xfae79e8cb8fbac2408e5baf89262bd92b6ca464a#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arb-one](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arb-one) | +| Arbitrum-goerli | [0x50c2d70a41ecefe4cc54a331457ea204ecf97292](https://goerli.arbiscan.io/address/0x50c2d70a41ecefe4cc54a331457ea204ecf97292#writeProxyContract) | [https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arbitrum-go](https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arbitrum-go) | :::info Each Graphcast ID can be associated with a single Indexer. To revoke a Graphcast ID for your Indexer, call `setGraphcastIDFor(indexer_address, graphcast_id)` with a Graphcast ID of `0x0` using a registered Indexer Operator Account. @@ -55,14 +55,13 @@ Each Graphcast ID can be associated with a single Indexer. To revoke a Graphcast ### Subgraph APIs -Here we list out the APIs the team supports actively. For network subgraph endpoint, We recommend you to expose your indexer-service's endpoint at `/network` queries with authentication. You can also index and serve registry subgraph but they are not currently deployed on the decentralized network. +Here we list out the APIs the team supports actively. For network subgraph endpoint, We recommend you to expose your indexer-service's endpoint at `/network` queries with authentication. You can also index and serve registry subgraph but they are not currently deployed on the decentralized network. Here are the endpoints available on the hosted service. -| Protocol Network | Graphcast Network | Registry Subgraph Endpoint | Network Subgraph Endpoint | -| -------------- | ----------------- | -------------------------- | ------------------------- | -| Ethereum Mainnet | `mainnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-mainnet` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet` | -| Goerli | `testnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli/network` | -| Arbitrum-One | `mainnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arb-one` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum` | -| Arbitrum-Goerli | `testnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arbitrum-go` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum-goerli` | - +| Protocol Network | Graphcast Network | Registry Subgraph Endpoint | Network Subgraph Endpoint | +| ---------------- | ----------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Ethereum Mainnet | `mainnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-mainnet` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet` | +| Goerli | `testnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-goerli` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli/network` | +| Arbitrum-One | `mainnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arb-one` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum` | +| Arbitrum-Goerli | `testnet` | `https://api.thegraph.com/subgraphs/name/hopeyen/graphcast-registry-arbitrum-go` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum-goerli` |