Skip to content

Commit d421bf9

Browse files
committed
test: mount tap-contracts to gateway in local-network setup
1 parent 1770db4 commit d421bf9

File tree

6 files changed

+163
-37
lines changed

6 files changed

+163
-37
lines changed

integration-tests/src/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ pub const TAP_AGENT_METRICS_URL: &str = "http://localhost:7300/metrics";
1717
// and the signing key account0_secret
1818
// they must match otherwise receipts would be rejected
1919
pub const TAP_VERIFIER_CONTRACT: &str = "0xC9a43158891282A2B1475592D5719c001986Aaec";
20+
21+
// V2 GraphTallyCollector contract address (for Horizon receipts)
22+
pub const GRAPH_TALLY_COLLECTOR_CONTRACT: &str = "0xB0D4afd8879eD9F52b28595d31B441D079B2Ca07";
2023
pub const ACCOUNT0_SECRET: &str =
2124
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
2225
pub const CHAIN_ID: u64 = 1337;

integration-tests/src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod constants;
55
mod load_test;
66
mod metrics;
77
mod rav_tests;
8+
mod signature_test;
89
mod utils;
910

1011
use anyhow::Result;
@@ -39,6 +40,9 @@ enum Commands {
3940
#[clap(long, short, value_parser)]
4041
num_receipts: usize,
4142
},
43+
44+
#[clap(name = "debug")]
45+
Debug,
4246
}
4347

4448
#[tokio::main]
@@ -65,6 +69,10 @@ async fn main() -> Result<()> {
6569
let concurrency = num_cpus::get();
6670
receipt_handler_load_test_v2(num_receipts, concurrency).await?;
6771
}
72+
// cargo run -- debug
73+
Commands::Debug => {
74+
signature_test::test_v2_signature_recovery().await?;
75+
}
6876
}
6977

7078
Ok(())

integration-tests/src/rav_tests.rs

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ use crate::{
2121
};
2222

2323
const WAIT_TIME_BATCHES: u64 = 40;
24-
const NUM_RECEIPTS: u32 = 3;
24+
const NUM_RECEIPTS: u32 = 30; // Increased to 30 receipts per batch
2525

2626
// Send receipts in batches with a delay in between
2727
// to ensure some receipts get outside the timestamp buffer
28-
const BATCHES: u32 = 2;
29-
const MAX_TRIGGERS: usize = 100;
28+
const BATCHES: u32 = 15; // Increased to 15 batches for total 450 receipts in Stage 1
29+
const MAX_TRIGGERS: usize = 200; // Increased trigger attempts to 200
3030

