From e22021f0b248ebcf3bd09210d59b2cdb4701032f Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 18 Feb 2026 19:41:29 +0000 Subject: fix: simplify purgatory sync - fix SelfSubscriber sync_level upgrade and negentropy fallback Three targeted fixes for purgatory announcement sync: 1. SelfSubscriber sync_level upgrade: After or_insert_with in process_batch, always set entry.sync_level = SyncLevel::Full so that when a promoted announcement is broadcast via notify_event and SelfSubscriber receives it, an existing StateOnly entry gets upgraded to Full and PR event subscriptions are triggered immediately (not delayed up to 24h). 2. Negentropy fallback filter split: In handle_eose, when falling back from negentropy to REQ+EOSE, split batch_repos by SyncLevel and call build_sync_level_aware_filters instead of build_layer2_and_layer3_filters. Prevents StateOnly (purgatory) repos from getting Layer 2 #a/#A/#q filters prematurely, which caused nostr-sdk client deduplication to permanently drop PR events after orphan rejection. 3. Recompute sync filters after announcement batch EOSE: Add recompute_new_sync_filters_for_relay calls at all three batch-completion paths in handle_eose for generic filter (announcement) batches. This triggers state-only subscriptions for any purgatory repos registered during that batch, fixing the 24h delay before state event sync starts. 4. User-submitted purgatory announcements: Add repo_sync_index field to PolicyContext with setter/getter, wire in main.rs after SyncManager creation, and register in AcceptPurgatory handler so user-submitted announcements get StateOnly sync started immediately. 5. Update archive tests: test_archive_without_state_events_does_not_sync_git updated to reflect that StateOnly subscription now proactively fetches state events from source relays. test_archive_read_only_creates_bare_repo un-ignored as it now works end-to-end. --- src/nostr/builder.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'src/nostr/builder.rs') diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index aff12a6..8d1e461 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs @@ -17,6 +17,7 @@ use crate::nostr::policy::{ AnnouncementPolicy, AnnouncementResult, PolicyContext, PrEventPolicy, ReferenceResult, RelatedEventPolicy, StatePolicy, StateResult, }; +use crate::sync::{RepoSyncIndex, RepoSyncNeeds, SyncLevel}; /// Type alias for the shared database used by the relay pub type SharedDatabase = Arc; @@ -98,6 +99,14 @@ impl Nip34WritePolicy { self.ctx.set_local_relay(relay); } + /// Set the repo sync index so that user-submitted purgatory announcements can + /// be registered for state event sync immediately. + /// + /// This must be called after SyncManager is created. + pub fn set_repo_sync_index(&self, index: RepoSyncIndex) { + self.ctx.set_repo_sync_index(index); + } + /// Handle repository announcement event async fn handle_announcement(&self, event: &Event) -> WritePolicyResult { let event_id_str = event.id.to_bech32().unwrap_or_else(|_| event.id.to_hex()); @@ -146,6 +155,51 @@ impl Nip34WritePolicy { "Accepted announcement to purgatory: {} (waiting for git data)", event_id_str ); + + // Register repo in repo_sync_index with StateOnly level so that + // state event sync starts promptly via the next batch EOSE recompute. + // This handles user-submitted purgatory announcements - the SelfSubscriber + // only sees DB events, so it won't pick these up automatically. + if let Some(repo_sync_index) = self.ctx.get_repo_sync_index() { + if let Ok(announcement) = + RepositoryAnnouncement::from_event(event.clone()) + { + use std::collections::HashSet; + let repo_id = format!( + "30617:{}:{}", + event.pubkey, + announcement.identifier + ); + + // Extract relay URLs from the announcement event tags + let relays: HashSet = event + .tags + .iter() + .flat_map(|tag| { + let tag_vec = tag.as_slice(); + if !tag_vec.is_empty() && tag_vec[0] == "relays" { + tag_vec[1..].iter().map(|s| s.to_string()).collect::>() + } else { + vec![] + } + }) + .collect(); + + let mut index = repo_sync_index.write().await; + index.entry(repo_id.clone()).or_insert_with(|| RepoSyncNeeds { + relays, + root_events: HashSet::new(), + sync_level: SyncLevel::StateOnly, + }); + drop(index); + + tracing::debug!( + repo_id = %repo_id, + "Registered purgatory announcement in repo_sync_index as StateOnly" + ); + } + } + WritePolicyResult::Reject { status: true, // Client sees OK message: "purgatory: won't be served until git data arrives".into(), -- cgit v1.2.3