upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-01-10 00:11:20 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-01-10 00:11:20 +0000
commitced6ef15a1e2ff4babd8a291f6d423846d986302 (patch)
treea51554146164490213c5c241fd2f2ab4ba797ef4
parentbbdc68cd9a04cdabfa4a1fffd507048f555d5fa5 (diff)
fix: detect NIP-77 NOTICE immediately during negentropy sync
Previously, when a relay didn't support NIP-77, the negentropy_sync_diff function would wait for the full client.sync() timeout even after receiving a NOTICE message that marked the relay as not supporting NIP-77. This change uses tokio::select! to race the sync operation against a polling task that checks the nip77_supported flag every 10ms. When a NOTICE is received (detected in the message handler), the poll task detects the status change and immediately returns an error, allowing quick fallback to REQ+EOSE without waiting for timeouts. Benefits: - Fast failure (within 10ms) when relay sends NIP-77 NOTICE - No artificial timeout reduction that could hurt legitimate operations - Maintains full timeout for relays that actually support NIP-77
-rw-r--r--src/sync/relay_connection.rs77
1 files changed, 50 insertions, 27 deletions
diff --git a/src/sync/relay_connection.rs b/src/sync/relay_connection.rs
index f19abb9..99fc4ea 100644
--- a/src/sync/relay_connection.rs
+++ b/src/sync/relay_connection.rs
@@ -509,42 +509,65 @@ impl RelayConnection {
509 // Use dry_run to only identify differences without downloading events 509 // Use dry_run to only identify differences without downloading events
510 let sync_opts = SyncOptions::default().dry_run(); 510 let sync_opts = SyncOptions::default().dry_run();
511 511
512 match self.client.sync(filter.clone(), &sync_opts).await { 512 // Clone the atomic for the polling task
513 Ok(output) => { 513 let nip77_status = self.nip77_supported.clone();
514 let reconciliation = output.val; 514 let url = self.url.clone();
515
516 // Create a polling task that checks if NIP-77 support was detected as unavailable
517 let poll_task = async move {
518 loop {
519 let status = nip77_status.load(std::sync::atomic::Ordering::Relaxed);
520 if status == 2 {
521 // Relay confirmed not to support NIP-77 (via NOTICE or other means)
522 return Err(format!(
523 "Relay {} does not support NIP-77 (detected via NOTICE)",
524 url
525 ));
526 }
527 tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
528 }
529 };
515 530
531 // Race the sync operation against the polling task
532 let sync_task = self.client.sync(filter.clone(), &sync_opts);
533
534 let result = tokio::select! {
535 poll_result = poll_task => {
536 // Polling detected NIP-77 not supported
537 poll_result
538 }
539 sync_result = sync_task => {
540 // Sync completed (or failed) first
541 match sync_result {
542 Ok(output) => {
543 // Check for any failures
544 // Note: Timeouts are common for relays without NIP-77 support
545 if !output.failed.is_empty() {
546 Err(format!("Negentropy diff had failures: {:?}", output.failed))
547 } else {
548 Ok(output.val)
549 }
550 }
551 Err(e) => Err(format!("Negentropy diff failed: {}", e))
552 }
553 }
554 };
555
556 match result {
557 Ok(reconciliation) => {
516 tracing::debug!( 558 tracing::debug!(
517 relay = %self.url, 559 relay = %self.url,
518 local_count = reconciliation.local.len(), 560 local_count = reconciliation.local.len(),
519 remote_count = reconciliation.remote.len(), 561 remote_count = reconciliation.remote.len(),
520 "Negentropy diff completed (dry run)" 562 "Negentropy diff completed (dry run)"
521 ); 563 );
522
523 // Check for any failures
524 // Note: Timeouts are common for relays without NIP-77 support
525 if !output.failed.is_empty() {
526 // Mark relay as not supporting NIP-77 (timeout typically means no support)
527 self.nip77_supported
528 .store(2, std::sync::atomic::Ordering::Relaxed);
529
530 // Log warning only once per relay to avoid spam
531 if !self
532 .nip77_warning_logged
533 .swap(true, std::sync::atomic::Ordering::Relaxed)
534 {
535 tracing::warn!(
536 relay = %self.url,
537 failures = ?output.failed,
538 "Negentropy diff had failures (timeout usually means relay doesn't support NIP-77), will fall back to REQ+EOSE"
539 );
540 }
541
542 return Err(format!("Negentropy diff had failures: {:?}", output.failed));
543 }
544
545 Ok(reconciliation) 564 Ok(reconciliation)
546 } 565 }
547 Err(e) => { 566 Err(e) => {
567 // Mark relay as not supporting NIP-77
568 self.nip77_supported
569 .store(2, std::sync::atomic::Ordering::Relaxed);
570
548 // Log warning only once per relay to avoid spam 571 // Log warning only once per relay to avoid spam
549 if !self 572 if !self
550 .nip77_warning_logged 573 .nip77_warning_logged
@@ -556,7 +579,7 @@ impl RelayConnection {
556 "Negentropy diff failed, will fall back to REQ+EOSE" 579 "Negentropy diff failed, will fall back to REQ+EOSE"
557 ); 580 );
558 } 581 }
559 Err(format!("Negentropy diff failed: {}", e)) 582 Err(e)
560 } 583 }
561 } 584 }
562 } 585 }