diff --git a/src/config.rs b/src/config.rs index 40b79a30..bfbff9e5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,15 +10,12 @@ use anyhow::Context; use ipnetwork::IpNetwork; use ordered_float::NotNan; use semver::Version; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; -use thegraph_core::{Address, DeploymentId}; +use thegraph_core::{Address, BlockNumber, DeploymentId}; use url::Url; -use crate::{ - auth::APIKey, indexers::public_poi::ProofOfIndexingInfo, - network::subgraph_client::TrustedIndexer, -}; +use crate::{auth::APIKey, network::subgraph_client::TrustedIndexer}; /// The Graph Gateway configuration. #[serde_as] @@ -56,7 +53,7 @@ pub struct Config { pub payment_required: bool, /// POI blocklist #[serde(default)] - pub poi_blocklist: Vec, + pub poi_blocklist: Vec, /// public API port pub port_api: u16, /// private metrics port @@ -174,6 +171,17 @@ pub struct Receipts { pub verifier: Address, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct BlockedPoi { + pub public_poi: B256, + pub deployment: DeploymentId, + pub block_number: BlockNumber, + /// Example query (should be minimal to reproduce bad response) + pub query: Option, + /// Bad query response, from the above query executed on indexers with this blocked PoI + pub bad_query_response: Option, +} + /// Load the configuration from a JSON file. pub fn load_from_file(path: &Path) -> anyhow::Result { let config_content = std::fs::read_to_string(path)?; diff --git a/src/indexers/public_poi.rs b/src/indexers/public_poi.rs index a660d918..266d00ea 100644 --- a/src/indexers/public_poi.rs +++ b/src/indexers/public_poi.rs @@ -35,34 +35,6 @@ pub enum Error { EmptyResponse, } -#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Deserialize)] -pub struct ProofOfIndexingInfo { - /// Proof of indexing (POI). - pub proof_of_indexing: ProofOfIndexing, - /// POI deployment ID (the IPFS Hash in the Graph Network Subgraph). - pub deployment_id: DeploymentId, - /// POI block number. - pub block_number: BlockNumber, -} - -impl ProofOfIndexingInfo { - /// Get the POI metadata. - pub fn meta(&self) -> (DeploymentId, BlockNumber) { - (self.deployment_id, self.block_number) - } -} - -impl From<((DeploymentId, BlockNumber), ProofOfIndexing)> for ProofOfIndexingInfo { - fn from(value: ((DeploymentId, BlockNumber), ProofOfIndexing)) -> Self { - let ((deployment_id, block_number), proof_of_indexing) = value; - Self { - deployment_id, - block_number, - proof_of_indexing, - } - } -} - #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] struct PublicProofOfIndexingRequest { diff --git a/src/main.rs b/src/main.rs index 73d28ef5..981c07e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -120,7 +120,7 @@ async fn main() { conf.min_graph_node_version, conf.blocked_indexers, indexer_host_blocklist, - conf.poi_blocklist, + conf.poi_blocklist.clone(), ); let indexing_perf = IndexingPerformance::new(network.clone()); network.wait_until_ready().await; @@ -172,6 +172,8 @@ async fn main() { reporter, }; + let poi_blocklist: &'static str = serde_json::to_string(&conf.poi_blocklist).unwrap().leak(); + // Host metrics on a separate server with a port that isn't open to public requests. tokio::spawn(async move { let router = Router::new().route("/metrics", routing::get(handle_metrics)); @@ -252,6 +254,10 @@ async fn main() { "/voucher", routing::post(vouchers::handle_voucher).with_state(legacy_signer), ) + .route( + "/poi_blocklist", + routing::get(move || async move { poi_blocklist }), + ) .nest("/api", api); let app_listener = TcpListener::bind(SocketAddr::new( diff --git a/src/network/indexer_indexing_poi_blocklist.rs b/src/network/indexer_indexing_poi_blocklist.rs index b57805f5..b2093792 100644 --- a/src/network/indexer_indexing_poi_blocklist.rs +++ b/src/network/indexer_indexing_poi_blocklist.rs @@ -11,28 +11,25 @@ use std::collections::{HashMap, HashSet}; use thegraph_core::{BlockNumber, DeploymentId, ProofOfIndexing}; -use crate::indexers::public_poi::ProofOfIndexingInfo; +use crate::config::BlockedPoi; /// A blocklist based on the Proof of Indexing (POI) of indexers. #[derive(Default)] pub struct PoiBlocklist { - blocklist: HashMap>, + blocklist: HashMap>, } impl PoiBlocklist { - pub fn new(conf: Vec) -> Self { - // Group the blocked POI info by deployment ID - let mut conf_map = HashMap::new(); + pub fn new(conf: Vec) -> Self { + let mut blocklist: HashMap> = + Default::default(); for info in conf.into_iter() { - conf_map - .entry(info.deployment_id) - .or_insert_with(HashSet::new) - .insert(info); - } - - Self { - blocklist: conf_map, + blocklist + .entry(info.deployment) + .or_default() + .push((info.block_number, info.public_poi.into())); } + Self { blocklist } } pub fn is_empty(&self) -> bool { @@ -49,11 +46,11 @@ impl PoiBlocklist { ) -> Vec<(DeploymentId, BlockNumber)> { deployments .into_iter() - .flat_map(|deployment_id| { - self.blocklist - .get(deployment_id) - .into_iter() - .flat_map(|pois| pois.iter().map(|poi_info| poi_info.meta())) + .flat_map(|deployment| { + self.blocklist.get(deployment).into_iter().flat_map(|pois| { + pois.iter() + .map(|(block_number, _)| (*deployment, *block_number)) + }) }) .collect() } @@ -74,17 +71,13 @@ impl PoiBlocklist { /// Check if the POI is in the blocklist. fn check_poi( &self, - deployment_id: DeploymentId, + deployment: DeploymentId, block_number: BlockNumber, poi: ProofOfIndexing, ) -> bool { - match self.blocklist.get(&deployment_id) { + match self.blocklist.get(&deployment) { None => false, - Some(blocked_pois) => blocked_pois.iter().any(|blocked| { - blocked.deployment_id == deployment_id - && blocked.block_number == block_number - && blocked.proof_of_indexing == poi - }), + Some(blocked_pois) => blocked_pois.contains(&(block_number, poi)), } } } diff --git a/src/network/service.rs b/src/network/service.rs index 716e0670..9f0c977a 100644 --- a/src/network/service.rs +++ b/src/network/service.rs @@ -28,7 +28,7 @@ use super::{ subgraph_client::Client as SubgraphClient, ResolutionError, }; -use crate::{config::BlockedIndexer, indexers::public_poi::ProofOfIndexingInfo}; +use crate::config::{BlockedIndexer, BlockedPoi}; /// Subgraph resolution information returned by the [`NetworkService`]. pub struct ResolvedSubgraphInfo { @@ -177,7 +177,7 @@ pub fn spawn( min_graph_node_version: Version, indexer_blocklist: BTreeMap, indexer_host_blocklist: HashSet, - indexer_pois_blocklist: Vec, + poi_blocklist: Vec, ) -> NetworkService { let internal_state = InternalState { indexer_blocklist, @@ -189,9 +189,7 @@ pub fn spawn( min_graph_node_version, }, indexer_version_resolver: VersionResolver::new(http_client.clone(), Duration::from_secs(5)), - poi_blocklist: PoiBlocklist::new( - indexer_pois_blocklist.into_iter().map(Into::into).collect(), - ), + poi_blocklist: PoiBlocklist::new(poi_blocklist), poi_resolver: PoiResolver::new( http_client.clone(), Duration::from_secs(5),