|
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | 4 | use anyhow::Result; |
5 | | -use reqwest::Client; |
6 | | -use serde_json::json; |
7 | | -use std::str::FromStr; |
8 | | -use std::sync::Arc; |
9 | | -use std::time::Duration; |
10 | | -use thegraph_core::alloy::signers::local::coins_bip39::English; |
11 | | -use thegraph_core::alloy::{ |
12 | | - primitives::Address, |
13 | | - signers::local::{MnemonicBuilder, PrivateKeySigner}, |
14 | | -}; |
15 | 5 |
|
16 | 6 | mod metrics; |
17 | 7 | mod receipt; |
18 | | -use metrics::MetricsChecker; |
19 | | -use receipt::create_tap_receipt; |
20 | | - |
21 | | -// TODO: Would be nice to read this values from: |
22 | | -// contrib/tap-agent/config.toml |
23 | | -// and contrib/local-network/.env |
24 | | -const GATEWAY_URL: &str = "http://localhost:7700"; |
25 | | -const SUBGRAPH_ID: &str = "BFr2mx7FgkJ36Y6pE5BiXs1KmNUmVDCnL82KUSdcLW1g"; |
26 | | -const TAP_ESCROW_CONTRACT: &str = "0x0355B7B8cb128fA5692729Ab3AAa199C1753f726"; |
27 | | -const GATEWAY_API_KEY: &str = "deadbeefdeadbeefdeadbeefdeadbeef"; |
28 | | -// const RECEIVER_ADDRESS: &str = "0xf4EF6650E48d099a4972ea5B414daB86e1998Bd3"; |
29 | | -const TAP_AGENT_METRICS_URL: &str = "http://localhost:7300/metrics"; |
30 | | - |
31 | | -const MNEMONIC: &str = "test test test test test test test test test test test junk"; |
32 | | -const GRAPH_URL: &str = "http://localhost:8000/subgraphs/name/graph-network"; |
| 8 | +mod rav_tests; |
33 | 9 |
|
34 | | -const GRT_DECIMALS: u8 = 18; |
35 | | -const GRT_BASE: u128 = 10u128.pow(GRT_DECIMALS as u32); |
36 | | - |
37 | | -// With trigger_value_divisor = 500_000 and max_amount_willing_to_lose_grt = 1000 |
38 | | -// trigger_value = 0.002 GRT |
39 | | -// We need to send at least 20 receipts to reach the trigger threshold |
40 | | -// Sending slightly more than required to ensure triggering |
41 | | -const MAX_RECEIPT_VALUE: u128 = GRT_BASE / 1_000; |
42 | | -// This value should match the timestamp_buffer_secs |
43 | | -// in the tap-agent setting + 10 seconds |
44 | | -const WAIT_TIME_BATCHES: u64 = 40; |
45 | | - |
46 | | -// Bellow constant is to define the number of receipts |
47 | | -const NUM_RECEIPTS: u32 = 70; |
| 10 | +use rav_tests::test_rav_generation; |
| 11 | +use receipt::create_tap_receipt; |
48 | 12 |
|
49 | | -// Send receipts in batches with a delay in between |
50 | | -// to ensure some receipts get outside the timestamp buffer |
51 | | -const BATCHES: u32 = 2; |
52 | | -const MAX_TRIGGERS: usize = 100; |
53 | 13 |
|
54 | 14 | #[tokio::main] |
55 | 15 | async fn main() -> Result<()> { |
56 | | - // Run the TAP receipt test |
57 | | - tap_rav_test().await |
58 | | -} |
59 | | - |
60 | | -async fn tap_rav_test() -> Result<()> { |
61 | | - // Setup wallet using your MnemonicBuilder |
62 | | - let index: u32 = 0; |
63 | | - let wallet: PrivateKeySigner = MnemonicBuilder::<English>::default() |
64 | | - .phrase(MNEMONIC) |
65 | | - .index(index) |
66 | | - .unwrap() |
67 | | - .build() |
68 | | - .unwrap(); |
69 | | - |
70 | | - let sender_address = wallet.address(); |
71 | | - println!("Using sender address: {}", sender_address); |
72 | | - |
73 | | - // Setup HTTP client |
74 | | - let http_client = Arc::new(Client::new()); |
75 | | - |
76 | | - // Query the network subgraph to find active allocations |
77 | | - println!("Querying for active allocations..."); |
78 | | - let response = http_client |
79 | | - .post(GRAPH_URL) |
80 | | - .json(&json!({ |
81 | | - "query": "{ allocations(where: { status: Active }) { id indexer { id } subgraphDeployment { id } } }" |
82 | | - })) |
83 | | - .send() |
84 | | - .await?; |
85 | | - |
86 | | - if !response.status().is_success() { |
87 | | - return Err(anyhow::anyhow!( |
88 | | - "Network subgraph request failed with status: {}", |
89 | | - response.status() |
90 | | - )); |
91 | | - } |
92 | | - |
93 | | - // Try to find a valid allocation |
94 | | - let response_text = response.text().await?; |
95 | | - println!("Network subgraph response: {}", response_text); |
96 | | - |
97 | | - let json_value = serde_json::from_str::<serde_json::Value>(&response_text)?; |
98 | | - let allocation_id = json_value |
99 | | - .get("data") |
100 | | - .and_then(|d| d.get("allocations")) |
101 | | - .and_then(|a| a.as_array()) |
102 | | - .filter(|arr| !arr.is_empty()) |
103 | | - .and_then(|arr| arr[0].get("id")) |
104 | | - .and_then(|id| id.as_str()) |
105 | | - .ok_or_else(|| anyhow::anyhow!("No valid allocation ID found"))?; |
106 | | - |
107 | | - let allocation_id = Address::from_str(allocation_id)?; |
108 | | - |
109 | | - // Create a metrics checker |
110 | | - let metrics_checker = |
111 | | - MetricsChecker::new(http_client.clone(), TAP_AGENT_METRICS_URL.to_string()); |
112 | | - |
113 | | - // First check initial metrics |
114 | | - let initial_metrics = metrics_checker.get_current_metrics().await?; |
115 | | - let initial_ravs_created = |
116 | | - initial_metrics.ravs_created_by_allocation(&allocation_id.to_string()); |
117 | | - let initial_unaggregated = |
118 | | - initial_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string()); |
119 | | - |
120 | | - println!( |
121 | | - "\n=== Initial metrics: RAVs created: {}, Unaggregated fees: {} ===", |
122 | | - initial_ravs_created, initial_unaggregated |
123 | | - ); |
124 | | - |
125 | | - // Calculate required receipts to trigger RAV |
126 | | - // With MAX_RECEIPT_VALUE = GRT_BASE / 1_000 (0.001 GRT) |
127 | | - // And trigger_value = 0.002 GRT |
128 | | - // We need at least 3 receipts to trigger a RAV (0.003 GRT > 0.002 GRT) |
129 | | - const RECEIPTS_NEEDED: u32 = 3; |
130 | | - |
131 | | - println!("\n=== STAGE 1: Sending large receipt batches with small pauses ==="); |
132 | | - |
133 | | - // Send multiple receipts in two batches with a gap between them |
134 | | - let mut total_successful = 0; |
135 | 16 |
|
136 | | - for batch in 0..2 { |
137 | | - println!( |
138 | | - "Sending batch {} of 2 with {} receipts each...", |
139 | | - batch + 1, |
140 | | - RECEIPTS_NEEDED |
141 | | - ); |
142 | | - |
143 | | - for i in 0..RECEIPTS_NEEDED { |
144 | | - let receipt = create_tap_receipt( |
145 | | - MAX_RECEIPT_VALUE, |
146 | | - &allocation_id, |
147 | | - TAP_ESCROW_CONTRACT, |
148 | | - &wallet, |
149 | | - )?; |
150 | | - |
151 | | - let receipt_json = serde_json::to_string(&receipt).unwrap(); |
152 | | - |
153 | | - let response = http_client |
154 | | - .post(format!("{}/api/subgraphs/id/{}", GATEWAY_URL, SUBGRAPH_ID)) |
155 | | - .header("Content-Type", "application/json") |
156 | | - .header("Authorization", format!("Bearer {}", GATEWAY_API_KEY)) |
157 | | - .header("Tap-Receipt", receipt_json) |
158 | | - .json(&json!({ |
159 | | - "query": "{ _meta { block { number } } }" |
160 | | - })) |
161 | | - .timeout(Duration::from_secs(10)) |
162 | | - .send() |
163 | | - .await?; |
164 | | - |
165 | | - if response.status().is_success() { |
166 | | - total_successful += 1; |
167 | | - println!("Receipt {} of batch {} sent successfully", i + 1, batch + 1); |
168 | | - } else { |
169 | | - println!("Failed to send receipt: {}", response.status()); |
170 | | - } |
171 | | - |
172 | | - // Small pause between receipts within batch |
173 | | - tokio::time::sleep(Duration::from_millis(100)).await; |
174 | | - } |
175 | | - |
176 | | - // Check metrics after batch |
177 | | - let batch_metrics = metrics_checker.get_current_metrics().await?; |
178 | | - println!( |
179 | | - "After batch {}: RAVs created: {}, Unaggregated fees: {}", |
180 | | - batch + 1, |
181 | | - batch_metrics.ravs_created_by_allocation(&allocation_id.to_string()), |
182 | | - batch_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string()) |
183 | | - ); |
184 | | - |
185 | | - // Wait between batches - long enough for first batch to exit buffer |
186 | | - if batch < 1 { |
187 | | - println!("Waiting for buffer period + 5s..."); |
188 | | - tokio::time::sleep(Duration::from_secs(WAIT_TIME_BATCHES + 5)).await; |
189 | | - } |
190 | | - } |
191 | | - |
192 | | - println!("\n=== STAGE 2: Sending continuous trigger receipts ==="); |
193 | | - |
194 | | - // Now send a series of regular receipts with short intervals until RAV is detected |
195 | | - for i in 0..MAX_TRIGGERS { |
196 | | - println!("Sending trigger receipt {}/{}...", i + 1, MAX_TRIGGERS); |
197 | | - |
198 | | - let trigger_receipt = create_tap_receipt( |
199 | | - MAX_RECEIPT_VALUE, |
200 | | - &allocation_id, |
201 | | - TAP_ESCROW_CONTRACT, |
202 | | - &wallet, |
203 | | - )?; |
204 | | - |
205 | | - let receipt_json = serde_json::to_string(&trigger_receipt).unwrap(); |
206 | | - |
207 | | - let response = http_client |
208 | | - .post(format!("{}/api/subgraphs/id/{}", GATEWAY_URL, SUBGRAPH_ID)) |
209 | | - .header("Content-Type", "application/json") |
210 | | - .header("Authorization", format!("Bearer {}", GATEWAY_API_KEY)) |
211 | | - .header("Tap-Receipt", receipt_json) |
212 | | - .json(&json!({ |
213 | | - "query": "{ _meta { block { number } } }" |
214 | | - })) |
215 | | - .timeout(Duration::from_secs(10)) |
216 | | - .send() |
217 | | - .await?; |
218 | | - |
219 | | - if response.status().is_success() { |
220 | | - total_successful += 1; |
221 | | - println!("Trigger receipt {} sent successfully", i + 1); |
222 | | - } else { |
223 | | - return Err(anyhow::anyhow!( |
224 | | - "Failed to send trigger receipt: {}", |
225 | | - response.status() |
226 | | - )); |
227 | | - } |
228 | | - |
229 | | - // Check after each trigger |
230 | | - tokio::time::sleep(Duration::from_secs(5)).await; |
231 | | - |
232 | | - let current_metrics = metrics_checker.get_current_metrics().await?; |
233 | | - let current_ravs_created = |
234 | | - current_metrics.ravs_created_by_allocation(&allocation_id.to_string()); |
235 | | - let current_unaggregated = |
236 | | - current_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string()); |
237 | | - |
238 | | - println!( |
239 | | - "After trigger {}: RAVs created: {}, Unaggregated fees: {}", |
240 | | - i + 1, |
241 | | - current_ravs_created, |
242 | | - current_unaggregated |
243 | | - ); |
244 | | - |
245 | | - // If we've succeeded, exit early |
246 | | - if current_ravs_created > initial_ravs_created { |
247 | | - println!( |
248 | | - "✅ TEST PASSED: RAVs created increased from {} to {}!", |
249 | | - initial_ravs_created, current_ravs_created |
250 | | - ); |
251 | | - return Ok(()); |
252 | | - } |
253 | | - |
254 | | - if current_unaggregated < initial_unaggregated * 0.9 { |
255 | | - println!( |
256 | | - "✅ TEST PASSED: Unaggregated fees decreased significantly from {} to {}!", |
257 | | - initial_unaggregated, current_unaggregated |
258 | | - ); |
259 | | - return Ok(()); |
260 | | - } |
261 | | - } |
262 | | - |
263 | | - println!("\n=== Summary ==="); |
264 | | - println!("Total receipts sent successfully: {}", total_successful); |
265 | | - println!( |
266 | | - "Total value sent: {} GRT", |
267 | | - (MAX_RECEIPT_VALUE as f64 * total_successful as f64) / GRT_BASE as f64 |
268 | | - ); |
269 | | - |
270 | | - // If we got here, test failed |
271 | | - println!("❌ TEST FAILED: No RAV generation detected"); |
272 | | - Err(anyhow::anyhow!("Failed to detect RAV generation")) |
| 17 | + // Run the TAP receipt test |
| 18 | + test_rav_generation().await |
273 | 19 | } |
0 commit comments