From 92cb350cc9ebbd565aa1ffa5b8e9c5c0da64d8ce Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 19 Jun 2025 16:48:20 -0400 Subject: [PATCH 01/20] docs(client-query): rust-document run-indexer-queries --- src/client_query.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/client_query.rs b/src/client_query.rs index 281c69d7..d2e55327 100644 --- a/src/client_query.rs +++ b/src/client_query.rs @@ -167,7 +167,72 @@ async fn resolve_subgraph_info( } } -#[allow(clippy::too_many_arguments)] +/// Executes a GraphQL query across multiple indexers in the Graph Network. +/// +/// This is the core orchestration function that handles the complete query lifecycle: +/// indexer selection, concurrent query execution, TAP payment processing, and response aggregation. +/// +/// # Arguments +/// +/// * `ctx` - Gateway context containing shared services (indexer client, network state, etc.) +/// * `request_id` - Unique identifier for this request used in tracing and reporting +/// * `auth` - Authenticated user settings including API key and authorized subgraphs +/// * `start_time` - Query start timestamp for latency calculations +/// * `subgraph` - Resolved subgraph information including available indexers and deployments +/// * `budget` - Maximum query budget in GRT smallest units (1 GRT = 10^18 units) +/// * `client_request` - Original GraphQL query and variables from the client +/// * `client_response` - Channel to send the first successful response back to the client +/// +/// # Behavior +/// +/// The function implements a multi-round retry strategy: +/// +/// 1. **Query Parsing**: Validates GraphQL syntax and creates execution context +/// 2. **Chain State**: Resolves current chain head and block requirements from query +/// 3. **Candidate Selection**: Filters available indexers by performance, block range, and availability +/// 4. **Concurrent Execution**: Selects up to 3 indexers per round and queries them simultaneously +/// 5. **First Success Wins**: Returns the first successful response to the client immediately +/// 6. **Retry Logic**: If all indexers fail, removes failed candidates and retries with remaining indexers +/// 7. **Comprehensive Reporting**: Exports all request data to Kafka regardless of success/failure +/// +/// # Payment Processing +/// +/// Each indexer request includes a cryptographically signed TAP receipt: +/// - Dynamic fee calculation based on budget and indexer count +/// - EIP-712 signed receipt with allocation ID, timestamp, nonce, and fee amount +/// - Budget feedback to dynamic pricing controller +/// +/// # Error Handling +/// +/// - **Query Errors**: Malformed GraphQL returns `Error::BadQuery` +/// - **No Indexers**: All indexers filtered out returns `Error::NoIndexers` +/// - **All Failed**: All indexers failed returns `Error::BadIndexers` with detailed error map +/// - **Timeout**: 60-second timeout with best available error response +/// +/// # Performance Characteristics +/// +/// - **Latency**: Optimized for first successful response (typically 100-500ms) +/// - **Fault Tolerance**: Multiple indexer selection with automatic failover +/// - **Concurrency**: All selected indexers queried simultaneously +/// - **Monitoring**: Comprehensive metrics and tracing for observability +/// +/// # Side Effects +/// +/// - Updates indexer performance metrics based on response times and success rates +/// - Sends budget feedback for dynamic pricing adjustments +/// - Exports complete request data to Kafka topics for analytics +/// - Updates chain head information from indexer responses +/// +/// # Example Flow +/// +/// ```text +/// Client Query → Parse GraphQL → Select 3 Indexers → Concurrent Requests +/// ↓ +/// First Success ← Client Response ← TAP Receipts ← Indexer Responses +/// ↓ +/// Kafka Export ← Performance Updates ← Budget Feedback ← Error Tracking +/// ``` +/// async fn run_indexer_queries( ctx: Context, request_id: String, From d7ff2dc1bcb9ef348eb9aa59c748de89a09631b5 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 19 Jun 2025 17:10:56 -0400 Subject: [PATCH 02/20] build: import tap-graph with horizon support --- Cargo.lock | 101 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 2 +- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f49c5c8..d71e9c6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d1aecf3cab3d0e7383064ce488616434b4ade10d8904dff422e74203c712f" +checksum = "0cd691724d088287334932cdef99b79ecb89ea01fff33a12552093495fdc99d1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -70,9 +70,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c6ad411efe0f49e0e99b9c7d8749a1eb55f6dbf74a1bc6953ab285b02c4f67" +checksum = "74a694d8be621ee12b45ae23e7f18393b9a1e04f1ba47a0136767cb8c955f7f8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf397edad57b696501702d5887e4e14d7d0bbae9fbb6439e148d361f7254f45" +checksum = "1647d47f59288584cc3b40eff3e7dde6af8c88a2fca8fe02c22de7b9ab218ffa" dependencies = [ "alloy-consensus", "alloy-eips", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977b97d271159578afcb26e39e1ca5ce1a7f937697793d7d571b0166dd8b8225" +checksum = "8bea12b41db8e25614b48636609549d721c3888a34a9512a7677a1b665c2c08c" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749b8449e4daf7359bdf1dabdba6ce424ff8b1bdc23bdb795661b2e991a08d87" +checksum = "715ae25d525c567481ba2fc97000415624836d516958b9c3f189f1e267d1d90a" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -217,9 +217,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fcbae2107f3f2df2b02bb7d9e81e8aa730ae371ca9dd7fd0c81c3d0cb78a452" +checksum = "696a83af273bfc512e02693bd4b5056c8c57898328bd0ce594013fb864de4dcf" dependencies = [ "alloy-eips", "alloy-primitives", @@ -243,9 +243,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc30b0e20fcd0843834ecad2a716661c7b9d5aca2486f8e96b93d5246eb83e06" +checksum = "42cc040744bc63111a70dd295984858e33a1ef1aff00c1c86be166b5c475ad23" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -258,9 +258,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaeb681024cf71f5ca14f3d812c0a8d8b49f13f7124713538e66d74d3bfe6aff" +checksum = "49f2d7663b088e11bed83ec9269347d7f56a5a80fb80c33a1ba1e60b470874e9" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a03ad273e1c55cc481889b4130e82860e33624e6969e9a08854e0f3ebe659295" +checksum = "35648c318b4649d2d141d1ed4f6e32c69f4959bdc2f6e44d53c0a333ed615a37" dependencies = [ "alloy-consensus", "alloy-eips", @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc164acf8c41c756e76c7aea3be8f0fb03f8a3ef90a33e3ddcea5d1614d8779" +checksum = "2bef5509f1f2ff26650eb4f8b715f31dacbf583cdadc70013824405c5940d9e5" dependencies = [ "alloy-chains", "alloy-consensus", @@ -386,9 +386,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c44d31bcb9afad460915fe1fba004a2af5a07a3376c307b9bdfeec3678c209" +checksum = "07bbbf4c34305ae1ff0a38e63e88690920d030ac197a71d009f36b3e48dc47c1" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -411,9 +411,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba2cf3d3c6ece87f1c6bb88324a997f28cf0ad7e98d5e0b6fa91c4003c30916" +checksum = "953b106defef74307b1379611ae9180e48d6b1ab8122d9a6fb8d6b7de1392cf9" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5b22062142ce3b2ed3374337d4b343437e5de6959397f55d2c9fe2c2ce0162" +checksum = "ed1e99233cff99aff94fe29cea9e9dd6014c85eeea7890ea4f0e4eada9957ceb" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -434,9 +434,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391e59f81bacbffc7bddd2da3a26d6eec0e2058e9237c279e9b1052bdf21b49e" +checksum = "c8300d59b0126876a1914102c588f9a4792eb4c754d483a954dc29904ddf79d6" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -454,9 +454,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea08bc854235d4dff08fd57df8033285c11b8d7548b20c6da218194e7e6035f" +checksum = "8070bc2af2d48969e3aa709ea3ebf1f8316176b91c2132efe33d113f74383a9e" dependencies = [ "alloy-primitives", "serde", @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb3759f85ef5f010a874d9ebd5ee6ce01cac65211510863124e0ebac6552db0" +checksum = "3f19a87067de976c8c8a0526d6d70de612bbd08bb8051a297b9a12084a9a4ae1" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -482,9 +482,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7942b850ec7be43de89b2680321d7921b7620b25be53b9981aae6fb29daa9e97" +checksum = "cf2abab2db4b0cdf94fa75c553470445f85c5532e612eea720c3f1ac6940f280" dependencies = [ "alloy-consensus", "alloy-network", @@ -500,9 +500,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74809e45053bd43d24338e618202ebea68d5660aa9632d77b0244faa2dcaa9d1" +checksum = "9f2a23310bc2532b55e43f9e3a262486c40ef78832274f49010195886a4de4ef" dependencies = [ "alloy-consensus", "alloy-network", @@ -518,9 +518,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c7e67367bc2b1d5790236448d2402865a4f0bc2b53cfda06d71b7ba3dbdffd" +checksum = "05c1f77fef0e1861f2eb08d1a114bd0870d8939f0055344dac825cefa393201e" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d95902d29e1290809e1c967a1e974145b44b78f6e3e12fc07a60c1225e3df0" +checksum = "6611724477b6b914202392c2f2d9dfd5c88e4eb8b1422ef90ac6cda3649b62a6" dependencies = [ "alloy-consensus", "alloy-network", @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdf4b7fc58ebb2605b2fc5a33dae5cf15527ea70476978351cc0db1c596ea93" +checksum = "e53f5b563b89faff55a3705e23972e40ee0c59634e356f1a79cc1e9c0be31744" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -650,9 +650,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4b0f3a9c28bcd3761504d9eb3578838d6d115c8959fc1ea05f59a3a8f691af" +checksum = "a6d25d5b547ce62a699957bcc3b3bd21d141d784dcc1b5b7dfc8a71cd36cd56d" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -681,9 +681,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79bf2869e66904b2148c809e7a75e23ca26f5d7b46663a149a1444fb98a69d1d" +checksum = "472e12600c46b766110edd8382b4804d70188870f064531ee8fd61a35ed18686" dependencies = [ "alloy-primitives", "darling", @@ -2568,9 +2568,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64", "bytes", @@ -3204,11 +3204,12 @@ dependencies = [ [[package]] name = "nybbles" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d51b0175c49668a033fe7cc69080110d9833b291566cdf332905f3ad9c68a0" +checksum = "675b3a54e5b12af997abc8b6638b0aee51a28caedab70d4967e0d5db3a3f1d06" dependencies = [ "alloy-rlp", + "cfg-if", "proptest", "ruint", "serde", diff --git a/Cargo.toml b/Cargo.toml index 4bc5a5b8..1cbf3b4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.116", features = ["raw_value"] } serde_with = "3.8.1" snmalloc-rs = "0.3" -tap_graph = "0.3.3" +tap_graph = { version = "0.3.4", features = ["v2"] } thegraph-core = { version = "0.15", features = [ "alloy-contract", "alloy-signer-local", From 17ee080158f1e97e0add1908099574c63bf74c10 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 26 Jun 2025 14:39:58 -0400 Subject: [PATCH 03/20] feat: support horizon Signed-off-by: Joseph Livesey --- Cargo.toml | 2 +- README.md | 11 +- src/client_query.rs | 31 +++- src/indexer_client.rs | 4 +- src/network/snapshot.rs | 6 +- src/receipts.rs | 393 ++++++++++++++++++++++++++++++++++++---- src/reports.rs | 247 +++++++++++++++++++++---- 7 files changed, 610 insertions(+), 84 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1cbf3b4e..4ac270bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ serde_json = { version = "1.0.116", features = ["raw_value"] } serde_with = "3.8.1" snmalloc-rs = "0.3" tap_graph = { version = "0.3.4", features = ["v2"] } -thegraph-core = { version = "0.15", features = [ +thegraph-core = { version = "0.15.1", features = [ "alloy-contract", "alloy-signer-local", "attestation", diff --git a/README.md b/README.md index 01f13dcc..8c48b378 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A gateway, in the Graph Network, is a client capable of managing relationships b data consumers and many indexers. A gateway is not necessary for a consumer to access the indexers participating in the Graph Network, though it does simplify consumer interactions. +**Note**: This is a horizon-ready gateway that generates TAP v2 (collection-based) receipts exclusively. It maintains backward compatibility for processing existing v1 (allocation-based) receipts. + A gateway is expected to be a reliable system, to compensate for indexers being relatively unreliable. Indexers may become unresponsive, malicious, or otherwise unsuitable for serving queries at any time without warning. It is the responsibility of the gateway to maintain the highest @@ -18,7 +20,7 @@ must have minimal impact on the primary responsibilities. ## indexer discovery For the gateway to route client queries to indexers, it must be able to associate subgraph and -subgraph deployment IDs with the indexers that have active allocations on the subgraph deployment +subgraph deployment IDs with the indexers that have active allocations or collections on the subgraph deployment being queried. The tree of subgraphs, subgraph deployments (versions), and allocated indexers is accessible via the network subgraph which indexes the Graph Network contracts. @@ -29,7 +31,7 @@ bootstrapping process for payments. When an indexer registers itself via the contract, it provides a URL to access its indexer-service. After the subgraph data is collected and organized, the gateway requests more information from each active indexer via the indexer-service. This includes software version information and, for each -allocation, the indexing status (progress on chain indexed by subgraph deployment) and cost models. +allocation or collection, the indexing status (progress on chain indexed by subgraph deployment) and cost models. The gateway may be configured to block public Proofs Of Indexing (POIs) that have been associated with bad query responses. In this case deployments with such POIs require an additional step in @@ -101,7 +103,7 @@ client query. Indexer fees are clamped to a maximum of the gateway's budget. For an overview of TAP see https://github.com/semiotic-ai/timeline-aggregation-protocol. -The gateway acts as a TAP sender, where each indexer request is sent with a TAP receipt. The gateway +The gateway acts as a TAP sender, where each indexer request is sent with a TAP v2 receipt. The gateway operator is expected to run 2 additional services: - [tap-aggregator](https://github.com/semiotic-ai/timeline-aggregation-protocol/tree/main/tap_aggregator): @@ -109,11 +111,12 @@ operator is expected to run 2 additional services: - [tap-escrow-manager](https://github.com/edgeandnode/tap-escrow-manager): maintains escrow balances for the TAP sender. This service requires data exported by the gateway into the "indexer requests" topic to calculate the value of outstanding receipts to each indexer. + For horizon compatibility, collection IDs are converted to addresses in the exported data. The gateway operator is also expected to manage at least 2 wallets: - sender: requires ETH for transaction gas and GRT to allocate into TAP escrow balances for paying indexers -- authorized signer: used by the gateway and tap-aggregator to sign receipts and RAVs +- authorized signer: used by the gateway and tap-aggregator to sign v2 receipts and RAVs ## operational notes diff --git a/src/client_query.rs b/src/client_query.rs index d2e55327..46cc821c 100644 --- a/src/client_query.rs +++ b/src/client_query.rs @@ -19,7 +19,7 @@ use prost::bytes::Buf; use rand::Rng as _; use serde::Deserialize; use serde_json::value::RawValue; -use thegraph_core::{AllocationId, DeploymentId, IndexerId, alloy::primitives::BlockNumber}; +use thegraph_core::{CollectionId, DeploymentId, IndexerId, alloy::primitives::BlockNumber}; use thegraph_headers::{HttpBuilderExt as _, graph_attestation::GraphAttestation}; use tokio::sync::mpsc; use tracing::{Instrument as _, info_span}; @@ -233,6 +233,7 @@ async fn resolve_subgraph_info( /// Kafka Export ← Performance Updates ← Budget Feedback ← Error Tracking /// ``` /// +#[allow(clippy::too_many_arguments)] async fn run_indexer_queries( ctx: Context, request_id: String, @@ -341,7 +342,7 @@ async fn run_indexer_queries( for &selection in &selections { let indexer = selection.id; let deployment = selection.data.deployment; - let largest_allocation = selection.data.largest_allocation; + let largest_collection = selection.data.largest_collection; let url = selection.data.url.clone(); let seconds_behind = selection.seconds_behind; let subgraph_chain = subgraph.chain.clone(); @@ -350,7 +351,13 @@ async fn run_indexer_queries( let min_fee = *(min_fee.0 * grt_per_usd * one_grt) / selections.len() as f64; let indexer_fee = selection.fee.as_f64() * budget as f64; let fee = indexer_fee.max(min_fee) as u128; - let receipt = match ctx.receipt_signer.create_receipt(largest_allocation, fee) { + let receipt = match ctx.receipt_signer.create_receipt( + largest_collection, + fee, + ctx.receipt_signer.payer_address(), + indexer.into_inner(), + indexer.into_inner(), + ) { Ok(receipt) => receipt, Err(err) => { tracing::error!(?indexer, %deployment, error=?err, "failed to create receipt"); @@ -477,7 +484,7 @@ async fn run_indexer_queries( tracing::info!( indexer = ?indexer_request.indexer, deployment = %indexer_request.deployment, - allocation = ?indexer_request.receipt.allocation(), + collection = ?indexer_request.receipt.collection(), url = indexer_request.url, result = ?indexer_request.result.as_ref().map(|_| ()), response_time_ms = indexer_request.response_time_ms, @@ -528,7 +535,7 @@ struct CandidateMetadata { deployment: DeploymentId, #[debug(with = std::fmt::Display::fmt)] url: Url, - largest_allocation: AllocationId, + largest_collection: CollectionId, } /// Given a list of indexings, build a list of candidates that are within the required block range @@ -636,7 +643,7 @@ fn build_candidates_list( data: CandidateMetadata { deployment, url: indexing.indexer.url.clone(), - largest_allocation: indexing.largest_allocation, + largest_collection: indexing.largest_collection, }, perf: perf.response, fee: Normalized::new(indexing.fee as f64 / budget as f64).unwrap_or(Normalized::ONE), @@ -746,8 +753,14 @@ pub async fn handle_indexer_query( let one_grt = NotNan::new(1e18).unwrap(); let fee = *(ctx.budgeter.query_fees_target.0 * grt_per_usd * one_grt) as u128; - let allocation = indexing.largest_allocation; - let receipt = match ctx.receipt_signer.create_receipt(allocation, fee) { + let collection = indexing.largest_collection; + let receipt = match ctx.receipt_signer.create_receipt( + collection, + fee, + ctx.receipt_signer.payer_address(), + indexer.into_inner(), + indexer.into_inner(), + ) { Ok(receipt) => receipt, Err(err) => { return Err(Error::Internal(anyhow!("failed to create receipt: {err}"))); @@ -809,7 +822,7 @@ pub async fn handle_indexer_query( tracing::info!( indexer = ?indexer_request.indexer, deployment = %indexer_request.deployment, - allocation = ?indexer_request.receipt.allocation(), + collection = ?indexer_request.receipt.collection(), url = indexer_request.url, result = ?indexer_request.result.as_ref().map(|_| ()), response_time_ms = indexer_request.response_time_ms, diff --git a/src/indexer_client.rs b/src/indexer_client.rs index b6d27944..f9226e88 100644 --- a/src/indexer_client.rs +++ b/src/indexer_client.rs @@ -116,11 +116,11 @@ impl IndexerClient { if let IndexerAuth::Paid(receipt, attestation_domain) = auth { match &payload.attestation { Some(attestation) => { - let allocation = receipt.allocation(); + let collection = receipt.collection(); if let Err(err) = attestation::verify( attestation_domain, attestation, - &allocation, + &collection.as_address(), query, &original_response, ) { diff --git a/src/network/snapshot.rs b/src/network/snapshot.rs index 3ee43ec8..ad02beda 100644 --- a/src/network/snapshot.rs +++ b/src/network/snapshot.rs @@ -6,7 +6,7 @@ use std::{ use custom_debug::CustomDebug; use thegraph_core::{ - AllocationId, DeploymentId, IndexerId, SubgraphId, alloy::primitives::BlockNumber, + CollectionId, DeploymentId, IndexerId, SubgraphId, alloy::primitives::BlockNumber, }; use url::Url; @@ -35,7 +35,7 @@ pub struct Indexing { /// /// This is, among all allocations associated with the indexer and deployment, the address /// with the largest amount of allocated tokens. - pub largest_allocation: AllocationId, + pub largest_collection: CollectionId, /// The indexer pub indexer: Arc, /// The indexing progress. @@ -364,7 +364,7 @@ fn construct_indexings_table_row( let indexing = Indexing { id: indexing_id, - largest_allocation: indexing_largest_allocation_addr, + largest_collection: indexing_largest_allocation_addr.into(), indexer: Arc::clone(indexer), progress: IndexingProgress { latest_block: indexing_progress.latest_block, diff --git a/src/receipts.rs b/src/receipts.rs index 63dc333a..eceb25c6 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -1,9 +1,9 @@ use std::time::SystemTime; use rand::RngCore; -use tap_graph::{Receipt as TapReceipt, SignedReceipt}; +use serde::Serialize; use thegraph_core::{ - AllocationId, + AllocationId, CollectionId, alloy::{ dyn_abi::Eip712Domain, primitives::{Address, U256}, @@ -11,47 +11,189 @@ use thegraph_core::{ }, }; -pub struct Receipt(SignedReceipt); +/// Abstraction over TAP receipts that supports both v1 and v2 formats +/// +/// Design: +/// - Gateway ONLY generates v2 receipts (collection-based) +/// - Gateway CAN process v1 receipts (allocation-based) for backward compatibility +/// - This horizon gateway focuses on v2 generation while maintaining v1 processing capability +#[derive(Debug, Clone)] +pub enum Receipt { + #[allow(dead_code)] // Used for processing existing v1 receipts + V1(V1Receipt), // For processing existing v1 receipts from indexers + V2(tap_graph::v2::SignedReceipt), // For generation and processing +} + +// Use tap_graph v1 types directly (should be available at root level) +pub use tap_graph::{Receipt as V1ReceiptMessage, SignedReceipt as V1Receipt}; impl Receipt { + /// Get the fee value from either receipt version pub fn value(&self) -> u128 { - self.0.message.value + match self { + Receipt::V1(receipt) => receipt.message.value, + Receipt::V2(receipt) => receipt.message.value, + } } - pub fn allocation(&self) -> Address { - self.0.message.allocation_id + /// Get the collection identifier + /// For v1: converts allocation_id to CollectionId + /// For v2: returns the collection_id directly + pub fn collection(&self) -> CollectionId { + match self { + Receipt::V1(receipt) => receipt.message.allocation_id.into(), + Receipt::V2(receipt) => receipt.message.collection_id.into(), + } + } + + /// Get the allocation ID + /// For v1: returns allocation_id directly + /// For v2: converts collection_id to AllocationId + #[allow(dead_code)] // Used when processing v1 receipts + pub fn allocation(&self) -> AllocationId { + match self { + Receipt::V1(receipt) => receipt.message.allocation_id.into(), + Receipt::V2(receipt) => CollectionId::from(receipt.message.collection_id).into(), + } } + /// Serialize the receipt to JSON string pub fn serialize(&self) -> String { - serde_json::to_string(&self.0).unwrap() + match self { + Receipt::V1(receipt) => serde_json::to_string(receipt).unwrap(), + Receipt::V2(receipt) => serde_json::to_string(receipt).unwrap(), + } + } + + /// Get receipt version for debugging/logging + #[allow(dead_code)] // Used for debugging when both receipt types are present + pub fn version(&self) -> &'static str { + match self { + Receipt::V1(_) => "v1", + Receipt::V2(_) => "v2", + } } + + /// Get payer address (only available for v2 receipts) + #[allow(dead_code)] // Used for v2 receipt processing + pub fn payer(&self) -> Option
{ + match self { + Receipt::V1(_) => None, // v1 receipts don't have explicit payer field + Receipt::V2(receipt) => Some(receipt.message.payer), + } + } + + /// Get data service address (only available for v2 receipts) + #[allow(dead_code)] // Used for v2 receipt processing + pub fn data_service(&self) -> Option
{ + match self { + Receipt::V1(_) => None, + Receipt::V2(receipt) => Some(receipt.message.data_service), + } + } + + /// Get service provider address (only available for v2 receipts) + #[allow(dead_code)] // Used for v2 receipt processing + pub fn service_provider(&self) -> Option
{ + match self { + Receipt::V1(_) => None, + Receipt::V2(receipt) => Some(receipt.message.service_provider), + } + } + + /// Check if this is a v1 receipt + #[allow(dead_code)] // Used when both receipt types are present + pub fn is_v1(&self) -> bool { + matches!(self, Receipt::V1(_)) + } + + /// Check if this is a v2 receipt + #[allow(dead_code)] // Used when both receipt types are present + pub fn is_v2(&self) -> bool { + matches!(self, Receipt::V2(_)) + } + + /// Parse a receipt from JSON string, attempting both v1 and v2 formats + #[allow(dead_code)] // Used for processing receipts from indexers + pub fn from_json(json: &str) -> anyhow::Result { + // Try v2 first (current generation format) + if let Ok(v2_receipt) = serde_json::from_str::(json) { + return Ok(Receipt::V2(v2_receipt)); + } + + // Try v1 format for backwards compatibility + if let Ok(v1_receipt) = serde_json::from_str::(json) { + return Ok(Receipt::V1(v1_receipt)); + } + + Err(anyhow::anyhow!( + "Failed to parse receipt as either v1 or v2 format" + )) + } +} + +impl Serialize for Receipt { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Receipt::V1(receipt) => receipt.serialize(serializer), + Receipt::V2(receipt) => receipt.serialize(serializer), + } + } +} + +/// Receipt version configuration +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ReceiptVersion { + #[allow(dead_code)] // Used when v1 support is needed + V1, // Read-only support for processing existing receipts + V2, // Current generation and processing version +} + +/// Configuration for receipt creation +#[derive(Debug, Clone)] +pub struct ReceiptConfig { + #[allow(dead_code)] // Used when multiple versions are configured + pub version: ReceiptVersion, + pub domain: Eip712Domain, } pub struct ReceiptSigner { signer: PrivateKeySigner, - domain: Eip712Domain, + v2_config: ReceiptConfig, } impl ReceiptSigner { pub fn new(signer: PrivateKeySigner, chain_id: U256, verifying_contract: Address) -> Self { + let v2_domain = Eip712Domain { + name: Some("TAP".into()), + version: Some("2".into()), + chain_id: Some(chain_id), + verifying_contract: Some(verifying_contract), + salt: None, + }; + Self { signer, - domain: Eip712Domain { - name: Some("TAP".into()), - version: Some("1".into()), - chain_id: Some(chain_id), - verifying_contract: Some(verifying_contract), - salt: None, + v2_config: ReceiptConfig { + version: ReceiptVersion::V2, + domain: v2_domain, }, } } - pub fn create_receipt(&self, allocation: AllocationId, fee: u128) -> anyhow::Result { - // Nonce generated with CSPRNG (ChaCha12), to avoid collision with receipts generated by - // other gateway processes. - // See https://docs.rs/rand/latest/rand/rngs/index.html#our-generators. + /// Create a v2 receipt (collection-based) - ONLY method for generating receipts + pub fn create_receipt( + &self, + collection: CollectionId, + fee: u128, + payer: Address, + data_service: Address, + service_provider: Address, + ) -> anyhow::Result { let nonce = rand::rng().next_u64(); - let timestamp_ns = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() @@ -59,16 +201,90 @@ impl ReceiptSigner { .try_into() .map_err(|_| anyhow::anyhow!("failed to convert timestamp to ns"))?; - let receipt = TapReceipt { - allocation_id: allocation.0.0.into(), + let receipt = tap_graph::v2::Receipt { + collection_id: collection.0.into(), + payer, + data_service, + service_provider, timestamp_ns, nonce, value: fee, }; - let signed = SignedReceipt::new(&self.domain, receipt, &self.signer) - .map_err(|e| anyhow::anyhow!("failed to sign receipt: {:?}", e))?; - Ok(Receipt(signed)) + let signed = + tap_graph::v2::SignedReceipt::new(&self.v2_config.domain, receipt, &self.signer) + .map_err(|e| anyhow::anyhow!("failed to sign v2 receipt: {:?}", e))?; + + Ok(Receipt::V2(signed)) + } + + pub fn payer_address(&self) -> Address { + self.signer.address() + } + + /// Get the generation version (always v2 - we only generate v2 receipts) + #[allow(dead_code)] // Used for debugging and configuration validation + pub fn generation_version(&self) -> ReceiptVersion { + ReceiptVersion::V2 + } + + /// Check if the gateway can process v1 receipts (read-only) + #[allow(dead_code)] // Used for capability checking + pub fn can_process_v1(&self) -> bool { + true // We can always process/deserialize v1 receipts + } + + /// Check if the gateway can generate v2 receipts + #[allow(dead_code)] // Used for capability checking + pub fn can_generate_v2(&self) -> bool { + true // We always generate v2 receipts + } +} + +/// Utility functions for receipt processing +impl Receipt { + /// Convert a v1 receipt to a format compatible with v2 processing + /// This is useful when we need to process existing v1 receipts in a v2-compatible way + #[allow(dead_code)] // Used when processing mixed receipt types + pub fn normalize_for_processing(&self) -> (CollectionId, u128) { + match self { + Receipt::V1(receipt) => { + // Convert allocation to collection format + let collection = receipt.message.allocation_id.into(); + (collection, receipt.message.value) + } + Receipt::V2(receipt) => { + let collection = receipt.message.collection_id.into(); + (collection, receipt.message.value) + } + } + } + + /// Create a v1 receipt for testing/processing purposes (not for generation) + #[allow(dead_code)] // Used for testing and processing existing v1 receipts + pub fn create_v1_for_processing( + allocation_id: AllocationId, + value: u128, + timestamp_ns: u64, + nonce: u64, + ) -> Self { + // Create the v1 receipt message + let receipt_message = V1ReceiptMessage { + allocation_id: allocation_id.0.into(), // Convert AllocationId to Address + value, + timestamp_ns, + nonce, + }; + + // Note: For testing purposes, we create an unsigned receipt + // In practice, v1 receipts from indexers would be properly signed + use thegraph_core::alloy::signers::Signature; + let signed_receipt = V1Receipt { + message: receipt_message, + signature: Signature::from_bytes_and_parity(&[0u8; 64], false), // Placeholder signature for testing + }; + + Receipt::V1(signed_receipt) } } @@ -77,29 +293,138 @@ mod tests { use thegraph_core::{ allocation_id, alloy::{primitives::address, signers::local::PrivateKeySigner}, + collection_id, }; use super::*; - #[test] - fn create_receipt() { - //* Given + fn create_test_signer() -> ReceiptSigner { let secret_key = PrivateKeySigner::from_slice(&[0xcd; 32]).expect("invalid secret key"); - let signer = ReceiptSigner::new( + ReceiptSigner::new( secret_key, 1.try_into().expect("invalid chain id"), address!("177b557b12f22bb17a9d73dcc994d978dd6f5f89"), - ); + ) + } + + #[test] + fn create_v2_receipt_only() { + let signer = create_test_signer(); + let collection = + collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); + let fee = 1000; + + let receipt = signer + .create_receipt( + collection, + fee, + address!("1111111111111111111111111111111111111111"), // payer + address!("2222222222222222222222222222222222222222"), // data_service + address!("3333333333333333333333333333333333333333"), // service_provider + ) + .expect("failed to create v2 receipt"); + + assert_eq!(receipt.value(), fee); + assert_eq!(receipt.version(), "v2"); + assert_eq!(receipt.collection(), collection); + assert!(receipt.is_v2()); + assert!(!receipt.is_v1()); + } + #[test] + fn process_v1_receipt() { let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); let fee = 1000; + let timestamp = 1234567890; + let nonce = 42; - //* When - let res = signer.create_receipt(allocation, fee); + let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, timestamp, nonce); - //* Then - let receipt = res.expect("failed to create tap receipt"); + assert_eq!(v1_receipt.value(), fee); + assert_eq!(v1_receipt.version(), "v1"); + assert_eq!(v1_receipt.allocation(), allocation); + assert!(v1_receipt.is_v1()); + assert!(!v1_receipt.is_v2()); - assert_eq!(receipt.value(), fee); + // v1 receipts don't have v2-specific fields + assert_eq!(v1_receipt.payer(), None); + assert_eq!(v1_receipt.data_service(), None); + assert_eq!(v1_receipt.service_provider(), None); + } + + #[test] + fn normalize_receipts_for_processing() { + let signer = create_test_signer(); + let collection = + collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); + let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); + let fee = 1000; + + // Create v2 receipt + let v2_receipt = signer + .create_receipt( + collection, + fee, + address!("1111111111111111111111111111111111111111"), + address!("2222222222222222222222222222222222222222"), + address!("3333333333333333333333333333333333333333"), + ) + .expect("failed to create v2 receipt"); + + // Create v1 receipt for processing + let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, 1234567890, 42); + + // Both should normalize to the same processing format + let (v2_collection, v2_value) = v2_receipt.normalize_for_processing(); + let (v1_collection, v1_value) = v1_receipt.normalize_for_processing(); + + assert_eq!(v2_value, fee); + assert_eq!(v1_value, fee); + assert_eq!(v2_collection, collection); + + // v1 allocation should convert to a collection (though different from v2) + let expected_collection: CollectionId = allocation.into(); + assert_eq!(v1_collection, expected_collection); + } + + #[test] + fn test_receipt_capabilities() { + let signer = create_test_signer(); + + assert!( + signer.can_process_v1(), + "Should be able to process v1 receipts" + ); + assert!( + signer.can_generate_v2(), + "Should be able to generate v2 receipts" + ); + assert_eq!(signer.generation_version(), ReceiptVersion::V2); + } + + #[test] + fn test_receipt_parsing() { + let signer = create_test_signer(); + let collection = + collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); + let fee = 1000; + + // Create and serialize a v2 receipt + let v2_receipt = signer + .create_receipt( + collection, + fee, + address!("1111111111111111111111111111111111111111"), + address!("2222222222222222222222222222222222222222"), + address!("3333333333333333333333333333333333333333"), + ) + .expect("failed to create v2 receipt"); + + let serialized = v2_receipt.serialize(); + + // Should be able to parse it back + let parsed = Receipt::from_json(&serialized).expect("failed to parse receipt"); + assert!(parsed.is_v2()); + assert_eq!(parsed.value(), fee); } } diff --git a/src/reports.rs b/src/reports.rs index e65620e6..5bf4a5d4 100644 --- a/src/reports.rs +++ b/src/reports.rs @@ -25,6 +25,8 @@ pub struct IndexerRequest { pub indexer: IndexerId, pub deployment: DeploymentId, pub url: String, + /// Receipt contains either v1 (allocation-based) or v2 (collection-based) receipt. + /// For reporting, collection IDs are converted to addresses for backward compatibility. pub receipt: Receipt, pub subgraph_chain: String, pub result: Result, @@ -108,32 +110,47 @@ impl Reporter { let indexer_queries = client_request .indexer_requests .iter() - .map(|indexer_request| IndexerQueryProtobuf { - indexer: indexer_request.indexer.to_vec(), - deployment: indexer_request.deployment.to_vec(), - allocation: indexer_request.receipt.allocation().to_vec(), - indexed_chain: indexer_request.subgraph_chain.clone(), - url: indexer_request.url.clone(), - fee_grt: indexer_request.receipt.value() as f64 * 1e-18, - response_time_ms: indexer_request.response_time_ms as u32, - seconds_behind: indexer_request.seconds_behind, - result: indexer_request - .result - .as_ref() - .map(|_| "success".to_string()) - .unwrap_or_else(|err| err.to_string()), - indexer_errors: indexer_request - .result - .as_ref() - .map(|r| { - r.errors - .iter() - .map(|err| err.as_str()) - .collect::>() - .join("; ") - }) - .unwrap_or_default(), - blocks_behind: indexer_request.blocks_behind, + .map(|indexer_request| { + let (allocation, collection) = if indexer_request.receipt.is_v1() { + ( + Some(indexer_request.receipt.allocation().as_ref().to_vec()), + None, + ) + } else { + ( + None, + Some(indexer_request.receipt.collection().as_ref().to_vec()), + ) + }; + + IndexerQueryProtobuf { + indexer: indexer_request.indexer.to_vec(), + deployment: indexer_request.deployment.to_vec(), + allocation, + collection, + indexed_chain: indexer_request.subgraph_chain.clone(), + url: indexer_request.url.clone(), + fee_grt: indexer_request.receipt.value() as f64 * 1e-18, + response_time_ms: indexer_request.response_time_ms as u32, + seconds_behind: indexer_request.seconds_behind, + result: indexer_request + .result + .as_ref() + .map(|_| "success".to_string()) + .unwrap_or_else(|err| err.to_string()), + indexer_errors: indexer_request + .result + .as_ref() + .map(|r| { + r.errors + .iter() + .map(|err| err.as_str()) + .collect::>() + .join("; ") + }) + .unwrap_or_default(), + blocks_behind: indexer_request.blocks_behind, + } }) .collect(); @@ -186,10 +203,16 @@ impl Reporter { .and_then(|r| Some((r.original_response, r.attestation?))) { const MAX_PAYLOAD_BYTES: usize = 100_000; + let allocation = if indexer_request.receipt.is_v1() { + indexer_request.receipt.allocation().as_ref().to_vec() // Direct allocation for v1 + } else { + indexer_request.receipt.collection().as_address().to_vec() // Collection as address for v2 + }; + AttestationProtobuf { request: Some(indexer_request.request).filter(|r| r.len() <= MAX_PAYLOAD_BYTES), response: Some(original_response).filter(|r| r.len() <= MAX_PAYLOAD_BYTES), - allocation: indexer_request.receipt.allocation().0.0.into(), + allocation, subgraph_deployment: attestation.deployment.0.into(), request_cid: attestation.request_cid.0.into(), response_cid: attestation.response_cid.0.into(), @@ -256,9 +279,12 @@ pub struct IndexerQueryProtobuf { /// 32 bytes #[prost(bytes, tag = "2")] deployment: Vec, - /// 20 bytes - #[prost(bytes, tag = "3")] - allocation: Vec, + /// 20 bytes - Allocation ID for v1 receipts + #[prost(bytes, optional, tag = "3")] + allocation: Option>, + /// 32 bytes - Collection ID for v2 receipts + #[prost(bytes, optional, tag = "12")] + collection: Option>, #[prost(string, tag = "4")] indexed_chain: String, #[prost(string, tag = "5")] @@ -283,7 +309,7 @@ pub struct AttestationProtobuf { request: Option, #[prost(string, optional, tag = "2")] response: Option, - /// 20 bytes + /// 20 bytes - Collection ID converted to address for backward compatibility #[prost(bytes, tag = "3")] allocation: Vec, /// 32 bytes @@ -299,3 +325,162 @@ pub struct AttestationProtobuf { #[prost(bytes, tag = "7")] signature: Vec, } + +#[cfg(test)] +mod tests { + use thegraph_core::{ + allocation_id, + alloy::{primitives::address, signers::local::PrivateKeySigner}, + collection_id, + }; + + use crate::receipts::{Receipt, ReceiptSigner}; + + fn create_test_signer() -> ReceiptSigner { + let secret_key = PrivateKeySigner::from_slice(&[0xcd; 32]).expect("invalid secret key"); + ReceiptSigner::new( + secret_key, + 1.try_into().expect("invalid chain id"), + address!("177b557b12f22bb17a9d73dcc994d978dd6f5f89"), + ) + } + + #[test] + fn test_protobuf_fields_v1_receipt() { + let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); + let fee = 1000; + let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, 1234567890, 42); + + // Test IndexerQueryProtobuf fields + let (allocation_field, collection_field) = if v1_receipt.is_v1() { + (Some(v1_receipt.allocation().as_ref().to_vec()), None) + } else { + (None, Some(v1_receipt.collection().as_ref().to_vec())) + }; + + assert!( + allocation_field.is_some(), + "v1 receipt should have allocation field" + ); + assert!( + collection_field.is_none(), + "v1 receipt should not have collection field" + ); + assert_eq!(allocation_field.unwrap(), allocation.as_ref().to_vec()); + } + + #[test] + fn test_protobuf_fields_v2_receipt() { + let signer = create_test_signer(); + let collection = + collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); + let fee = 1000; + + let v2_receipt = signer + .create_receipt( + collection, + fee, + address!("1111111111111111111111111111111111111111"), + address!("2222222222222222222222222222222222222222"), + address!("3333333333333333333333333333333333333333"), + ) + .expect("failed to create v2 receipt"); + + // Test IndexerQueryProtobuf fields + let (allocation_field, collection_field) = if v2_receipt.is_v1() { + (Some(v2_receipt.allocation().as_ref().to_vec()), None) + } else { + (None, Some(v2_receipt.collection().as_ref().to_vec())) + }; + + assert!( + allocation_field.is_none(), + "v2 receipt should not have allocation field" + ); + assert!( + collection_field.is_some(), + "v2 receipt should have collection field" + ); + assert_eq!(collection_field.unwrap(), collection.as_ref().to_vec()); + } + + /// Test that simulates tap-aggregator receipt processing + #[test] + fn test_tap_aggregator_v1_compatibility() { + let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); + let fee = 1000u128; + let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, 1234567890, 42); + + // Simulate IndexerQueryProtobuf creation (what gateway sends) + let (allocation_field, collection_field) = if v1_receipt.is_v1() { + (Some(v1_receipt.allocation().as_ref().to_vec()), None) + } else { + (None, Some(v1_receipt.collection().as_ref().to_vec())) + }; + + // Verify format matches tap-aggregator v1 expectations + assert!( + allocation_field.is_some(), + "v1 receipt must have allocation field" + ); + assert!( + collection_field.is_none(), + "v1 receipt must not have collection field" + ); + + let allocation_bytes = allocation_field.unwrap(); + assert_eq!(allocation_bytes.len(), 20, "allocation should be 20 bytes"); + assert_eq!(allocation_bytes, allocation.as_ref().to_vec()); + } + + /// Test that simulates tap-aggregator v2 receipt processing + #[test] + fn test_tap_aggregator_v2_compatibility() { + let signer = create_test_signer(); + let collection = + collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); + let fee = 1000u128; + + let v2_receipt = signer + .create_receipt( + collection, + fee, + address!("1111111111111111111111111111111111111111"), + address!("2222222222222222222222222222222222222222"), + address!("3333333333333333333333333333333333333333"), + ) + .expect("failed to create v2 receipt"); + + // Simulate IndexerQueryProtobuf creation (what gateway sends) + let (allocation_field, collection_field) = if v2_receipt.is_v1() { + (Some(v2_receipt.allocation().as_ref().to_vec()), None) + } else { + (None, Some(v2_receipt.collection().as_ref().to_vec())) + }; + + // Verify format matches tap-aggregator v2 expectations + assert!( + allocation_field.is_none(), + "v2 receipt must not have allocation field" + ); + assert!( + collection_field.is_some(), + "v2 receipt must have collection field" + ); + + let collection_bytes = collection_field.unwrap(); + assert_eq!(collection_bytes.len(), 32, "collection should be 32 bytes"); + assert_eq!(collection_bytes, collection.as_ref().to_vec()); + + // Also verify v2-specific fields are available + assert!(v2_receipt.payer().is_some(), "v2 receipt should have payer"); + assert!( + v2_receipt.data_service().is_some(), + "v2 receipt should have data_service" + ); + assert!( + v2_receipt.service_provider().is_some(), + "v2 receipt should have service_provider" + ); + } +} From 7d564929a4f8ca2cde107bf7b2d07d655505c040 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 12:48:33 -0400 Subject: [PATCH 04/20] chore: add debug logging to receipt generation Signed-off-by: Joseph Livesey --- src/client_query.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/client_query.rs b/src/client_query.rs index 46cc821c..48b7ab60 100644 --- a/src/client_query.rs +++ b/src/client_query.rs @@ -331,6 +331,10 @@ async fn run_indexer_queries( // all the available indexers in the `bad indexers` response. while !candidates.is_empty() && (start_time.elapsed() < Duration::from_secs(60)) { let selections: ArrayVec<_, SELECTION_LIMIT> = indexer_selection::select(&candidates); + tracing::debug!( + selections_count = selections.len(), + "indexer_selection returned selections" + ); if selections.is_empty() { // Candidates that would never be selected should be filtered out for improved errors. tracing::error!("no candidates selected"); @@ -339,6 +343,12 @@ async fn run_indexer_queries( let (tx, mut rx) = mpsc::channel(SELECTION_LIMIT); let min_fee = *ctx.budgeter.min_indexer_fees.borrow(); + tracing::debug!( + selections_count = selections.len(), + ?min_fee, + budget, + "about to process selections for receipt creation" + ); for &selection in &selections { let indexer = selection.id; let deployment = selection.data.deployment; @@ -351,6 +361,14 @@ async fn run_indexer_queries( let min_fee = *(min_fee.0 * grt_per_usd * one_grt) / selections.len() as f64; let indexer_fee = selection.fee.as_f64() * budget as f64; let fee = indexer_fee.max(min_fee) as u128; + tracing::debug!( + ?indexer, + ?largest_collection, + fee, + indexer_fee, + min_fee, + "processing indexer for receipt creation" + ); let receipt = match ctx.receipt_signer.create_receipt( largest_collection, fee, @@ -358,7 +376,15 @@ async fn run_indexer_queries( indexer.into_inner(), indexer.into_inner(), ) { - Ok(receipt) => receipt, + Ok(receipt) => { + tracing::debug!( + ?indexer, + fee, + receipt_value = receipt.value(), + "successfully created TAP receipt" + ); + receipt + } Err(err) => { tracing::error!(?indexer, %deployment, error=?err, "failed to create receipt"); continue; @@ -376,6 +402,11 @@ async fn run_indexer_queries( // URL checked: ref df8e647b-1e6e-422a-8846-dc9ee7e0dcc2 let deployment_url = url.join(&format!("subgraphs/id/{deployment}")).unwrap(); let auth = IndexerAuth::Paid(&receipt, ctx.attestation_domain); + tracing::debug!( + ?indexer, + receipt_value = receipt.value(), + "querying indexer with Paid auth" + ); let result = indexer_client .query_indexer(deployment_url, auth, &indexer_query) .in_current_span() From fdb8c842484b4a63c68df5542d2ba5f1e45e1f1f Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 14:46:57 -0400 Subject: [PATCH 05/20] chore: remove dead v1 code Signed-off-by: Joseph Livesey --- src/middleware/legacy_auth.rs | 12 +- src/receipts.rs | 294 +++------------------------------- src/reports.rs | 108 ++----------- 3 files changed, 39 insertions(+), 375 deletions(-) diff --git a/src/middleware/legacy_auth.rs b/src/middleware/legacy_auth.rs index 8ce6c22c..c76f34b0 100644 --- a/src/middleware/legacy_auth.rs +++ b/src/middleware/legacy_auth.rs @@ -22,12 +22,12 @@ pub async fn legacy_auth_adapter(mut request: Request, next: Next) -> Response>>().await { - if let Some(api_key) = path.get("api_key") { - parts - .headers - .typed_insert(Authorization::bearer(api_key).expect("valid api_key")); - } + if let Ok(Path(path)) = parts.extract::>>().await + && let Some(api_key) = path.get("api_key") + { + parts + .headers + .typed_insert(Authorization::bearer(api_key).expect("valid api_key")); } // reconstruct the request diff --git a/src/receipts.rs b/src/receipts.rs index eceb25c6..cbda54aa 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -3,7 +3,7 @@ use std::time::SystemTime; use rand::RngCore; use serde::Serialize; use thegraph_core::{ - AllocationId, CollectionId, + CollectionId, alloy::{ dyn_abi::Eip712Domain, primitives::{Address, U256}, @@ -11,124 +11,26 @@ use thegraph_core::{ }, }; -/// Abstraction over TAP receipts that supports both v1 and v2 formats +/// TAP v2 receipts for the Horizon upgrade /// -/// Design: -/// - Gateway ONLY generates v2 receipts (collection-based) -/// - Gateway CAN process v1 receipts (allocation-based) for backward compatibility -/// - This horizon gateway focuses on v2 generation while maintaining v1 processing capability +/// This gateway only generates and processes v2 receipts (collection-based) #[derive(Debug, Clone)] -pub enum Receipt { - #[allow(dead_code)] // Used for processing existing v1 receipts - V1(V1Receipt), // For processing existing v1 receipts from indexers - V2(tap_graph::v2::SignedReceipt), // For generation and processing -} - -// Use tap_graph v1 types directly (should be available at root level) -pub use tap_graph::{Receipt as V1ReceiptMessage, SignedReceipt as V1Receipt}; +pub struct Receipt(pub tap_graph::v2::SignedReceipt); impl Receipt { - /// Get the fee value from either receipt version + /// Get the fee value from the receipt pub fn value(&self) -> u128 { - match self { - Receipt::V1(receipt) => receipt.message.value, - Receipt::V2(receipt) => receipt.message.value, - } + self.0.message.value } - /// Get the collection identifier - /// For v1: converts allocation_id to CollectionId - /// For v2: returns the collection_id directly + /// Get the collection identifier pub fn collection(&self) -> CollectionId { - match self { - Receipt::V1(receipt) => receipt.message.allocation_id.into(), - Receipt::V2(receipt) => receipt.message.collection_id.into(), - } - } - - /// Get the allocation ID - /// For v1: returns allocation_id directly - /// For v2: converts collection_id to AllocationId - #[allow(dead_code)] // Used when processing v1 receipts - pub fn allocation(&self) -> AllocationId { - match self { - Receipt::V1(receipt) => receipt.message.allocation_id.into(), - Receipt::V2(receipt) => CollectionId::from(receipt.message.collection_id).into(), - } + self.0.message.collection_id.into() } /// Serialize the receipt to JSON string pub fn serialize(&self) -> String { - match self { - Receipt::V1(receipt) => serde_json::to_string(receipt).unwrap(), - Receipt::V2(receipt) => serde_json::to_string(receipt).unwrap(), - } - } - - /// Get receipt version for debugging/logging - #[allow(dead_code)] // Used for debugging when both receipt types are present - pub fn version(&self) -> &'static str { - match self { - Receipt::V1(_) => "v1", - Receipt::V2(_) => "v2", - } - } - - /// Get payer address (only available for v2 receipts) - #[allow(dead_code)] // Used for v2 receipt processing - pub fn payer(&self) -> Option
{ - match self { - Receipt::V1(_) => None, // v1 receipts don't have explicit payer field - Receipt::V2(receipt) => Some(receipt.message.payer), - } - } - - /// Get data service address (only available for v2 receipts) - #[allow(dead_code)] // Used for v2 receipt processing - pub fn data_service(&self) -> Option
{ - match self { - Receipt::V1(_) => None, - Receipt::V2(receipt) => Some(receipt.message.data_service), - } - } - - /// Get service provider address (only available for v2 receipts) - #[allow(dead_code)] // Used for v2 receipt processing - pub fn service_provider(&self) -> Option
{ - match self { - Receipt::V1(_) => None, - Receipt::V2(receipt) => Some(receipt.message.service_provider), - } - } - - /// Check if this is a v1 receipt - #[allow(dead_code)] // Used when both receipt types are present - pub fn is_v1(&self) -> bool { - matches!(self, Receipt::V1(_)) - } - - /// Check if this is a v2 receipt - #[allow(dead_code)] // Used when both receipt types are present - pub fn is_v2(&self) -> bool { - matches!(self, Receipt::V2(_)) - } - - /// Parse a receipt from JSON string, attempting both v1 and v2 formats - #[allow(dead_code)] // Used for processing receipts from indexers - pub fn from_json(json: &str) -> anyhow::Result { - // Try v2 first (current generation format) - if let Ok(v2_receipt) = serde_json::from_str::(json) { - return Ok(Receipt::V2(v2_receipt)); - } - - // Try v1 format for backwards compatibility - if let Ok(v1_receipt) = serde_json::from_str::(json) { - return Ok(Receipt::V1(v1_receipt)); - } - - Err(anyhow::anyhow!( - "Failed to parse receipt as either v1 or v2 format" - )) + serde_json::to_string(&self.0).unwrap() } } @@ -137,26 +39,13 @@ impl Serialize for Receipt { where S: serde::Serializer, { - match self { - Receipt::V1(receipt) => receipt.serialize(serializer), - Receipt::V2(receipt) => receipt.serialize(serializer), - } + self.0.serialize(serializer) } } -/// Receipt version configuration -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ReceiptVersion { - #[allow(dead_code)] // Used when v1 support is needed - V1, // Read-only support for processing existing receipts - V2, // Current generation and processing version -} - /// Configuration for receipt creation #[derive(Debug, Clone)] pub struct ReceiptConfig { - #[allow(dead_code)] // Used when multiple versions are configured - pub version: ReceiptVersion, pub domain: Eip712Domain, } @@ -177,10 +66,7 @@ impl ReceiptSigner { Self { signer, - v2_config: ReceiptConfig { - version: ReceiptVersion::V2, - domain: v2_domain, - }, + v2_config: ReceiptConfig { domain: v2_domain }, } } @@ -215,83 +101,17 @@ impl ReceiptSigner { tap_graph::v2::SignedReceipt::new(&self.v2_config.domain, receipt, &self.signer) .map_err(|e| anyhow::anyhow!("failed to sign v2 receipt: {:?}", e))?; - Ok(Receipt::V2(signed)) + Ok(Receipt(signed)) } pub fn payer_address(&self) -> Address { self.signer.address() } - - /// Get the generation version (always v2 - we only generate v2 receipts) - #[allow(dead_code)] // Used for debugging and configuration validation - pub fn generation_version(&self) -> ReceiptVersion { - ReceiptVersion::V2 - } - - /// Check if the gateway can process v1 receipts (read-only) - #[allow(dead_code)] // Used for capability checking - pub fn can_process_v1(&self) -> bool { - true // We can always process/deserialize v1 receipts - } - - /// Check if the gateway can generate v2 receipts - #[allow(dead_code)] // Used for capability checking - pub fn can_generate_v2(&self) -> bool { - true // We always generate v2 receipts - } -} - -/// Utility functions for receipt processing -impl Receipt { - /// Convert a v1 receipt to a format compatible with v2 processing - /// This is useful when we need to process existing v1 receipts in a v2-compatible way - #[allow(dead_code)] // Used when processing mixed receipt types - pub fn normalize_for_processing(&self) -> (CollectionId, u128) { - match self { - Receipt::V1(receipt) => { - // Convert allocation to collection format - let collection = receipt.message.allocation_id.into(); - (collection, receipt.message.value) - } - Receipt::V2(receipt) => { - let collection = receipt.message.collection_id.into(); - (collection, receipt.message.value) - } - } - } - - /// Create a v1 receipt for testing/processing purposes (not for generation) - #[allow(dead_code)] // Used for testing and processing existing v1 receipts - pub fn create_v1_for_processing( - allocation_id: AllocationId, - value: u128, - timestamp_ns: u64, - nonce: u64, - ) -> Self { - // Create the v1 receipt message - let receipt_message = V1ReceiptMessage { - allocation_id: allocation_id.0.into(), // Convert AllocationId to Address - value, - timestamp_ns, - nonce, - }; - - // Note: For testing purposes, we create an unsigned receipt - // In practice, v1 receipts from indexers would be properly signed - use thegraph_core::alloy::signers::Signature; - let signed_receipt = V1Receipt { - message: receipt_message, - signature: Signature::from_bytes_and_parity(&[0u8; 64], false), // Placeholder signature for testing - }; - - Receipt::V1(signed_receipt) - } } #[cfg(test)] mod tests { use thegraph_core::{ - allocation_id, alloy::{primitives::address, signers::local::PrivateKeySigner}, collection_id, }; @@ -308,7 +128,7 @@ mod tests { } #[test] - fn create_v2_receipt_only() { + fn create_v2_receipt() { let signer = create_test_signer(); let collection = collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); @@ -325,92 +145,17 @@ mod tests { .expect("failed to create v2 receipt"); assert_eq!(receipt.value(), fee); - assert_eq!(receipt.version(), "v2"); assert_eq!(receipt.collection(), collection); - assert!(receipt.is_v2()); - assert!(!receipt.is_v1()); - } - - #[test] - fn process_v1_receipt() { - let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); - let fee = 1000; - let timestamp = 1234567890; - let nonce = 42; - - let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, timestamp, nonce); - - assert_eq!(v1_receipt.value(), fee); - assert_eq!(v1_receipt.version(), "v1"); - assert_eq!(v1_receipt.allocation(), allocation); - assert!(v1_receipt.is_v1()); - assert!(!v1_receipt.is_v2()); - - // v1 receipts don't have v2-specific fields - assert_eq!(v1_receipt.payer(), None); - assert_eq!(v1_receipt.data_service(), None); - assert_eq!(v1_receipt.service_provider(), None); } #[test] - fn normalize_receipts_for_processing() { + fn test_receipt_serialization() { let signer = create_test_signer(); let collection = collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); - let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); let fee = 1000; - // Create v2 receipt - let v2_receipt = signer - .create_receipt( - collection, - fee, - address!("1111111111111111111111111111111111111111"), - address!("2222222222222222222222222222222222222222"), - address!("3333333333333333333333333333333333333333"), - ) - .expect("failed to create v2 receipt"); - - // Create v1 receipt for processing - let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, 1234567890, 42); - - // Both should normalize to the same processing format - let (v2_collection, v2_value) = v2_receipt.normalize_for_processing(); - let (v1_collection, v1_value) = v1_receipt.normalize_for_processing(); - - assert_eq!(v2_value, fee); - assert_eq!(v1_value, fee); - assert_eq!(v2_collection, collection); - - // v1 allocation should convert to a collection (though different from v2) - let expected_collection: CollectionId = allocation.into(); - assert_eq!(v1_collection, expected_collection); - } - - #[test] - fn test_receipt_capabilities() { - let signer = create_test_signer(); - - assert!( - signer.can_process_v1(), - "Should be able to process v1 receipts" - ); - assert!( - signer.can_generate_v2(), - "Should be able to generate v2 receipts" - ); - assert_eq!(signer.generation_version(), ReceiptVersion::V2); - } - - #[test] - fn test_receipt_parsing() { - let signer = create_test_signer(); - let collection = - collection_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2a00000000000000000000000"); - let fee = 1000; - - // Create and serialize a v2 receipt - let v2_receipt = signer + let receipt = signer .create_receipt( collection, fee, @@ -420,11 +165,8 @@ mod tests { ) .expect("failed to create v2 receipt"); - let serialized = v2_receipt.serialize(); - - // Should be able to parse it back - let parsed = Receipt::from_json(&serialized).expect("failed to parse receipt"); - assert!(parsed.is_v2()); - assert_eq!(parsed.value(), fee); + let serialized = receipt.serialize(); + assert!(!serialized.is_empty()); + assert_eq!(receipt.value(), fee); } } diff --git a/src/reports.rs b/src/reports.rs index 5bf4a5d4..c0bcf230 100644 --- a/src/reports.rs +++ b/src/reports.rs @@ -111,17 +111,11 @@ impl Reporter { .indexer_requests .iter() .map(|indexer_request| { - let (allocation, collection) = if indexer_request.receipt.is_v1() { - ( - Some(indexer_request.receipt.allocation().as_ref().to_vec()), - None, - ) - } else { - ( - None, - Some(indexer_request.receipt.collection().as_ref().to_vec()), - ) - }; + // Only v2 receipts exist in this system + let (allocation, collection) = ( + None, + Some(indexer_request.receipt.collection().as_ref().to_vec()), + ); IndexerQueryProtobuf { indexer: indexer_request.indexer.to_vec(), @@ -203,11 +197,8 @@ impl Reporter { .and_then(|r| Some((r.original_response, r.attestation?))) { const MAX_PAYLOAD_BYTES: usize = 100_000; - let allocation = if indexer_request.receipt.is_v1() { - indexer_request.receipt.allocation().as_ref().to_vec() // Direct allocation for v1 - } else { - indexer_request.receipt.collection().as_address().to_vec() // Collection as address for v2 - }; + // Only v2 receipts exist - use collection as address + let allocation = indexer_request.receipt.collection().as_address().to_vec(); AttestationProtobuf { request: Some(indexer_request.request).filter(|r| r.len() <= MAX_PAYLOAD_BYTES), @@ -329,12 +320,11 @@ pub struct AttestationProtobuf { #[cfg(test)] mod tests { use thegraph_core::{ - allocation_id, alloy::{primitives::address, signers::local::PrivateKeySigner}, collection_id, }; - use crate::receipts::{Receipt, ReceiptSigner}; + use crate::receipts::ReceiptSigner; fn create_test_signer() -> ReceiptSigner { let secret_key = PrivateKeySigner::from_slice(&[0xcd; 32]).expect("invalid secret key"); @@ -345,30 +335,6 @@ mod tests { ) } - #[test] - fn test_protobuf_fields_v1_receipt() { - let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); - let fee = 1000; - let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, 1234567890, 42); - - // Test IndexerQueryProtobuf fields - let (allocation_field, collection_field) = if v1_receipt.is_v1() { - (Some(v1_receipt.allocation().as_ref().to_vec()), None) - } else { - (None, Some(v1_receipt.collection().as_ref().to_vec())) - }; - - assert!( - allocation_field.is_some(), - "v1 receipt should have allocation field" - ); - assert!( - collection_field.is_none(), - "v1 receipt should not have collection field" - ); - assert_eq!(allocation_field.unwrap(), allocation.as_ref().to_vec()); - } - #[test] fn test_protobuf_fields_v2_receipt() { let signer = create_test_signer(); @@ -386,12 +352,9 @@ mod tests { ) .expect("failed to create v2 receipt"); - // Test IndexerQueryProtobuf fields - let (allocation_field, collection_field) = if v2_receipt.is_v1() { - (Some(v2_receipt.allocation().as_ref().to_vec()), None) - } else { - (None, Some(v2_receipt.collection().as_ref().to_vec())) - }; + // Test IndexerQueryProtobuf fields - only v2 receipts exist + let (allocation_field, collection_field): (Option>, Option>) = + (None, Some(v2_receipt.collection().as_ref().to_vec())); assert!( allocation_field.is_none(), @@ -404,35 +367,6 @@ mod tests { assert_eq!(collection_field.unwrap(), collection.as_ref().to_vec()); } - /// Test that simulates tap-aggregator receipt processing - #[test] - fn test_tap_aggregator_v1_compatibility() { - let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); - let fee = 1000u128; - let v1_receipt = Receipt::create_v1_for_processing(allocation, fee, 1234567890, 42); - - // Simulate IndexerQueryProtobuf creation (what gateway sends) - let (allocation_field, collection_field) = if v1_receipt.is_v1() { - (Some(v1_receipt.allocation().as_ref().to_vec()), None) - } else { - (None, Some(v1_receipt.collection().as_ref().to_vec())) - }; - - // Verify format matches tap-aggregator v1 expectations - assert!( - allocation_field.is_some(), - "v1 receipt must have allocation field" - ); - assert!( - collection_field.is_none(), - "v1 receipt must not have collection field" - ); - - let allocation_bytes = allocation_field.unwrap(); - assert_eq!(allocation_bytes.len(), 20, "allocation should be 20 bytes"); - assert_eq!(allocation_bytes, allocation.as_ref().to_vec()); - } - /// Test that simulates tap-aggregator v2 receipt processing #[test] fn test_tap_aggregator_v2_compatibility() { @@ -451,12 +385,9 @@ mod tests { ) .expect("failed to create v2 receipt"); - // Simulate IndexerQueryProtobuf creation (what gateway sends) - let (allocation_field, collection_field) = if v2_receipt.is_v1() { - (Some(v2_receipt.allocation().as_ref().to_vec()), None) - } else { - (None, Some(v2_receipt.collection().as_ref().to_vec())) - }; + // Simulate IndexerQueryProtobuf creation (what gateway sends) - only v2 receipts + let (allocation_field, collection_field): (Option>, Option>) = + (None, Some(v2_receipt.collection().as_ref().to_vec())); // Verify format matches tap-aggregator v2 expectations assert!( @@ -472,15 +403,6 @@ mod tests { assert_eq!(collection_bytes.len(), 32, "collection should be 32 bytes"); assert_eq!(collection_bytes, collection.as_ref().to_vec()); - // Also verify v2-specific fields are available - assert!(v2_receipt.payer().is_some(), "v2 receipt should have payer"); - assert!( - v2_receipt.data_service().is_some(), - "v2 receipt should have data_service" - ); - assert!( - v2_receipt.service_provider().is_some(), - "v2 receipt should have service_provider" - ); + // v2 receipt verified by successful creation and collection extraction } } From 815e4d02fc0fc2e2faa990ea093e9985ead56595 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 15:50:39 -0400 Subject: [PATCH 06/20] chore: fix query routing Signed-off-by: Joseph Livesey --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 49a9e498..6ea5752e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,7 +46,7 @@ pub struct Config { /// Minimum indexer-service version that will receive queries #[serde_as(as = "DisplayFromStr")] pub min_indexer_version: Version, - /// Indexers used to query the network subgraph + /// Trusted indexers that can serve the network subgraph for free pub trusted_indexers: Vec, /// Check payment state of client (disable for testnets) pub payment_required: bool, From ce3d3f391fbbaa2e29c7dc33a18b0b633047ed88 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 17:12:32 -0400 Subject: [PATCH 07/20] chore: implement horizon subgraph queries Signed-off-by: Joseph Livesey --- src/network/indexer_processing.rs | 16 ++++---- src/network/pre_processing.rs | 66 ++++++++++++++++++++----------- src/network/snapshot.rs | 4 +- src/network/subgraph_client.rs | 36 ++++++++++++----- 4 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/network/indexer_processing.rs b/src/network/indexer_processing.rs index 700e3d6f..3be0a9f7 100644 --- a/src/network/indexer_processing.rs +++ b/src/network/indexer_processing.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use custom_debug::CustomDebug; -use thegraph_core::{AllocationId, DeploymentId, IndexerId, alloy::primitives::BlockNumber}; +use thegraph_core::{CollectionId, DeploymentId, IndexerId, alloy::primitives::BlockNumber}; use tracing::Instrument; use url::Url; @@ -52,8 +52,8 @@ pub struct IndexerInfo { /// This is not the final representation of the indexer's indexing information. #[derive(Clone, Debug, Eq, PartialEq)] pub struct IndexingRawInfo { - /// The largest allocation. - pub largest_allocation: AllocationId, + /// The largest collection (V2 Horizon). + pub largest_collection: CollectionId, } /// Internal representation of the fetched indexer's indexing information. @@ -62,8 +62,8 @@ pub struct IndexingRawInfo { /// information: unresolved, partially resolved (with indexing progress) and completely resolved. #[derive(Debug)] pub struct IndexingInfo { - /// The largest allocation. - pub largest_allocation: AllocationId, + /// The largest collection (V2 Horizon). + pub largest_collection: CollectionId, /// The indexing progress information /// @@ -77,7 +77,7 @@ pub struct IndexingInfo { impl From for IndexingInfo<(), ()> { fn from(raw: IndexingRawInfo) -> Self { Self { - largest_allocation: raw.largest_allocation, + largest_collection: raw.largest_collection, progress: (), fee: (), } @@ -92,7 +92,7 @@ impl IndexingInfo<(), ()> { progress: IndexingProgress, ) -> IndexingInfo { IndexingInfo { - largest_allocation: self.largest_allocation, + largest_collection: self.largest_collection, progress, fee: self.fee, } @@ -104,7 +104,7 @@ impl IndexingInfo { /// state by adding the cost model to the indexing information. fn with_fee(self, fee: u128) -> IndexingInfo { IndexingInfo { - largest_allocation: self.largest_allocation, + largest_collection: self.largest_collection, progress: self.progress, fee, } diff --git a/src/network/pre_processing.rs b/src/network/pre_processing.rs index dc7b64a3..c9bdb57a 100644 --- a/src/network/pre_processing.rs +++ b/src/network/pre_processing.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, hash_map::Entry}; use anyhow::{anyhow, ensure}; -use thegraph_core::{AllocationId, DeploymentId, IndexerId, SubgraphId}; +use thegraph_core::{CollectionId, DeploymentId, IndexerId, SubgraphId}; use url::Url; use crate::network::{ @@ -16,9 +16,9 @@ use crate::network::{ pub fn into_internal_indexers_raw_info<'a>( data: impl Iterator, ) -> HashMap { - let mut indexer_indexing_largest_allocation: HashMap< + let mut indexer_indexing_largest_collection: HashMap< (IndexerId, DeploymentId), - (AllocationId, u128), + (CollectionId, u128), > = HashMap::new(); data.flat_map(|subgraph| { @@ -28,14 +28,14 @@ pub fn into_internal_indexers_raw_info<'a>( .map(|version| (&subgraph.id, version)) }) .fold(HashMap::new(), |mut acc, (subgraph_id, version)| { - for allocation in &version.subgraph_deployment.allocations { - let indexer_id = allocation.indexer.id; + for payments_escrow in &version.subgraph_deployment.payments_escrows { + let indexer_id = payments_escrow.indexer.id; let deployment_id = version.subgraph_deployment.id; // If the indexer info is not present, insert it if it is valid let indexer = match acc.entry(indexer_id) { Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => match try_into_indexer_raw_info(&allocation.indexer) { + Entry::Vacant(entry) => match try_into_indexer_raw_info(&payments_escrow.indexer) { Ok(info) => entry.insert(info), Err(err) => { // Log the error and skip the indexer @@ -43,7 +43,7 @@ pub fn into_internal_indexers_raw_info<'a>( subgraph_id=%subgraph_id, version=%version.version, deployment_id=%deployment_id, - allocation_id=%allocation.id, + payments_escrow_id=%payments_escrow.id, indexer_id=%indexer_id, "invalid indexer info: {err}" ); @@ -52,21 +52,41 @@ pub fn into_internal_indexers_raw_info<'a>( }, }; - // Update the indexer's indexings largest allocations table - let indexing_largest_allocation = match indexer_indexing_largest_allocation + // Update the indexer's indexings largest collections table (V2 escrow-based) + // Find the largest collection from this payments escrow + let largest_collection_id = payments_escrow + .collections + .first() // Use first collection (V2 equivalent of largest allocation) + .map(|c| { + // Log collection status for monitoring + tracing::trace!(collection_id = %c.id, status = %c.status, "processing collection"); + c.id + }) + .unwrap_or_else(|| { + // Fallback: create a collection ID from payments escrow ID for compatibility + use thegraph_core::{CollectionId, alloy::primitives::FixedBytes}; + // This is a temporary compatibility layer - create a collection ID from the escrow ID + let mut bytes = [0u8; 32]; + let escrow_bytes = payments_escrow.id.as_bytes(); + let copy_len = escrow_bytes.len().min(32); + bytes[..copy_len].copy_from_slice(&escrow_bytes[..copy_len]); + CollectionId::new(FixedBytes::from(bytes)) + }); + + let indexing_largest_collection = match indexer_indexing_largest_collection .entry((indexer_id, deployment_id)) { Entry::Vacant(entry) => { - entry.insert((allocation.id, allocation.allocated_tokens)); - allocation.id + entry.insert((largest_collection_id, payments_escrow.balance)); + largest_collection_id } Entry::Occupied(entry) => { - let (largest_allocation_address, largest_allocation_amount) = entry.into_mut(); - if allocation.allocated_tokens > *largest_allocation_amount { - *largest_allocation_address = allocation.id; - *largest_allocation_amount = allocation.allocated_tokens; + let (largest_collection_id_ref, largest_collection_amount) = entry.into_mut(); + if payments_escrow.balance > *largest_collection_amount { + *largest_collection_id_ref = largest_collection_id; + *largest_collection_amount = payments_escrow.balance; } - *largest_allocation_address + *largest_collection_id_ref } }; @@ -75,10 +95,10 @@ pub fn into_internal_indexers_raw_info<'a>( .indexings .entry(deployment_id) .or_insert(IndexingRawInfo { - largest_allocation: allocation.id, + largest_collection: largest_collection_id, }); - indexing.largest_allocation = indexing_largest_allocation; + indexing.largest_collection = indexing_largest_collection; } acc @@ -136,9 +156,9 @@ pub fn into_internal_deployments_raw_info<'a>( .subgraphs .extend(deployment_raw_info.subgraphs.iter().cloned()); - // Merge the associated allocations + // Merge the associated indexer allocations (derived from V2 payments escrows) deployment - .allocations + .allocations // Field name preserved for compatibility with existing processing pipeline .extend(deployment_raw_info.allocations.iter().cloned()); acc @@ -180,10 +200,10 @@ fn into_subgraph_version_raw_info( let deployment = version.subgraph_deployment; let deployment_allocations = deployment - .allocations + .payments_escrows .into_iter() - .map(|allocation| AllocationInfo { - indexer: allocation.indexer.id, + .map(|payments_escrow| AllocationInfo { + indexer: payments_escrow.indexer.id, }) .collect::>(); diff --git a/src/network/snapshot.rs b/src/network/snapshot.rs index ad02beda..89c23a12 100644 --- a/src/network/snapshot.rs +++ b/src/network/snapshot.rs @@ -358,13 +358,13 @@ fn construct_indexings_table_row( }; // Construct the indexing table row - let indexing_largest_allocation_addr = indexing_info.largest_allocation; + let indexing_largest_collection_id = indexing_info.largest_collection; let indexing_progress = indexing_info.progress.to_owned(); let fee = indexing_info.fee; let indexing = Indexing { id: indexing_id, - largest_collection: indexing_largest_allocation_addr.into(), + largest_collection: indexing_largest_collection_id, indexer: Arc::clone(indexer), progress: IndexingProgress { latest_block: indexing_progress.latest_block, diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index 1bd218e8..bf5c452c 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -34,7 +34,7 @@ pub mod types { use serde::Deserialize; use serde_with::serde_as; use thegraph_core::{ - AllocationId, DeploymentId, IndexerId, SubgraphId, alloy::primitives::BlockNumber, + CollectionId, DeploymentId, IndexerId, SubgraphId, alloy::primitives::BlockNumber, }; #[derive(Debug, Clone, Deserialize)] @@ -66,18 +66,26 @@ pub mod types { #[serde(rename = "ipfsHash")] pub id: DeploymentId, pub manifest: Option, - #[serde(rename = "indexerAllocations")] - pub allocations: Vec, + #[serde(rename = "paymentsEscrows")] + pub payments_escrows: Vec, } #[serde_as] #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct Allocation { - pub id: AllocationId, + pub struct PaymentsEscrow { + pub id: String, #[serde_as(as = "serde_with::DisplayFromStr")] - pub allocated_tokens: u128, + pub balance: u128, pub indexer: Indexer, + pub collections: Vec, + } + + #[derive(Debug, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Collection { + pub id: CollectionId, + pub status: String, } #[serde_as] @@ -172,18 +180,22 @@ impl Client { network startBlock } - indexerAllocations( + paymentsEscrows( first: 100 - orderBy: allocatedTokens, orderDirection: desc + orderBy: balance, orderDirection: desc where: { status: Active } ) { id - allocatedTokens + balance indexer { id url stakedTokens } + collections { + id + status + } } } } @@ -232,10 +244,14 @@ impl Client { "last": last_id.unwrap_or_default(), }, }); + let network_subgraph_url = indexer + .url + .join("subgraphs/name/graph-network") + .context("failed to construct network subgraph URL")?; let response = self .client .query_indexer( - indexer.url.clone(), + network_subgraph_url, IndexerAuth::Free(&indexer.auth), &page_query.to_string(), ) From c2433ea360315ff3a617dba723db243c9b695266 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 17:53:42 -0400 Subject: [PATCH 08/20] chore: add debug logging to subgraph client --- src/indexer_client.rs | 10 +++++++++- src/network/subgraph_client.rs | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/indexer_client.rs b/src/indexer_client.rs index f9226e88..b75837f1 100644 --- a/src/indexer_client.rs +++ b/src/indexer_client.rs @@ -51,9 +51,17 @@ impl IndexerClient { IndexerAuth::Free(token) => (AUTHORIZATION.as_str(), format!("Bearer {token}")), }; + // Debug logging to track actual HTTP request details + tracing::debug!( + url = %deployment_url, + auth_header = auth_key, + query_length = query.len(), + "sending HTTP POST request to indexer" + ); + let result = self .client - .post(deployment_url) + .post(deployment_url.clone()) .header(CONTENT_TYPE.as_str(), "application/json") .header(auth_key, auth_value) .body(query.to_string()) diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index bf5c452c..a0bf5d64 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -248,6 +248,14 @@ impl Client { .url .join("subgraphs/name/graph-network") .context("failed to construct network subgraph URL")?; + + // Debug logging for network subgraph URL construction + tracing::debug!( + indexer_base_url = %indexer.url, + constructed_url = %network_subgraph_url, + "constructed network subgraph URL for trusted indexer query" + ); + let response = self .client .query_indexer( From 4fbd482ae91a90db130c21894c2481ecb80b9997 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 18:00:39 -0400 Subject: [PATCH 09/20] chore: use full trusted indexer url --- src/network/subgraph_client.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index a0bf5d64..446d70ec 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -102,7 +102,7 @@ pub mod types { #[serde_as] #[derive(Clone, CustomDebug, Deserialize)] pub struct TrustedIndexer { - /// network subgraph endpoint + /// Complete network subgraph endpoint URL (e.g., http://indexer:7601/subgraphs/id/Qmc2Cb...) #[debug(with = std::fmt::Display::fmt)] #[serde_as(as = "serde_with::DisplayFromStr")] pub url: Url, @@ -244,16 +244,13 @@ impl Client { "last": last_id.unwrap_or_default(), }, }); - let network_subgraph_url = indexer - .url - .join("subgraphs/name/graph-network") - .context("failed to construct network subgraph URL")?; + // Use trusted indexer URL directly - it already contains the complete network subgraph endpoint + let network_subgraph_url = indexer.url.clone(); - // Debug logging for network subgraph URL construction + // Debug logging for network subgraph URL usage tracing::debug!( - indexer_base_url = %indexer.url, - constructed_url = %network_subgraph_url, - "constructed network subgraph URL for trusted indexer query" + network_subgraph_url = %network_subgraph_url, + "using trusted indexer URL for network subgraph query" ); let response = self From 497ddca189fe492925d5227517fdbacf216662d6 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 18:37:03 -0400 Subject: [PATCH 10/20] fix: update subgraph querying Signed-off-by: Joseph Livesey --- src/network/subgraph_client.rs | 228 +++++++++++++++++++++++++++++---- 1 file changed, 203 insertions(+), 25 deletions(-) diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index 446d70ec..1d0a1550 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -41,6 +41,13 @@ pub mod types { #[serde(rename_all = "camelCase")] pub struct Subgraph { pub id: SubgraphId, + // V2 Horizon fields (populated programmatically, not from JSON) + #[serde(skip)] + pub _service_provider: Option, + #[serde(skip)] + pub _allocations: Vec, + // V1 compatibility (for gradual migration) + #[serde(default)] pub versions: Vec, } @@ -66,7 +73,7 @@ pub mod types { #[serde(rename = "ipfsHash")] pub id: DeploymentId, pub manifest: Option, - #[serde(rename = "paymentsEscrows")] + #[serde(default)] pub payments_escrows: Vec, } @@ -79,6 +86,7 @@ pub mod types { pub balance: u128, pub indexer: Indexer, pub collections: Vec, + pub _subgraph_deployment: SubgraphDeployment, } #[derive(Debug, Clone, Deserialize)] @@ -88,6 +96,36 @@ pub mod types { pub status: String, } + #[derive(Debug, Clone, Deserialize)] + pub struct ServiceProvider { + pub _id: IndexerId, + pub _url: Option, + #[serde(rename = "stakedTokens")] + pub _staked_tokens: String, + } + + #[derive(Debug, Clone, Deserialize)] + pub struct ServiceAllocation { + pub _id: String, + pub _tokens: String, + #[serde(rename = "subgraphDeployment")] + pub _subgraph_deployment: DeploymentInfo, + } + + #[derive(Debug, Clone, Deserialize)] + pub struct DeploymentInfo { + #[serde(rename = "ipfsHash")] + pub _ipfs_hash: DeploymentId, + pub _manifest: Option, + } + + #[derive(Debug, Clone, Deserialize)] + pub struct ManifestInfo { + pub _network: Option, + #[serde(rename = "startBlock")] + pub _start_block: String, + } + #[serde_as] #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -160,42 +198,30 @@ impl Client { let query = r#" query ($block: Block_height!, $first: Int!, $last: String!) { meta: _meta(block: $block) { block { number hash timestamp } } - results: subgraphs( + subgraphs( block: $block - orderBy: id, orderDirection: asc first: $first - where: { - id_gt: $last - entityVersion: 2 - versionCount_gte: 1 - active: true - } + orderBy: id, orderDirection: asc + where: { id_gt: $last } ) { id - versions(orderBy: version, orderDirection: desc) { + versions { version subgraphDeployment { + id ipfsHash manifest { network startBlock } - paymentsEscrows( - first: 100 - orderBy: balance, orderDirection: desc - where: { status: Active } - ) { + indexerAllocations { id - balance + allocatedTokens indexer { id url stakedTokens } - collections { - id - status - } } } } @@ -211,7 +237,60 @@ impl Client { #[derive(Debug, Deserialize)] pub struct QueryData { meta: Meta, - results: Vec, + subgraphs: Vec, + } + + #[derive(Debug, Deserialize)] + pub struct SubgraphWithVersions { + pub id: String, + pub versions: Vec, + } + + #[derive(Debug, Deserialize)] + pub struct SubgraphVersionWithDeployment { + pub version: u32, + #[serde(rename = "subgraphDeployment")] + pub subgraph_deployment: DeploymentWithAllocations, + } + + #[derive(Debug, Deserialize)] + pub struct DeploymentWithAllocations { + pub id: String, + #[serde(rename = "ipfsHash")] + pub ipfs_hash: String, + pub manifest: Option, + #[serde(rename = "indexerAllocations")] + pub indexer_allocations: Vec, + } + + #[derive(Debug, Deserialize)] + pub struct AllocationWithIndexer { + pub id: String, + #[serde(rename = "allocatedTokens")] + pub allocated_tokens: String, + pub indexer: IndexerInfo, + } + + #[derive(Debug, Deserialize)] + pub struct IndexerInfo { + pub id: thegraph_core::IndexerId, + pub url: Option, + #[serde(rename = "stakedTokens")] + pub staked_tokens: String, + } + + #[derive(Debug, Deserialize)] + pub struct DeploymentInfo { + #[serde(rename = "ipfsHash")] + pub _ipfs_hash: thegraph_core::DeploymentId, + pub _manifest: Option, + } + + #[derive(Debug, Deserialize)] + pub struct ManifestInfo { + pub network: String, + #[serde(rename = "startBlock")] + pub start_block: String, } #[derive(Debug, Deserialize)] pub struct Meta { @@ -271,7 +350,7 @@ impl Client { if !response.errors.is_empty() { bail!("{:?}", response.errors); } - let mut data = response + let data = response .data .ok_or_else(|| anyhow!("response missing data"))?; let block = Block { @@ -297,9 +376,108 @@ impl Client { ); query_block = Some(block); } - last_id = data.results.last().map(|entry| entry.id.to_string()); - let page_len = data.results.len(); - results.append(&mut data.results); + // Process V2 subgraphs data with correct SubgraphId → DeploymentId mapping + let subgraphs = data.subgraphs; + last_id = subgraphs.last().map(|entry| entry.id.clone()); + let page_len = subgraphs.len(); + + results.extend(subgraphs.into_iter().map(|subgraph| { + // Parse the SubgraphId from the response + let subgraph_id = subgraph + .id + .parse::() + .unwrap_or_else(|_| { + // Fallback: create SubgraphId from string bytes + use thegraph_core::{SubgraphId, alloy::primitives::FixedBytes}; + let mut bytes = [0u8; 32]; + let id_bytes = subgraph.id.as_bytes(); + let copy_len = id_bytes.len().min(32); + bytes[..copy_len].copy_from_slice(&id_bytes[..copy_len]); + SubgraphId::new(FixedBytes::from(bytes)) + }); + + types::Subgraph { + id: subgraph_id, + _service_provider: None, // V2 doesn't use service providers + _allocations: Vec::new(), // V2 data structure doesn't populate this + versions: subgraph + .versions + .into_iter() + .map(|version| { + // Log deployment info for debugging + tracing::debug!( + deployment_id = %version.subgraph_deployment.id, + ipfs_hash = %version.subgraph_deployment.ipfs_hash, + "processing V2 subgraph deployment" + ); + + let deployment_id = version + .subgraph_deployment + .ipfs_hash + .parse::() + .expect("V2 IPFS hash should be valid DeploymentId"); + + types::SubgraphVersion { + version: version.version, + subgraph_deployment: types::SubgraphDeployment { + id: deployment_id, + manifest: version.subgraph_deployment.manifest.map(|m| { + types::Manifest { + network: Some(m.network), + start_block: m.start_block.parse().unwrap_or(0), + } + }), + payments_escrows: version + .subgraph_deployment + .indexer_allocations + .into_iter() + .map(|alloc| { + let allocation_id = alloc.id.clone(); + types::PaymentsEscrow { + id: alloc.id, + balance: alloc + .allocated_tokens + .parse() + .unwrap_or(0), + indexer: types::Indexer { + id: alloc.indexer.id, + url: alloc.indexer.url, + staked_tokens: alloc + .indexer + .staked_tokens + .parse() + .unwrap_or(0), + }, + collections: vec![types::Collection { + id: { + // Convert allocation ID to collection ID + use thegraph_core::{ + CollectionId, + alloy::primitives::FixedBytes, + }; + let mut bytes = [0u8; 32]; + let id_bytes = allocation_id.as_bytes(); + let copy_len = id_bytes.len().min(32); + bytes[..copy_len] + .copy_from_slice(&id_bytes[..copy_len]); + CollectionId::new(FixedBytes::from(bytes)) + }, + status: "Active".to_string(), // Default status + }], + _subgraph_deployment: types::SubgraphDeployment { + id: deployment_id, + manifest: None, // Avoid circular reference + payments_escrows: Vec::new(), // Avoid circular reference + }, + } + }) + .collect(), + }, + } + }) + .collect(), + } + })); if page_len < self.page_size { break; } From 2f1ccfb14c3b28f6cd6572bf7492310df52ad631 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 22:13:56 -0400 Subject: [PATCH 11/20] feat: inject what 'too far behind' means --- src/config.rs | 8 ++++++++ src/main.rs | 1 + src/network/subgraph_client.rs | 4 +++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 6ea5752e..9c0691dd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -48,6 +48,9 @@ pub struct Config { pub min_indexer_version: Version, /// Trusted indexers that can serve the network subgraph for free pub trusted_indexers: Vec, + /// Maximum acceptable lag (in seconds) for network subgraph responses (default: 120) + #[serde(default = "default_network_subgraph_max_lag_seconds")] + pub network_subgraph_max_lag_seconds: u64, /// Check payment state of client (disable for testnets) pub payment_required: bool, /// public API port @@ -60,6 +63,11 @@ pub struct Config { pub receipts: Receipts, } +/// Default network subgraph max lag threshold (120 seconds) +fn default_network_subgraph_max_lag_seconds() -> u64 { + 120 +} + /// Deserialize a `NotNan` from a `f64` and return an error if the value is NaN. fn deserialize_not_nan_f64<'de, D>(deserializer: D) -> Result, D::Error> where diff --git a/src/main.rs b/src/main.rs index 7485f31c..aaf12ed8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,6 +112,7 @@ async fn main() { indexers: conf.trusted_indexers, latest_block: None, page_size: 500, + max_lag_seconds: conf.network_subgraph_max_lag_seconds, }; let indexer_host_blocklist = match &conf.ip_blocker_db { Some(path) => { diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index 1d0a1550..22c58f46 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -171,6 +171,7 @@ pub struct Client { pub indexers: Vec, pub page_size: usize, pub latest_block: Option, + pub max_lag_seconds: u64, } impl Client { @@ -371,7 +372,8 @@ impl Client { "response block before latest", ); ensure!( - (unix_timestamp() / 1_000).saturating_sub(block.timestamp) < 120, + (unix_timestamp() / 1_000).saturating_sub(block.timestamp) + < self.max_lag_seconds, "response too far behind", ); query_block = Some(block); From c9b642eb86efd62ed41788e6d6e00e02096ad456 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Thu, 14 Aug 2025 22:43:11 -0400 Subject: [PATCH 12/20] fix: fix tap receipt header --- src/indexer_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexer_client.rs b/src/indexer_client.rs index b75837f1..e8f9a568 100644 --- a/src/indexer_client.rs +++ b/src/indexer_client.rs @@ -47,7 +47,7 @@ impl IndexerClient { query: &str, ) -> Result { let (auth_key, auth_value) = match auth { - IndexerAuth::Paid(receipt, _) => ("Tap-Receipt", receipt.serialize()), + IndexerAuth::Paid(receipt, _) => ("tap-receipt", receipt.serialize()), IndexerAuth::Free(token) => (AUTHORIZATION.as_str(), format!("Bearer {token}")), }; From e6ce3964b5972210c95553dc9fd0c7e69384bd0b Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Fri, 15 Aug 2025 09:28:45 -0400 Subject: [PATCH 13/20] build: update tap-aggregator to 0.5.8 --- Cargo.lock | 569 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 + 2 files changed, 563 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d71e9c6b..b87d3444 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,6 +707,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + [[package]] name = "anyhow" version = "1.0.98" @@ -1141,6 +1191,7 @@ checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core", "bytes", + "form_urlencoded", "futures-util", "http 1.3.1", "http-body 1.0.1", @@ -1157,6 +1208,7 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", + "serde_urlencoded", "sync_wrapper", "tokio", "tower", @@ -1378,6 +1430,8 @@ version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -1400,6 +1454,46 @@ dependencies = [ "windows-link", ] +[[package]] +name = "clap" +version = "4.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + [[package]] name = "cmake" version = "0.1.54" @@ -1432,6 +1526,12 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "combine" version = "4.6.7" @@ -1555,6 +1655,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -1932,6 +2042,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.2" @@ -2093,13 +2209,13 @@ dependencies = [ "jsonwebtoken", "once_cell", "prost 0.13.5", - "prost-types", + "prost-types 0.13.5", "reqwest", "secret-vault-value", "serde", "serde_json", "tokio", - "tonic", + "tonic 0.13.1", "tower", "tower-layer", "tower-util", @@ -2176,6 +2292,7 @@ dependencies = [ "anyhow", "assert_matches", "axum", + "base64", "custom_debug", "faster-hex", "futures", @@ -2195,13 +2312,14 @@ dependencies = [ "prometheus", "prost 0.14.1", "rand 0.9.1", - "rdkafka", + "rdkafka 0.38.0", "reqwest", "semver 1.0.26", "serde", "serde_json", "serde_with", "snmalloc-rs", + "tap_aggregator", "tap_graph", "thegraph-core", "thegraph-graphql-http", @@ -2584,7 +2702,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -2797,7 +2915,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg", @@ -2825,6 +2943,12 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -2849,6 +2973,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -2859,6 +2993,97 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpsee" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fba77a59c4c644fd48732367624d1bcf6f409f9c9a286fbc71d2f1fc0b2ea16" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693c93cbb7db25f4108ed121304b671a36002c2db67dff2ee4391a688c738547" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "pin-project 1.1.10", + "rand 0.9.1", + "rustc-hash", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fa4f5daed39f982a1bb9d15449a28347490ad42b212f8eaa2a2a344a0dce9e9" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38b0bcf407ac68d241f90e2d46041e6a06988f97fe1721fb80b91c42584fae6" +dependencies = [ + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project 1.1.10", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df7256371c45621b3b7d2fb23aea923d577616b9c0e9c0b950a6ea5c2be0ca" +dependencies = [ + "http 1.3.1", + "serde", + "serde_json", + "thiserror 2.0.12", +] + [[package]] name = "jsonwebtoken" version = "9.3.1" @@ -3095,6 +3320,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "native-tls" version = "0.2.14" @@ -3235,6 +3466,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl" version = "0.10.73" @@ -3409,6 +3646,16 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap 2.10.0", +] + [[package]] name = "pin-project" version = "0.4.30" @@ -3507,6 +3754,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn 2.0.104", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -3569,6 +3826,7 @@ dependencies = [ "lazy_static", "memchr", "parking_lot", + "protobuf", "thiserror 2.0.12", ] @@ -3612,6 +3870,28 @@ dependencies = [ "prost-derive 0.14.1", ] +[[package]] +name = "prost-build" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.14.1", + "prost-types 0.14.1", + "pulldown-cmark", + "pulldown-cmark-to-cmark", + "regex", + "syn 2.0.104", + "tempfile", +] + [[package]] name = "prost-derive" version = "0.13.5" @@ -3647,6 +3927,55 @@ dependencies = [ "prost 0.13.5", ] +[[package]] +name = "prost-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" +dependencies = [ + "prost 0.14.1", +] + +[[package]] +name = "protobuf" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +dependencies = [ + "bitflags 2.9.1", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" +dependencies = [ + "pulldown-cmark", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3745,6 +4074,44 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rdkafka" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b52c81ac3cac39c9639b95c20452076e74b8d9a71bc6fc4d83407af2ea6fff" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "log", + "rdkafka-sys", + "serde", + "serde_derive", + "serde_json", + "slab", + "tokio", +] + [[package]] name = "rdkafka" version = "0.38.0" @@ -3945,6 +4312,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "ruint" version = "1.15.0" @@ -4207,7 +4580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc32a777b53b3433b974c9c26b6d502a50037f8da94e46cb8ce2ced2cfdfaea0" dependencies = [ "prost 0.13.5", - "prost-types", + "prost-types 0.13.5", "serde", "serde_json", "zeroize", @@ -4535,6 +4908,32 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64", + "bytes", + "futures", + "http 1.3.1", + "httparse", + "log", + "rand 0.8.5", + "sha1", +] + [[package]] name = "spki" version = "0.7.3" @@ -4678,6 +5077,56 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tap_aggregator" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6f3c4d06f8d12d03cefc6db063c62e16f0d1567a7dc94a3d8f3109ecc05739" +dependencies = [ + "anyhow", + "axum", + "clap", + "futures-util", + "hyper", + "jsonrpsee", + "lazy_static", + "log", + "prometheus", + "prost 0.14.1", + "rayon", + "rdkafka 0.37.0", + "serde", + "serde_json", + "strum", + "tap_core", + "tap_graph", + "thegraph-core", + "tokio", + "tonic 0.14.1", + "tonic-build", + "tonic-prost", + "tonic-prost-build", + "tower", + "tracing-subscriber", +] + +[[package]] +name = "tap_core" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3f0ea044210aa27b40d5a19069b10f3ece4929488d4f2a872ff0f02075d0d7" +dependencies = [ + "anyhow", + "async-trait", + "rand 0.9.1", + "tap_eip712_message", + "tap_graph", + "tap_receipt", + "thegraph-core", + "thiserror 2.0.12", + "tokio", +] + [[package]] name = "tap_eip712_message" version = "0.2.2" @@ -4907,7 +5356,7 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2", + "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", ] @@ -4976,6 +5425,7 @@ checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -5019,7 +5469,7 @@ dependencies = [ "pin-project 1.1.10", "prost 0.13.5", "rustls-native-certs", - "socket2", + "socket2 0.5.10", "tokio", "tokio-rustls", "tokio-stream", @@ -5029,6 +5479,75 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project 1.1.10", + "socket2 0.6.0", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", + "zstd", +] + +[[package]] +name = "tonic-build" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tonic-prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" +dependencies = [ + "bytes", + "prost 0.14.1", + "tonic 0.14.1", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types 0.14.1", + "quote", + "syn 2.0.104", + "tempfile", + "tonic-build", +] + [[package]] name = "tower" version = "0.5.2" @@ -5268,6 +5787,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.17.0" @@ -5975,3 +6500,31 @@ dependencies = [ "quote", "syn 2.0.104", ] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 4ac270bb..76ed80bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ axum = { version = "0.8.1", default-features = false, features = [ "tokio", "http1", ] } +base64 = "0.22.1" custom_debug = "0.6.1" faster-hex = "0.10.0" futures = "0.3" @@ -46,6 +47,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.116", features = ["raw_value"] } serde_with = "3.8.1" snmalloc-rs = "0.3" +tap_aggregator = "0.5.8" tap_graph = { version = "0.3.4", features = ["v2"] } thegraph-core = { version = "0.15.1", features = [ "alloy-contract", From c4b4a4c4bc22e144d383da09facff9b5275a3dd9 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Fri, 15 Aug 2025 11:18:19 -0400 Subject: [PATCH 14/20] chore(network): add debug logging Signed-off-by: Joseph Livesey --- src/indexer_client.rs | 10 ++++++++++ src/network/pre_processing.rs | 9 +++++++++ src/network/service.rs | 17 ++++++++++++++++- src/receipts.rs | 13 +++++++++++-- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/indexer_client.rs b/src/indexer_client.rs index e8f9a568..c065974e 100644 --- a/src/indexer_client.rs +++ b/src/indexer_client.rs @@ -55,10 +55,20 @@ impl IndexerClient { tracing::debug!( url = %deployment_url, auth_header = auth_key, + auth_value_length = auth_value.len(), query_length = query.len(), "sending HTTP POST request to indexer" ); + // CRITICAL DEBUG: Log if TAP receipt header is being set + if auth_key == "tap-receipt" { + tracing::warn!( + url = %deployment_url, + auth_value_preview = &auth_value[..auth_value.len().min(50)], + "TAP RECEIPT HEADER BEING SENT TO INDEXER" + ); + } + let result = self .client .post(deployment_url.clone()) diff --git a/src/network/pre_processing.rs b/src/network/pre_processing.rs index c9bdb57a..4bbd2962 100644 --- a/src/network/pre_processing.rs +++ b/src/network/pre_processing.rs @@ -142,10 +142,19 @@ pub fn into_internal_deployments_raw_info<'a>( .fold(HashMap::new(), |mut acc, deployment_raw_info| { let deployment_id = deployment_raw_info.id; + // Debug logging for deployment processing + tracing::debug!( + ?deployment_id, + subgraphs_count = deployment_raw_info.subgraphs.len(), + allocations_count = deployment_raw_info.allocations.len(), + "processing deployment in pre_processing pipeline" + ); + // If the deployment info is not present, insert it let deployment = match acc.entry(deployment_id) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { + tracing::debug!(?deployment_id, "inserting new deployment into registry"); entry.insert(deployment_raw_info.clone()); return acc; } diff --git a/src/network/service.rs b/src/network/service.rs index 06d70f07..6b2e644c 100644 --- a/src/network/service.rs +++ b/src/network/service.rs @@ -128,9 +128,24 @@ impl NetworkService { ) -> Result, DeploymentError> { let network = self.network.borrow(); + // Debug logging for deployment resolution + tracing::debug!( + ?id, + total_deployments = network.deployments.len(), + "attempting to resolve deployment ID" + ); + + // Log first few deployment IDs for comparison + for (dep_id, _) in network.deployments.iter().take(3) { + tracing::debug!(?dep_id, "available deployment in registry"); + } + // Resolve the deployment information let deployment = match network.deployments.get(id) { - None => return Ok(None), + None => { + tracing::warn!(?id, "deployment ID not found in registry"); + return Ok(None); + } Some(Err(err)) => return Err(err.to_owned()), Some(Ok(deployment)) => deployment, }; diff --git a/src/receipts.rs b/src/receipts.rs index cbda54aa..4585ac1c 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -1,5 +1,7 @@ use std::time::SystemTime; +use base64::{Engine as _, engine::general_purpose}; +use prost::Message; use rand::RngCore; use serde::Serialize; use thegraph_core::{ @@ -28,9 +30,16 @@ impl Receipt { self.0.message.collection_id.into() } - /// Serialize the receipt to JSON string + /// Serialize the receipt to base64-encoded protobuf format for V2 compatibility pub fn serialize(&self) -> String { - serde_json::to_string(&self.0).unwrap() + // Convert tap_graph::v2::SignedReceipt to protobuf format + let protobuf_receipt: tap_aggregator::grpc::v2::SignedReceipt = self.0.clone().into(); + + // Encode to protobuf bytes + let bytes = protobuf_receipt.encode_to_vec(); + + // Base64 encode the bytes + general_purpose::STANDARD.encode(&bytes) } } From 76c4118b1d6a536103bb7837e108d5e4e2e8af08 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Fri, 15 Aug 2025 11:54:36 -0400 Subject: [PATCH 15/20] build: install protobuf compiler --- .github/workflows/ci.yml | 2 +- Dockerfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c433336..d1000d74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: uses: Leafwing-Studios/cargo-cache@43ec9a5bad6e7f174e7fc65dcf533de75ff65881 # v2 - name: prepare build env - run: sudo apt-get install -y lld librdkafka-dev libsasl2-dev + run: sudo apt-get install -y lld librdkafka-dev libsasl2-dev protobuf-compiler - run: cargo check - run: cargo clippy -- -Dwarnings --force-warn deprecated --force-warn dead-code diff --git a/Dockerfile b/Dockerfile index 48584f67..dc75b24f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ RUN apt-get update && apt-get install -y \ libsasl2-dev \ libssl-dev \ pkg-config \ + protobuf-compiler \ zlib1g-dev \ && rm -rf /var/lib/apt/lists/* From 774fafee474f644f563d75fff3e09f58e81a47b4 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Fri, 15 Aug 2025 16:09:15 -0400 Subject: [PATCH 16/20] chore(client_query): add query entrypoint log --- src/client_query.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/client_query.rs b/src/client_query.rs index 48b7ab60..3f3d8aed 100644 --- a/src/client_query.rs +++ b/src/client_query.rs @@ -61,9 +61,18 @@ pub async fn handle_query( ) -> Result, Error> { let start_time = Instant::now(); + // CRITICAL DEBUG: Log query entry point + tracing::warn!( + ?selector, + payload_length = payload.len(), + "QUERY ENTRY POINT - starting query processing" + ); + // Check if the query selector is authorized by the auth token and // resolve the subgraph deployments for the query. + tracing::debug!("about to call resolve_subgraph_info"); let subgraph = resolve_subgraph_info(&ctx, &auth, selector).await?; + tracing::debug!("resolve_subgraph_info completed successfully"); let client_request: QueryBody = serde_json::from_reader(payload.reader()).map_err(|err| Error::BadQuery(err.into()))?; From ca10bef3561e393e14f57ea572967af56ec80792 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Sat, 16 Aug 2025 13:28:45 -0400 Subject: [PATCH 17/20] fix: fix collection id derivation --- src/network/subgraph_client.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index 22c58f46..89673271 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -452,17 +452,14 @@ impl Client { }, collections: vec![types::Collection { id: { - // Convert allocation ID to collection ID - use thegraph_core::{ - CollectionId, - alloy::primitives::FixedBytes, - }; - let mut bytes = [0u8; 32]; - let id_bytes = allocation_id.as_bytes(); - let copy_len = id_bytes.len().min(32); - bytes[..copy_len] - .copy_from_slice(&id_bytes[..copy_len]); - CollectionId::new(FixedBytes::from(bytes)) + // Convert allocation ID to collection ID using standard left-padding + use thegraph_core::AllocationId; + let allocation_parsed = allocation_id + .parse::() + .expect( + "V2 allocation ID should be valid", + ); + allocation_parsed.into() // Uses standard left-padding conversion }, status: "Active".to_string(), // Default status }], From ecf7926b4dc4f0a56c4584da408edc69422dab5f Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Sat, 16 Aug 2025 16:41:55 -0400 Subject: [PATCH 18/20] fix: use correct addresses in the EIP712 message hash --- src/client_query.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client_query.rs b/src/client_query.rs index 3f3d8aed..575b3c38 100644 --- a/src/client_query.rs +++ b/src/client_query.rs @@ -381,9 +381,9 @@ async fn run_indexer_queries( let receipt = match ctx.receipt_signer.create_receipt( largest_collection, fee, - ctx.receipt_signer.payer_address(), - indexer.into_inner(), - indexer.into_inner(), + ctx.receipt_signer.payer_address(), // payer: gateway address + ctx.receipt_signer.payer_address(), // data_service: gateway address + indexer.into_inner(), // service_provider: indexer address ) { Ok(receipt) => { tracing::debug!( @@ -797,9 +797,9 @@ pub async fn handle_indexer_query( let receipt = match ctx.receipt_signer.create_receipt( collection, fee, - ctx.receipt_signer.payer_address(), - indexer.into_inner(), - indexer.into_inner(), + ctx.receipt_signer.payer_address(), // payer: gateway address + ctx.receipt_signer.payer_address(), // data_service: gateway address + indexer.into_inner(), // service_provider: indexer address ) { Ok(receipt) => receipt, Err(err) => { From f46580057972e78f8667000cbf104643957731d5 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Sat, 16 Aug 2025 19:33:41 -0400 Subject: [PATCH 19/20] chore: add more debug logging --- src/receipts.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/receipts.rs b/src/receipts.rs index 4585ac1c..7872423a 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -39,7 +39,17 @@ impl Receipt { let bytes = protobuf_receipt.encode_to_vec(); // Base64 encode the bytes - general_purpose::STANDARD.encode(&bytes) + let serialized = general_purpose::STANDARD.encode(&bytes); + + // DEBUG: Log receipt serialization details + tracing::debug!( + serialized_preview = &serialized[..serialized.len().min(100)], + serialized_length = serialized.len(), + protobuf_bytes_length = bytes.len(), + "TAP receipt serialized format" + ); + + serialized } } @@ -106,10 +116,39 @@ impl ReceiptSigner { value: fee, }; + // DEBUG: Log all receipt fields before signing + tracing::debug!( + collection_id = ?receipt.collection_id, + payer = ?receipt.payer, + data_service = ?receipt.data_service, + service_provider = ?receipt.service_provider, + timestamp_ns = receipt.timestamp_ns, + nonce = receipt.nonce, + value = receipt.value, + "TAP receipt fields before signing" + ); + + // DEBUG: Log EIP-712 domain components + tracing::debug!( + domain_name = ?self.v2_config.domain.name, + domain_version = ?self.v2_config.domain.version, + domain_chain_id = ?self.v2_config.domain.chain_id, + domain_verifying_contract = ?self.v2_config.domain.verifying_contract, + "TAP EIP-712 domain components" + ); + let signed = tap_graph::v2::SignedReceipt::new(&self.v2_config.domain, receipt, &self.signer) .map_err(|e| anyhow::anyhow!("failed to sign v2 receipt: {:?}", e))?; + // DEBUG: Log the signature being generated + tracing::debug!( + signature = ?signed.signature, + signature_bytes = ?signed.signature.as_bytes(), + expected_signer = ?self.signer.address(), + "TAP receipt signature generated" + ); + Ok(Receipt(signed)) } From 20fc916f27af3b05851a613c9118c2e8b7dc792a Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Sat, 16 Aug 2025 20:51:04 -0400 Subject: [PATCH 20/20] build: update cargo dependencies (tap-aggregator) --- Cargo.lock | 575 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 282 insertions(+), 295 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b87d3444..b9002679 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd691724d088287334932cdef99b79ecb89ea01fff33a12552093495fdc99d1" +checksum = "48dff4dd98e17de00203f851800bbc8b76eb29a4d4e3e44074614338b7a3308d" dependencies = [ "alloy-consensus", "alloy-contract", @@ -59,9 +59,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5674914c2cfdb866c21cb0c09d82374ee39a1395cf512e7515f4c014083b3fff" +checksum = "4195a29a4b87137b2bb02105e746102873bc03561805cf45c0e510c961f160e6" dependencies = [ "alloy-primitives", "num_enum", @@ -70,9 +70,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a694d8be621ee12b45ae23e7f18393b9a1e04f1ba47a0136767cb8c955f7f8" +checksum = "eda689f7287f15bd3582daba6be8d1545bad3740fd1fb778f629a1fe866bb43b" dependencies = [ "alloy-eips", "alloy-primitives", @@ -90,14 +90,14 @@ dependencies = [ "secp256k1", "serde", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "alloy-consensus-any" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1647d47f59288584cc3b40eff3e7dde6af8c88a2fca8fe02c22de7b9ab218ffa" +checksum = "2b5659581e41e8fe350ecc3593cb5c9dcffddfd550896390f2b78a07af67b0fa" dependencies = [ "alloy-consensus", "alloy-eips", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bea12b41db8e25614b48636609549d721c3888a34a9512a7677a1b665c2c08c" +checksum = "944085cf3ac8f32d96299aa26c03db7c8ca6cdaafdbc467910b889f0328e6b70" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -126,14 +126,14 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "alloy-core" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad31216895d27d307369daa1393f5850b50bbbd372478a9fa951c095c210627e" +checksum = "d47400608fc869727ad81dba058d55f97b29ad8b5c5256d9598523df8f356ab6" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b95b3deca680efc7e9cba781f1a1db352fa1ea50e6384a514944dcf4419e652" +checksum = "d9e8a436f0aad7df8bb47f144095fba61202265d9f5f09a70b0e3227881a668e" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -169,7 +169,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -192,14 +192,14 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "serde", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "alloy-eips" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715ae25d525c567481ba2fc97000415624836d516958b9c3f189f1e267d1d90a" +checksum = "6f35887da30b5fc50267109a3c61cd63e6ca1f45967983641053a40ee83468c1" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -217,9 +217,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696a83af273bfc512e02693bd4b5056c8c57898328bd0ce594013fb864de4dcf" +checksum = "11d4009efea6f403b3a80531f9c6f70fc242399498ff71196a1688cc1c901f44" dependencies = [ "alloy-eips", "alloy-primitives", @@ -231,9 +231,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15516116086325c157c18261d768a20677f0f699348000ed391d4ad0dcb82530" +checksum = "459f98c6843f208856f338bfb25e65325467f7aff35dfeb0484d0a76e059134b" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -243,24 +243,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cc040744bc63111a70dd295984858e33a1ef1aff00c1c86be166b5c475ad23" +checksum = "883dee3b4020fcb5667ee627b4f401e899dad82bf37b246620339dd980720ed9" dependencies = [ "alloy-primitives", "alloy-sol-types", "http 1.3.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", "tracing", ] [[package]] name = "alloy-network" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2d7663b088e11bed83ec9269347d7f56a5a80fb80c33a1ba1e60b470874e9" +checksum = "cd6e5b8ac1654a05c224390008e43634a2bdc74e181e02cf8ed591d8b3d4ad08" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -279,14 +279,14 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "alloy-network-primitives" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35648c318b4649d2d141d1ed4f6e32c69f4959bdc2f6e44d53c0a333ed615a37" +checksum = "80d7980333dd9391719756ac28bc2afa9baa705fc70ffd11dc86ab078dd64477" dependencies = [ "alloy-consensus", "alloy-eips", @@ -297,9 +297,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6177ed26655d4e84e00b65cb494d4e0b8830e7cae7ef5d63087d445a2600fb55" +checksum = "3cfebde8c581a5d37b678d0a48a32decb51efd7a63a08ce2517ddec26db705c8" dependencies = [ "alloy-rlp", "bytes", @@ -307,14 +307,14 @@ dependencies = [ "const-hex", "derive_more", "foldhash", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "indexmap 2.10.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "rand 0.9.1", + "rand 0.9.2", "ruint", "rustc-hash", "serde", @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef5509f1f2ff26650eb4f8b715f31dacbf583cdadc70013824405c5940d9e5" +checksum = "478a42fe167057b7b919cd8b0c2844f0247f667473340dad100eaf969de5754e" dependencies = [ "alloy-chains", "alloy-consensus", @@ -348,14 +348,13 @@ dependencies = [ "either", "futures", "futures-utils-wasm", - "http 1.3.1", "lru", "parking_lot", "pin-project 1.1.10", "reqwest", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tracing", "url", @@ -381,20 +380,19 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "alloy-rpc-client" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbbf4c34305ae1ff0a38e63e88690920d030ac197a71d009f36b3e48dc47c1" +checksum = "8a0c6d723fbdf4a87454e2e3a275e161be27edcfbf46e2e3255dd66c138634b6" dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-transport", "alloy-transport-http", - "async-stream", "futures", "pin-project 1.1.10", "reqwest", @@ -404,16 +402,15 @@ dependencies = [ "tokio-stream", "tower", "tracing", - "tracing-futures", "url", "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953b106defef74307b1379611ae9180e48d6b1ab8122d9a6fb8d6b7de1392cf9" +checksum = "c41492dac39365b86a954de86c47ec23dcc7452cdb2fde591caadc194b3e34c6" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -423,9 +420,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1e99233cff99aff94fe29cea9e9dd6014c85eeea7890ea4f0e4eada9957ceb" +checksum = "8f7eb22670a972ad6c222a6c6dac3eef905579acffe9d63ab42be24c7d158535" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -434,9 +431,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8300d59b0126876a1914102c588f9a4792eb4c754d483a954dc29904ddf79d6" +checksum = "b777b98526bbe5b7892ca22a7fd5f18ed624ff664a79f40d0f9f2bf94ba79a84" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -449,14 +446,15 @@ dependencies = [ "itertools 0.14.0", "serde", "serde_json", - "thiserror 2.0.12", + "serde_with", + "thiserror 2.0.15", ] [[package]] name = "alloy-serde" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8070bc2af2d48969e3aa709ea3ebf1f8316176b91c2132efe33d113f74383a9e" +checksum = "ee8d2c52adebf3e6494976c8542fbdf12f10123b26e11ad56f77274c16a2a039" dependencies = [ "alloy-primitives", "serde", @@ -465,9 +463,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f19a87067de976c8c8a0526d6d70de612bbd08bb8051a297b9a12084a9a4ae1" +checksum = "7c0494d1e0f802716480aabbe25549c7f6bc2a25ff33b08fd332bbb4b7d06894" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -477,14 +475,14 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "alloy-signer-aws" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2abab2db4b0cdf94fa75c553470445f85c5532e612eea720c3f1ac6940f280" +checksum = "0559495d87c099f7dbd0804145032e6a16ee675d1d2a15e98dc2658d64265cde" dependencies = [ "alloy-consensus", "alloy-network", @@ -494,15 +492,15 @@ dependencies = [ "aws-sdk-kms", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.15", "tracing", ] [[package]] name = "alloy-signer-gcp" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2a23310bc2532b55e43f9e3a262486c40ef78832274f49010195886a4de4ef" +checksum = "946fbac85c43e1f73db388629e2115c41c4211ec8532bc46514d8153e81e818b" dependencies = [ "alloy-consensus", "alloy-network", @@ -512,15 +510,15 @@ dependencies = [ "gcloud-sdk", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.15", "tracing", ] [[package]] name = "alloy-signer-ledger" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c1f77fef0e1861f2eb08d1a114bd0870d8939f0055344dac825cefa393201e" +checksum = "d4891d26fe418793186c30ea49451da8b5be2d9368547c9f1877002d3b0a192a" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -532,15 +530,15 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.26", - "thiserror 2.0.12", + "thiserror 2.0.15", "tracing", ] [[package]] name = "alloy-signer-local" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6611724477b6b914202392c2f2d9dfd5c88e4eb8b1422ef90ac6cda3649b62a6" +checksum = "59c2435eb8979a020763ced3fb478932071c56e5f75ea86db41f320915d325ba" dependencies = [ "alloy-consensus", "alloy-network", @@ -549,28 +547,28 @@ dependencies = [ "async-trait", "k256", "rand 0.8.5", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "alloy-sol-macro" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a14f21d053aea4c6630687c2f4ad614bed4c81e14737a9b904798b24f30ea849" +checksum = "aedac07a10d4c2027817a43cc1f038313fc53c7ac866f7363239971fd01f9f18" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d99282e7c9ef14eb62727981a985a01869e586d1dec729d3bb33679094c100" +checksum = "24f9a598f010f048d8b8226492b6401104f5a5c1273c2869b72af29b48bb4ba9" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -580,16 +578,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda029f955b78e493360ee1d7bd11e1ab9f2a220a5715449babc79d6d0a01105" +checksum = "f494adf9d60e49aa6ce26dfd42c7417aa6d4343cf2ae621f20e4d92a5ad07d85" dependencies = [ "alloy-json-abi", "const-hex", @@ -599,15 +597,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.104", + "syn 2.0.106", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10db1bd7baa35bc8d4a1b07efbf734e73e5ba09f2580fb8cee3483a36087ceb2" +checksum = "52db32fbd35a9c0c0e538b58b81ebbae08a51be029e7ad60e08b60481c2ec6c3" dependencies = [ "serde", "winnow", @@ -615,9 +613,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58377025a47d8b8426b3e4846a251f2c1991033b27f517aade368146f6ab1dfe" +checksum = "a285b46e3e0c177887028278f04cc8262b76fd3b8e0e20e93cea0a58c35f5ac5" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -627,12 +625,13 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f5b563b89faff55a3705e23972e40ee0c59634e356f1a79cc1e9c0be31744" +checksum = "3c0107675e10c7f248bf7273c1e7fdb02409a717269cc744012e6f3c39959bfb" dependencies = [ "alloy-json-rpc", "alloy-primitives", + "auto_impl", "base64", "derive_more", "futures", @@ -640,7 +639,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tower", "tracing", @@ -650,9 +649,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d25d5b547ce62a699957bcc3b3bd21d141d784dcc1b5b7dfc8a71cd36cd56d" +checksum = "78e3736701b5433afd06eecff08f0688a71a10e0e1352e0bbf0bed72f0dd4e35" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -681,15 +680,15 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "472e12600c46b766110edd8382b4804d70188870f064531ee8fd61a35ed18686" +checksum = "6acb36318dfa50817154064fea7932adf2eec3f51c86680e2b37d7e8906c66bb" dependencies = [ "alloy-primitives", "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -759,9 +758,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "anymap3" @@ -910,9 +909,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ "flate2", "futures-core", @@ -940,18 +939,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -968,7 +967,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -979,9 +978,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-credential-types" -version = "1.2.3" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" +checksum = "1541072f81945fa1251f8795ef6c92c4282d74d59f88498ae7d4bf00f0ebdad9" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -991,9 +990,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.8" +version = "1.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f6c68419d8ba16d9a7463671593c54f81ba58cab466e9b759418da606dcc2e2" +checksum = "c034a1bc1d70e16e7f4e4caf7e9f7693e4c9c24cd91cf17c2a0b21abaebc7c8b" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1015,9 +1014,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.77.0" +version = "1.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cd57d0c1a5bd6c7eaa2b26462e046d5ca7b72189346718d2435dfc48bfa988b" +checksum = "98037a2a0745914d2f0fee41acb6cf88a76f0ed31dd75753b4dc318aa5a4da39" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1037,9 +1036,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfb9021f581b71870a17eac25b52335b82211cdc092e02b6876b2bcefa61666" +checksum = "084c34162187d39e3740cb635acd73c4e3a551a36146ad6fe8883c929c9f876c" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1070,9 +1069,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.62.1" +version = "0.62.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" +checksum = "7c4dacf2d38996cf729f55e7a762b30918229917eca115de45dfa8dfb97796c9" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1108,9 +1107,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.8.3" +version = "1.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" +checksum = "9e107ce0783019dbff59b3a244aa0c114e4a8c9d93498af9162608cd5474e796" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1131,9 +1130,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.8.1" +version = "1.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8531b6d8882fd8f48f82a9754e682e29dd44cff27154af51fa3eb730f59efb" +checksum = "75d52251ed4b9776a3e8487b2a01ac915f73b2da3af8fc1e77e0fce697a550d4" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1171,9 +1170,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.7" +version = "1.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" +checksum = "b069d19bf01e46298eaedd7c6f283fe565a59263e53eebec945f3e6398f42390" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1317,9 +1316,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "bitvec" @@ -1426,9 +1425,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.29" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "jobserver", "libc", @@ -1485,7 +1484,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1633,9 +1632,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1726,7 +1725,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -1751,7 +1750,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1762,7 +1761,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1834,7 +1833,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "unicode-xid", ] @@ -1867,7 +1866,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1890,9 +1889,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" @@ -1957,7 +1956,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2156,7 +2155,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2197,9 +2196,9 @@ checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] name = "gcloud-sdk" -version = "0.27.3" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac903b34cd86b6e3479924e8a9517edba8d5deebee0c1013353b05108ea9bd3" +checksum = "c8458d2ad7741b6a16981b84e66b7e4d8026423096da721894769c6980d06ecc" dependencies = [ "async-trait", "bytes", @@ -2281,9 +2280,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "graph-gateway" @@ -2311,7 +2310,7 @@ dependencies = [ "pin-project 1.1.10", "prometheus", "prost 0.14.1", - "rand 0.9.1", + "rand 0.9.2", "rdkafka 0.38.0", "reqwest", "semver 1.0.26", @@ -2324,7 +2323,7 @@ dependencies = [ "thegraph-core", "thegraph-graphql-http", "thegraph-headers", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tokio-test", "tower", @@ -2368,9 +2367,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -2408,9 +2407,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -2498,9 +2497,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "ring", - "thiserror 2.0.12", + "thiserror 2.0.15", "tinyvec", "tokio", "tracing", @@ -2520,10 +2519,10 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.1", + "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tracing", ] @@ -2686,9 +2685,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64", "bytes", @@ -2702,7 +2701,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -2864,7 +2863,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2894,17 +2893,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "libc", ] @@ -3022,11 +3021,11 @@ dependencies = [ "jsonrpsee-types", "parking_lot", "pin-project 1.1.10", - "rand 0.9.1", + "rand 0.9.2", "rustc-hash", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tower", "tracing", @@ -3042,7 +3041,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3064,7 +3063,7 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tokio-stream", "tokio-util", @@ -3081,7 +3080,7 @@ dependencies = [ "http 1.3.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -3140,9 +3139,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libm" @@ -3221,7 +3220,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -3232,7 +3231,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3430,14 +3429,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "nybbles" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675b3a54e5b12af997abc8b6638b0aee51a28caedab70d4967e0d5db3a3f1d06" +checksum = "63cb50036b1ad148038105af40aaa70ff24d8a14fbc44ae5c914e1348533d12e" dependencies = [ "alloy-rlp", "cfg-if", @@ -3478,7 +3477,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "foreign-types", "libc", @@ -3495,7 +3494,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3572,7 +3571,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3642,7 +3641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.15", "ucd-trie", ] @@ -3693,7 +3692,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3761,7 +3760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3803,14 +3802,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -3827,7 +3826,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -3838,10 +3837,10 @@ checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.1", + "bitflags 2.9.2", "lazy_static", "num-traits", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax 0.8.5", @@ -3888,7 +3887,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn 2.0.104", + "syn 2.0.106", "tempfile", ] @@ -3902,7 +3901,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3915,7 +3914,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3962,7 +3961,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "memchr", "unicase", ] @@ -4017,9 +4016,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4147,11 +4146,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -4171,7 +4170,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4226,9 +4225,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "async-compression", "base64", @@ -4320,9 +4319,9 @@ checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" [[package]] name = "ruint" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -4337,7 +4336,7 @@ dependencies = [ "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.1", + "rand 0.9.2", "rlp", "ruint-macro", "serde", @@ -4363,9 +4362,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4399,22 +4398,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", @@ -4433,7 +4432,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.2.0", + "security-framework 3.3.0", ] [[package]] @@ -4447,9 +4446,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -4458,9 +4457,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" @@ -4575,12 +4574,12 @@ dependencies = [ [[package]] name = "secret-vault-value" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc32a777b53b3433b974c9c26b6d502a50037f8da94e46cb8ce2ced2cfdfaea0" +checksum = "662c7f8e99d46c9d3a87561d771a970c29efaccbab4bbdc6ab65d099d2358077" dependencies = [ - "prost 0.13.5", - "prost-types 0.13.5", + "prost 0.14.1", + "prost-types 0.14.1", "serde", "serde_json", "zeroize", @@ -4592,7 +4591,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4601,11 +4600,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -4666,14 +4665,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -4732,7 +4731,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4836,9 +4835,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -4861,15 +4860,15 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.12", + "thiserror 2.0.15", "time", ] [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -4964,24 +4963,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5003,9 +5001,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -5014,14 +5012,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ac494e7266fcdd2ad80bf4375d55d27a117ea5c866c26d0e97fe5b3caeeb75" +checksum = "a7a985ff4ffd7373e10e0fb048110fb11a162e5a4c47f92ddb8787a6f766b769" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5041,7 +5039,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5050,7 +5048,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5079,9 +5077,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tap_aggregator" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6f3c4d06f8d12d03cefc6db063c62e16f0d1567a7dc94a3d8f3109ecc05739" +checksum = "b8a883f8752e0054595101a9a1d069cf3238b101002bc66fce763dc5d121a179" dependencies = [ "anyhow", "axum", @@ -5112,18 +5110,18 @@ dependencies = [ [[package]] name = "tap_core" -version = "4.1.4" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3f0ea044210aa27b40d5a19069b10f3ece4929488d4f2a872ff0f02075d0d7" +checksum = "c282c3989f6062ccc09b1bfb6032f742fc6243507b68b82b64e1d72aea330531" dependencies = [ "anyhow", "async-trait", - "rand 0.9.1", + "rand 0.9.2", "tap_eip712_message", "tap_graph", "tap_receipt", "thegraph-core", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", ] @@ -5135,7 +5133,7 @@ checksum = "e648d9aafebc6835d1bb50398452a82a237539a60d8cb02f3541c1e2d291fc39" dependencies = [ "serde", "thegraph-core", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -5144,7 +5142,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff0f8dd640afe0d3a53a6b6ae58f628f0f99267d33f61b7a1264daa9d4307003" dependencies = [ - "rand 0.9.1", + "rand 0.9.2", "serde", "tap_eip712_message", "tap_receipt", @@ -5163,7 +5161,7 @@ dependencies = [ "serde", "tap_eip712_message", "thegraph-core", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -5189,7 +5187,7 @@ dependencies = [ "bs58", "serde", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -5229,11 +5227,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.15", ] [[package]] @@ -5244,18 +5242,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5343,9 +5341,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -5356,9 +5354,9 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2 0.5.10", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5369,7 +5367,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5419,9 +5417,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -5518,7 +5516,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5543,7 +5541,7 @@ dependencies = [ "prost-build", "prost-types 0.14.1", "quote", - "syn 2.0.104", + "syn 2.0.106", "tempfile", "tonic-build", ] @@ -5573,7 +5571,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytes", "futures-util", "http 1.3.1", @@ -5642,7 +5640,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5655,18 +5653,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "futures", - "futures-task", - "pin-project 1.1.10", - "tracing", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -5795,9 +5781,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -5883,7 +5869,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -5918,7 +5904,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6051,7 +6037,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6062,7 +6048,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6143,7 +6129,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -6179,10 +6165,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -6342,9 +6329,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -6365,7 +6352,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -6403,7 +6390,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6424,7 +6411,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6444,7 +6431,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6465,7 +6452,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6481,9 +6468,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -6498,7 +6485,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 76ed80bf..96e0ba7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.116", features = ["raw_value"] } serde_with = "3.8.1" snmalloc-rs = "0.3" -tap_aggregator = "0.5.8" +tap_aggregator = "0.5.9" tap_graph = { version = "0.3.4", features = ["v2"] } thegraph-core = { version = "0.15.1", features = [ "alloy-contract",