3131
// Function to test the tap RAV generation
3232
pub async fn test_tap_rav_v1() -> Result<()> {
@@ -241,17 +241,23 @@ pub async fn test_tap_rav_v2() -> Result<()> {
241241
"\n=== V2 Initial metrics: RAVs created: {initial_ravs_created}, Unaggregated fees: {initial_unaggregated} ==="
242242
);
243243

244+
// Calculate expected thresholds
245+
let trigger_threshold = 2_000_000_000_000_000u128; // 0.002 GRT trigger value
246+
let receipts_needed = trigger_threshold / (MAX_RECEIPT_VALUE / 10); // Using trigger receipt value
247+
println!("📊 RAV trigger threshold: {trigger_threshold} wei (0.002 GRT)",);
248+
let receipt_value = MAX_RECEIPT_VALUE / 10;
249+
println!(
250+
"📊 Receipts needed for trigger: ~{receipts_needed} receipts at {receipt_value} wei each",
251+
);
252+
244253
println!("\n=== V2 STAGE 1: Sending large receipt batches with small pauses ===");
245254

246255
// Send multiple V2 receipts in two batches with a gap between them
247256
let mut total_successful = 0;
248257

249258
for batch in 0..BATCHES {
250-
println!(
251-
"Sending V2 batch {} of 2 with {} receipts each...",
252-
batch + 1,
253-
NUM_RECEIPTS
254-
);
259+
let batch = batch + 1;
260+
println!("Sending V2 batch {batch} of {BATCHES} with {NUM_RECEIPTS} receipts each...",);
255261

256262
for i in 0..NUM_RECEIPTS {
257263
// Create V2 receipt
@@ -267,16 +273,17 @@ pub async fn test_tap_rav_v2() -> Result<()> {
267273

268274
let receipt_encoded = encode_v2_receipt(&receipt)?;
269275

270-
let response = create_request(
271-
&http_client,
272-
&format!("{INDEXER_URL}/subgraphs/id/{SUBGRAPH_ID}"),
273-
&receipt_encoded,
274-
&json!({
276+
let response = http_client
277+
.post(format!("{GATEWAY_URL}/api/subgraphs/id/{SUBGRAPH_ID}"))
278+
.header("Content-Type", "application/json")
279+
.header("Authorization", format!("Bearer {GATEWAY_API_KEY}"))
280+
.header("Tap-Receipt", receipt_encoded)
281+
.json(&json!({
275282
"query": "{ _meta { block { number } } }"
276-
}),
277-
)
278-
.send()
279-
.await?;
283+
}))
284+
.timeout(Duration::from_secs(10))
285+
.send()
286+
.await?;
280287

281288
if response.status().is_success() {
282289
total_successful += 1;
@@ -298,15 +305,22 @@ pub async fn test_tap_rav_v2() -> Result<()> {
298305

299306
// Check metrics after batch
300307
let batch_metrics = metrics_checker.get_current_metrics().await?;
308+
let current_unaggregated =
309+
batch_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string());
310+
let trigger_threshold = 2_000_000_000_000_000u128;
311+
let progress_pct =
312+
(current_unaggregated as f64 / trigger_threshold as f64 * 100.0).min(100.0);
313+
301314
println!(
302-
"After V2 batch {}: RAVs created: {}, Unaggregated fees: {}",
315+
"After V2 batch {}: RAVs created: {}, Unaggregated fees: {} ({:.1}% of trigger threshold)",
303316
batch + 1,
304317
batch_metrics.ravs_created_by_allocation(&allocation_id.to_string()),
305-
batch_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string())
318+
current_unaggregated,
319+
progress_pct
306320
);
307321

308322
// Wait between batches - long enough for first batch to exit buffer
309-
if batch < 1 {
323+
if batch < BATCHES - 1 {
310324
println!("Waiting for buffer period + 5s...");
311325
tokio::time::sleep(Duration::from_secs(WAIT_TIME_BATCHES)).await;
312326
}
@@ -331,16 +345,17 @@ pub async fn test_tap_rav_v2() -> Result<()> {
331345

332346
let receipt_encoded = encode_v2_receipt(&receipt)?;
333347

334-
let response = create_request(
335-
&http_client,
336-
&format!("{INDEXER_URL}/subgraphs/id/{SUBGRAPH_ID}"),
337-
&receipt_encoded,
338-
&json!({
348+
let response = http_client
349+
.post(format!("{GATEWAY_URL}/api/subgraphs/id/{SUBGRAPH_ID}"))
350+
.header("Content-Type", "application/json")
351+
.header("Authorization", format!("Bearer {GATEWAY_API_KEY}"))
352+
.header("Tap-Receipt", receipt_encoded)
353+
.json(&json!({
339354
"query": "{ _meta { block { number } } }"
340-
}),
341-
)
342-
.send()
343-
.await?;
355+
}))
356+
.timeout(Duration::from_secs(10))
357+
.send()
358+
.await?;
344359

345360
if response.status().is_success() {
346361
total_successful += 1;
@@ -361,11 +376,17 @@ pub async fn test_tap_rav_v2() -> Result<()> {
361376
let current_unaggregated =
362377
current_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string());
363378

379+
// Calculate progress toward trigger threshold
380+
let trigger_threshold = 2_000_000_000_000_000u128;
381+
let progress_pct =
382+
(current_unaggregated as f64 / trigger_threshold as f64 * 100.0).min(100.0);
383+
364384
println!(
365-
"After V2 trigger {}: RAVs created: {}, Unaggregated fees: {}",
385+
"After V2 trigger {}: RAVs created: {}, Unaggregated fees: {} ({:.1}% of trigger threshold)",
366386
i + 1,
367387
current_ravs_created,
368-
current_unaggregated
388+
current_unaggregated,
389+
progress_pct
369390
);
370391

371392
// If we've succeeded, exit early
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Test to verify V2 signature creation and recovery works correctly
5+
6+
use anyhow::Result;
7+
use std::str::FromStr;
8+
use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain};
9+
use tap_graph::v2::Receipt as V2Receipt;
10+
use thegraph_core::{
11+
alloy::{primitives::Address, signers::local::PrivateKeySigner},
12+
CollectionId,
13+
};
14+
15+
use crate::constants::{
16+
ACCOUNT0_SECRET, CHAIN_ID, GRAPH_TALLY_COLLECTOR_CONTRACT, TEST_DATA_SERVICE,
17+
};
18+
19+
pub async fn test_v2_signature_recovery() -> Result<()> {
20+
println!("=== V2 Signature Recovery Test ===");
21+
22+
let wallet: PrivateKeySigner = ACCOUNT0_SECRET.parse()?;
23+
let wallet_address = wallet.address();
24+
println!("Wallet address: {wallet_address:?}");
25+
26+
// Create EIP-712 domain - V2 uses GraphTallyCollector
27+
let domain = tap_eip712_domain(CHAIN_ID, Address::from_str(GRAPH_TALLY_COLLECTOR_CONTRACT)?);
28+
println!("Using domain: chain_id={CHAIN_ID}, verifier={GRAPH_TALLY_COLLECTOR_CONTRACT}");
29+
30+
// Create a V2 receipt
31+
let allocation_id = Address::from_str("0xc172ed1c6470dfa3b12a789317dda50cdd8b85df")?;
32+
let collection_id = CollectionId::from(allocation_id);
33+
let payer = wallet_address;
34+
let service_provider = allocation_id;
35+
let data_service = Address::from_str(TEST_DATA_SERVICE)?;
36+
37+
println!("V2 Receipt parameters:");
38+
println!(" Collection ID: {collection_id:?}");
39+
println!(" Payer: {payer:?}");
40+
println!(" Service provider: {service_provider:?}");
41+
println!(" Data service: {data_service:?}");
42+
43+
let receipt = V2Receipt::new(
44+
*collection_id,
45+
payer,
46+
data_service,
47+
service_provider,
48+
100_000_000_000_000_000u128, // 0.1 GRT
49+
)?;
50+
51+
// Sign the receipt
52+
let signed_receipt = Eip712SignedMessage::new(&domain, receipt, &wallet)?;
53+
println!("Receipt signed successfully");
54+
55+
// Recover the signer
56+
let recovered_signer = signed_receipt.recover_signer(&domain)?;
57+
println!("Recovered signer: {recovered_signer:?}");
58+
println!("Expected signer: {wallet_address:?}");
59+
60+
// Check if they match
61+
if recovered_signer == wallet_address {
62+
println!("✅ SUCCESS: Signature recovery matches wallet address");
63+
Ok(())
64+
} else {
65+
println!("❌ FAILURE: Signature recovery mismatch!");
66+
println!(" Expected: {wallet_address:?}");
67+
println!(" Got: {recovered_signer:?}");
68+
Err(anyhow::anyhow!("Signature recovery failed for V2 receipt"))
69+
}
70+
}

integration-tests/src/utils.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use tap_graph::Receipt;
1919
use thegraph_core::alloy::{primitives::Address, signers::local::PrivateKeySigner};
2020
use thegraph_core::CollectionId;
2121

22-
use crate::constants::TEST_DATA_SERVICE;
22+
use crate::constants::{GRAPH_TALLY_COLLECTOR_CONTRACT, TEST_DATA_SERVICE};
2323

2424
pub fn create_tap_receipt(
2525
value: u128,
@@ -58,8 +58,8 @@ pub fn create_tap_receipt(
5858

5959
pub fn create_tap_receipt_v2(
6060
value: u128,
61-
allocation_id: &Address, // Used to derive collection_id in V2
62-
verifier_contract: &str,
61+
allocation_id: &Address, // Used to derive collection_id in V2
62+
_verifier_contract: &str, // V2 uses GraphTallyCollector, not TAPVerifier
6363
chain_id: u64,
6464
wallet: &PrivateKeySigner,
6565
payer: &Address,
@@ -77,12 +77,21 @@ pub fn create_tap_receipt_v2(
7777
// For the migration period, we derive collection_id from allocation_id
7878
let collection_id = CollectionId::from(*allocation_id);
7979

80-
// Create domain separator
80+
// Create domain separator - V2 uses GraphTallyCollector
8181
let eip712_domain_separator =
82-
tap_eip712_domain(chain_id, Address::from_str(verifier_contract)?);
82+
tap_eip712_domain(chain_id, Address::from_str(GRAPH_TALLY_COLLECTOR_CONTRACT)?);
8383

84+
let wallet_address = wallet.address();
8485
// Create and sign V2 receipt
8586
println!("Creating and signing V2 receipt...");
87+
println!("V2 Receipt details:");
88+
println!(" Payer (from wallet): {payer:?}");
89+
println!(" Service provider: {service_provider:?}");
90+
println!(" Data service: {TEST_DATA_SERVICE}");
91+
println!(" Collection ID: {collection_id:?}");
92+
println!(" Wallet address: {wallet_address:?}");
93+
println!(" Using GraphTallyCollector: {GRAPH_TALLY_COLLECTOR_CONTRACT}");
94+
8695
let receipt = Eip712SignedMessage::new(
8796
&eip712_domain_separator,
8897
tap_graph::v2::Receipt {

setup-test-network.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,26 @@ source local-network/.env
260260
docker build -t local-gateway:latest ./local-network/gateway
261261

262262
echo "Running gateway container..."
263-
# Updated to use the horizon file structure
263+
# Verify required files exist before starting gateway
264+
if [ ! -f "local-network/horizon.json" ]; then
265+
echo "ERROR: local-network/horizon.json not found!"
266+
exit 1
267+
fi
268+
if [ ! -f "local-network/tap-contracts.json" ]; then
269+
echo "ERROR: local-network/tap-contracts.json not found!"
270+
exit 1
271+
fi
272+
if [ ! -f "local-network/subgraph-service.json" ]; then
273+
echo "ERROR: local-network/subgraph-service.json not found!"
274+
exit 1
275+
fi
276+
277+
# Updated to use the horizon file structure and include tap-contracts.json
264278
docker run -d --name gateway \
265279
--network local-network_default \
266280
-p 7700:7700 \
267281
-v "$(pwd)/local-network/horizon.json":/opt/horizon.json:ro \
282+
-v "$(pwd)/local-network/tap-contracts.json":/opt/tap-contracts.json:ro \
268283
-v "$(pwd)/local-network/subgraph-service.json":/opt/subgraph-service.json:ro \
269284
-v "$(pwd)/local-network/.env":/opt/.env:ro \
270285
-e RUST_LOG=info,graph_gateway=trace \

0 commit comments

Comments
 (0)