diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-26 21:12:27 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-26 21:12:27 +0000 |
| commit | a94fbc1f616128c93539c71b003495e5f6291c69 (patch) | |
| tree | 42bd4a38a6b4a122c2cd4c365c509237a402e6bb /src | |
| parent | 01aeb2a3265bcafa162987c85dd281981770bba7 (diff) | |
feat: request kind-5 deletions for state/announcement events by #e
Add a non_proposal_event_ids parameter to get_fetch_filters and a
corresponding field on FetchRequest. On each fetch, the event IDs of
cached repo announcements and the state event are collected and used to
build a dedicated kind-5 filter keyed on #e tags, as specified by
NIP-09. The existing #a-tagged filter already covers addressable-event
deletions; this new filter catches deletions from clients that follow
NIP-09 strictly and do not embed a repo coordinate in their deletion
event.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/client.rs | 46 |
1 files changed, 44 insertions, 2 deletions
diff --git a/src/lib/client.rs b/src/lib/client.rs index e657f53..95dd78d 100644 --- a/src/lib/client.rs +++ b/src/lib/client.rs | |||
| @@ -798,6 +798,9 @@ impl Connect for Client { | |||
| 798 | ) | 798 | ) |
| 799 | .copied() | 799 | .copied() |
| 800 | .collect(); | 800 | .collect(); |
| 801 | // Only request non-proposal event deletions on the first loop iteration; | ||
| 802 | // cleared after first use so subsequent iterations don't re-request them. | ||
| 803 | let mut fresh_non_proposal_event_ids = request.non_proposal_event_ids.clone(); | ||
| 801 | 804 | ||
| 802 | let mut report = FetchReport::default(); | 805 | let mut report = FetchReport::default(); |
| 803 | 806 | ||
| @@ -813,8 +816,13 @@ impl Connect for Client { | |||
| 813 | let dim = Style::new().color256(247); | 816 | let dim = Style::new().color256(247); |
| 814 | 817 | ||
| 815 | loop { | 818 | loop { |
| 816 | let filters = | 819 | let filters = get_fetch_filters( |
| 817 | get_fetch_filters(&fresh_coordinates, &fresh_proposal_roots, &fresh_profiles); | 820 | &fresh_coordinates, |
| 821 | &fresh_proposal_roots, | ||
| 822 | &fresh_non_proposal_event_ids, | ||
| 823 | &fresh_profiles, | ||
| 824 | ); | ||
| 825 | fresh_non_proposal_event_ids = HashSet::new(); | ||
| 818 | 826 | ||
| 819 | if let Some(pb) = &pb { | 827 | if let Some(pb) = &pb { |
| 820 | pb.set_prefix( | 828 | pb.set_prefix( |
| @@ -1727,6 +1735,8 @@ async fn create_relays_request( | |||
| 1727 | for filter in get_fetch_filters( | 1735 | for filter in get_fetch_filters( |
| 1728 | &repo_coordinates_without_relays, | 1736 | &repo_coordinates_without_relays, |
| 1729 | &proposals, | 1737 | &proposals, |
| 1738 | &HashSet::new(), /* non_proposal_event_ids not yet computed; deletion events are not | ||
| 1739 | * cached locally */ | ||
| 1730 | &missing_contributor_profiles | 1740 | &missing_contributor_profiles |
| 1731 | .union( | 1741 | .union( |
| 1732 | &profiles_to_fetch_from_user_relays | 1742 | &profiles_to_fetch_from_user_relays |
| @@ -1828,6 +1838,21 @@ async fn create_relays_request( | |||
| 1828 | } else { | 1838 | } else { |
| 1829 | None | 1839 | None |
| 1830 | }, | 1840 | }, |
| 1841 | non_proposal_event_ids: { | ||
| 1842 | let mut ids: HashSet<EventId> = HashSet::new(); | ||
| 1843 | // Include repo announcement event IDs so we can request kind-5 | ||
| 1844 | // deletions for them by #e tag (NIP-09 style). | ||
| 1845 | if let Some(repo_ref) = &repo_ref { | ||
| 1846 | for event in repo_ref.events.values() { | ||
| 1847 | ids.insert(event.id); | ||
| 1848 | } | ||
| 1849 | // Also include the state event ID if we have one. | ||
| 1850 | if let Ok(existing_state) = get_state_from_cache(git_repo_path, repo_ref).await { | ||
| 1851 | ids.insert(existing_state.event.id); | ||
| 1852 | } | ||
| 1853 | } | ||
| 1854 | ids | ||
| 1855 | }, | ||
| 1831 | proposals, | 1856 | proposals, |
| 1832 | contributors, | 1857 | contributors, |
| 1833 | missing_contributor_profiles, | 1858 | missing_contributor_profiles, |
| @@ -2075,6 +2100,7 @@ pub fn consolidate_fetch_reports(reports: Vec<Result<FetchReport>>) -> FetchRepo | |||
| 2075 | pub fn get_fetch_filters( | 2100 | pub fn get_fetch_filters( |
| 2076 | repo_coordinates: &HashSet<Nip19Coordinate>, | 2101 | repo_coordinates: &HashSet<Nip19Coordinate>, |
| 2077 | proposal_ids: &HashSet<EventId>, | 2102 | proposal_ids: &HashSet<EventId>, |
| 2103 | non_proposal_event_ids: &HashSet<EventId>, | ||
| 2078 | required_profiles: &HashSet<PublicKey>, | 2104 | required_profiles: &HashSet<PublicKey>, |
| 2079 | ) -> Vec<nostr::Filter> { | 2105 | ) -> Vec<nostr::Filter> { |
| 2080 | [ | 2106 | [ |
| @@ -2124,6 +2150,19 @@ pub fn get_fetch_filters( | |||
| 2124 | ), | 2150 | ), |
| 2125 | ] | 2151 | ] |
| 2126 | }, | 2152 | }, |
| 2153 | // Request kind-5 deletions for state events and repo announcements by | ||
| 2154 | // their event ID (#e tag), as per NIP-09. The #a-tagged filter above | ||
| 2155 | // covers addressable-event deletions; this covers the specific event IDs | ||
| 2156 | // of the state and announcement events we already have cached. | ||
| 2157 | if non_proposal_event_ids.is_empty() { | ||
| 2158 | vec![] | ||
| 2159 | } else { | ||
| 2160 | vec![ | ||
| 2161 | nostr::Filter::default() | ||
| 2162 | .kind(Kind::EventDeletion) | ||
| 2163 | .events(non_proposal_event_ids.clone()), | ||
| 2164 | ] | ||
| 2165 | }, | ||
| 2127 | if required_profiles.is_empty() { | 2166 | if required_profiles.is_empty() { |
| 2128 | vec![] | 2167 | vec![] |
| 2129 | } else { | 2168 | } else { |
| @@ -2290,6 +2329,9 @@ pub struct FetchRequest { | |||
| 2290 | repo_coordinates_without_relays: Vec<(Nip19Coordinate, Option<Timestamp>)>, | 2329 | repo_coordinates_without_relays: Vec<(Nip19Coordinate, Option<Timestamp>)>, |
| 2291 | state: Option<(Timestamp, EventId)>, | 2330 | state: Option<(Timestamp, EventId)>, |
| 2292 | proposals: HashSet<EventId>, | 2331 | proposals: HashSet<EventId>, |
| 2332 | /// Event IDs of non-proposal events (state events, repo announcements) for | ||
| 2333 | /// which we should also request kind-5 deletion events by `#e` tag. | ||
| 2334 | non_proposal_event_ids: HashSet<EventId>, | ||
| 2293 | contributors: HashSet<PublicKey>, | 2335 | contributors: HashSet<PublicKey>, |
| 2294 | missing_contributor_profiles: HashSet<PublicKey>, | 2336 | missing_contributor_profiles: HashSet<PublicKey>, |
| 2295 | existing_events: HashSet<EventId>, | 2337 | existing_events: HashSet<EventId>, |