Skip to content

Commit ed9531b

Browse files
committed
test: update local test config
Signed-off-by: Joseph Livesey <[email protected]>
1 parent fe22bb9 commit ed9531b

File tree

9 files changed

+221
-47
lines changed

9 files changed

+221
-47
lines changed

contrib/tap-agent/start.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ serve_escrow_subgraph = false
132132
max_amount_willing_to_lose_grt = 1000
133133
134134
[tap.rav_request]
135-
timestamp_buffer_secs = 1000
135+
timestamp_buffer_secs = 10 # Reduced from 1000 to 10 seconds for testing
136136
137137
[tap.sender_aggregator_endpoints]
138138
${ACCOUNT0_ADDRESS} = "http://tap-aggregator:${TAP_AGGREGATOR}"

crates/service/src/middleware/auth/tap.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,42 @@ where
5555

5656
async move {
5757
let execute = || async {
58-
let receipt = receipt.ok_or(IndexerServiceError::ReceiptNotFound)?;
58+
let receipt = receipt.ok_or_else(|| {
59+
tracing::debug!(
60+
"TAP receipt validation failed: receipt not found in request extensions"
61+
);
62+
IndexerServiceError::ReceiptNotFound
63+
})?;
64+
65+
let version = match &receipt {
66+
TapReceipt::V1(_) => "V1",
67+
TapReceipt::V2(_) => "V2",
68+
};
69+
tracing::debug!(receipt_version = version, "Starting TAP receipt validation");
70+
5971
// Verify the receipt and store it in the database
6072
tap_manager
6173
.verify_and_store_receipt(&ctx.unwrap_or_default(), receipt)
6274
.await
63-
.inspect_err(|_| {
64-
if let Some(labels) = labels {
75+
.inspect_err(|err| {
76+
tracing::debug!(error = %err, receipt_version = version, "TAP receipt validation failed");
77+
if let Some(labels) = &labels {
6578
failed_receipt_metric
6679
.with_label_values(&labels.get_labels())
6780
.inc()
6881
}
6982
})?;
83+
84+
tracing::debug!(
85+
receipt_version = version,
86+
"TAP receipt validation successful"
87+
);
7088
Ok::<_, IndexerServiceError>(request)
7189
};
72-
execute().await.map_err(|error| error.into_response())
90+
execute().await.map_err(|error| {
91+
tracing::debug!(error = %error, "TAP authorization failed, returning HTTP error response");
92+
error.into_response()
93+
})
7394
}
7495
}
7596
}

crates/service/src/middleware/tap_receipt.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,21 @@ use crate::service::TapHeader;
1414
///
1515
/// This is useful to not deserialize multiple times the same receipt
1616
pub async fn receipt_middleware(mut request: Request, next: Next) -> Response {
17-
if let Ok(TypedHeader(TapHeader(receipt))) =
18-
request.extract_parts::<TypedHeader<TapHeader>>().await
19-
{
20-
request.extensions_mut().insert(receipt);
17+
match request.extract_parts::<TypedHeader<TapHeader>>().await {
18+
Ok(TypedHeader(TapHeader(receipt))) => {
19+
let version = match &receipt {
20+
crate::tap::TapReceipt::V1(_) => "V1",
21+
crate::tap::TapReceipt::V2(_) => "V2",
22+
};
23+
tracing::debug!(
24+
receipt_version = version,
25+
"TAP receipt extracted successfully"
26+
);
27+
request.extensions_mut().insert(receipt);
28+
}
29+
Err(e) => {
30+
tracing::debug!(error = %e, "No TAP receipt found in request headers");
31+
}
2132
}
2233
next.run(request).await
2334
}

crates/service/src/service/tap_receipt_header.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,40 @@ impl Header for TapHeader {
3131
{
3232
let mut execute = || -> anyhow::Result<TapHeader> {
3333
let raw_receipt = values.next().ok_or(headers::Error::invalid())?;
34+
tracing::debug!(
35+
raw_receipt_length = raw_receipt.len(),
36+
"Processing TAP receipt header"
37+
);
3438

3539
// we first try to decode a v2 receipt since it's cheaper and fail earlier than using
3640
// serde
3741
match BASE64_STANDARD.decode(raw_receipt) {
3842
Ok(raw_receipt) => {
39-
tracing::debug!("Decoded v2");
40-
let receipt = grpc::v2::SignedReceipt::decode(raw_receipt.as_ref())?;
41-
Ok(TapHeader(TapReceipt::V2(receipt.try_into()?)))
43+
tracing::debug!(
44+
decoded_length = raw_receipt.len(),
45+
"Successfully base64 decoded v2 receipt"
46+
);
47+
let receipt =
48+
grpc::v2::SignedReceipt::decode(raw_receipt.as_ref()).map_err(|e| {
49+
tracing::debug!(error = %e, "Failed to protobuf decode v2 receipt");
50+
e
51+
})?;
52+
tracing::debug!("Successfully protobuf decoded v2 receipt");
53+
let converted_receipt = receipt.try_into().map_err(|e: anyhow::Error| {
54+
tracing::debug!(error = %e, "Failed to convert v2 receipt");
55+
e
56+
})?;
57+
tracing::debug!("Successfully converted v2 receipt to TapReceipt::V2");
58+
Ok(TapHeader(TapReceipt::V2(converted_receipt)))
4259
}
43-
Err(_) => {
44-
tracing::debug!("Could not decode v2, trying v1");
60+
Err(e) => {
61+
tracing::debug!(error = %e, "Could not base64 decode v2 receipt, trying v1");
4562
let parsed_receipt: SignedReceipt =
46-
serde_json::from_slice(raw_receipt.as_ref())?;
63+
serde_json::from_slice(raw_receipt.as_ref()).map_err(|e| {
64+
tracing::debug!(error = %e, "Failed to JSON decode v1 receipt");
65+
e
66+
})?;
67+
tracing::debug!("Successfully decoded v1 receipt");
4768
Ok(TapHeader(TapReceipt::V1(parsed_receipt)))
4869
}
4970
}

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: 56 additions & 27 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,15 +241,29 @@ 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!(
248+
"📊 RAV trigger threshold: {} wei (0.002 GRT)",
249+
trigger_threshold
250+
);
251+
println!(
252+
"📊 Receipts needed for trigger: ~{} receipts at {} wei each",
253+
receipts_needed,
254+
MAX_RECEIPT_VALUE / 10
255+
);
256+
244257
println!("\n=== V2 STAGE 1: Sending large receipt batches with small pauses ===");
245258

246259
// Send multiple V2 receipts in two batches with a gap between them
247260
let mut total_successful = 0;
248261

249262
for batch in 0..BATCHES {
250263
println!(
251-
"Sending V2 batch {} of 2 with {} receipts each...",
264+
"Sending V2 batch {} of {} with {} receipts each...",
252265
batch + 1,
266+
BATCHES,
253267
NUM_RECEIPTS
254268
);
255269

@@ -267,16 +281,17 @@ pub async fn test_tap_rav_v2() -> Result<()> {
267281

268282
let receipt_encoded = encode_v2_receipt(&receipt)?;
269283

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

281296
if response.status().is_success() {
282297
total_successful += 1;
@@ -298,15 +313,22 @@ pub async fn test_tap_rav_v2() -> Result<()> {
298313

299314
// Check metrics after batch
300315
let batch_metrics = metrics_checker.get_current_metrics().await?;
316+
let current_unaggregated =
317+
batch_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string());
318+
let trigger_threshold = 2_000_000_000_000_000u128;
319+
let progress_pct =
320+
(current_unaggregated as f64 / trigger_threshold as f64 * 100.0).min(100.0);
321+
301322
println!(
302-
"After V2 batch {}: RAVs created: {}, Unaggregated fees: {}",
323+
"After V2 batch {}: RAVs created: {}, Unaggregated fees: {} ({:.1}% of trigger threshold)",
303324
batch + 1,
304325
batch_metrics.ravs_created_by_allocation(&allocation_id.to_string()),
305-
batch_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string())
326+
current_unaggregated,
327+
progress_pct
306328
);
307329

308330
// Wait between batches - long enough for first batch to exit buffer
309-
if batch < 1 {
331+
if batch < BATCHES - 1 {
310332
println!("Waiting for buffer period + 5s...");
311333
tokio::time::sleep(Duration::from_secs(WAIT_TIME_BATCHES)).await;
312334
}
@@ -331,16 +353,17 @@ pub async fn test_tap_rav_v2() -> Result<()> {
331353

332354
let receipt_encoded = encode_v2_receipt(&receipt)?;
333355

334-
let response = create_request(
335-
&http_client,
336-
&format!("{INDEXER_URL}/subgraphs/id/{SUBGRAPH_ID}"),
337-
&receipt_encoded,
338-
&json!({
356+
let response = http_client
357+
.post(format!("{GATEWAY_URL}/api/subgraphs/id/{SUBGRAPH_ID}"))
358+
.header("Content-Type", "application/json")
359+
.header("Authorization", format!("Bearer {GATEWAY_API_KEY}"))
360+
.header("Tap-Receipt", receipt_encoded)
361+
.json(&json!({
339362
"query": "{ _meta { block { number } } }"
340-
}),
341-
)
342-
.send()
343-
.await?;
363+
}))
364+
.timeout(Duration::from_secs(10))
365+
.send()
366+
.await?;
344367

345368
if response.status().is_success() {
346369
total_successful += 1;
@@ -361,11 +384,17 @@ pub async fn test_tap_rav_v2() -> Result<()> {
361384
let current_unaggregated =
362385
current_metrics.unaggregated_fees_by_allocation(&allocation_id.to_string());
363386

387+
// Calculate progress toward trigger threshold
388+
let trigger_threshold = 2_000_000_000_000_000u128;
389+
let progress_pct =
390+
(current_unaggregated as f64 / trigger_threshold as f64 * 100.0).min(100.0);
391+
364392
println!(
365-
"After V2 trigger {}: RAVs created: {}, Unaggregated fees: {}",
393+
"After V2 trigger {}: RAVs created: {}, Unaggregated fees: {} ({:.1}% of trigger threshold)",
366394
i + 1,
367395
current_ravs_created,
368-
current_unaggregated
396+
current_unaggregated,
397+
progress_pct
369398
);
370399

371400
// 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+
// Test to verify V2 signature creation and recovery works correctly
2+
3+
use anyhow::Result;
4+
use std::str::FromStr;
5+
use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain};
6+
use tap_graph::v2::Receipt as V2Receipt;
7+
use thegraph_core::{
8+
alloy::{primitives::Address, signers::local::PrivateKeySigner},
9+
CollectionId,
10+
};
11+
12+
use crate::constants::{
13+
ACCOUNT0_SECRET, CHAIN_ID, GRAPH_TALLY_COLLECTOR_CONTRACT, TEST_DATA_SERVICE,
14+
};
15+
16+
pub async fn test_v2_signature_recovery() -> Result<()> {
17+
println!("=== V2 Signature Recovery Test ===");
18+
19+
let wallet: PrivateKeySigner = ACCOUNT0_SECRET.parse()?;
20+
let wallet_address = wallet.address();
21+
println!("Wallet address: {:?}", wallet_address);
22+
23+
// Create EIP-712 domain - V2 uses GraphTallyCollector
24+
let domain = tap_eip712_domain(CHAIN_ID, Address::from_str(GRAPH_TALLY_COLLECTOR_CONTRACT)?);
25+
println!(
26+
"Using domain: chain_id={}, verifier={}",
27+
CHAIN_ID, GRAPH_TALLY_COLLECTOR_CONTRACT
28+
);
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+
}

0 commit comments

Comments
 (0)