diff --git a/crates/signed_state/src/backend.rs b/crates/signed_state/src/backend.rs index b59d900..4168202 100644 --- a/crates/signed_state/src/backend.rs +++ b/crates/signed_state/src/backend.rs @@ -160,15 +160,15 @@ impl Backend { let task = cx.background_spawn(async move { for url in BOOTSTRAP_RELAYS { - client.add_relay(url).await?; + client.add_relay(url).and_connect().await?; } for url in INDEXER_RELAYS { client .add_relay(url) .capabilities(RelayCapabilities::DISCOVERY) + .and_connect() .await?; } - client.connect().await; Ok::<(), Error>(()) }); @@ -425,6 +425,7 @@ impl Backend { let path = cache.repo_path(&addr); let owner = public_key.to_bech32().unwrap(); let servers = grasp_servers.clone(); + let client = self.client.clone(); cx.spawn(async move |this, cx| { // Initialize the local clone and create the user's working copy from it. @@ -488,10 +489,9 @@ impl Backend { let commit_sha = Sha1Hash::from_str(&commit).map_err(|_| anyhow!("invalid id"))?; // The nostr client queues events until each relay is connected. - this.update(cx, |this, cx| { - let urls: Vec = servers.iter().map(ToString::to_string).collect(); - this.add_relays(urls, cx); - })?; + for url in &servers { + client.add_relay(url).and_connect().await.ok(); + } // The state event is the push authorization. It must be accepted before the push below. let announcement = GitRepositoryAnnouncement { @@ -622,6 +622,7 @@ impl Backend { let owner = public_key.to_bech32().unwrap(); let servers = grasp_servers.clone(); + let client = self.client.clone(); cx.spawn(async move |this, cx| { let work = cx.background_spawn({ @@ -635,10 +636,9 @@ impl Backend { let (state, euc) = work.await?; // The nostr client queues events until each relay is connected. - this.update(cx, |this, cx| { - let urls: Vec = servers.iter().map(ToString::to_string).collect(); - this.add_relays(urls, cx); - })?; + for url in &servers { + client.add_relay(url).and_connect().await.ok(); + } // The state event is the push authorization. It must be accepted before the push below. let announcement = GitRepositoryAnnouncement { @@ -1045,22 +1045,22 @@ impl Backend { task.detach(); } - /// Fetch the user's grasp list and add the listed grasp servers as relays. + /// Sync the user's grasp list and add the listed grasp servers as relays. fn bootstrap_user(&mut self, public_key: PublicKey, cx: &mut Context) { let client = self.client.clone(); let task: Task> = cx.spawn(async move |this, cx| { let result = async { - let events: Vec = client - .fetch_events(filters::grasp_list(public_key)) - .await? - .into_iter() - .collect(); + sync_bootstrap_only( + &client, + filters::grasp_list(public_key), + SyncOptions::default(), + ) + .await?; - for url in latest_grasp_list_servers(events) { - client.add_relay(url.as_str()).await.ok(); + for url in user_grasp_list_servers(client.clone(), public_key).await? { + client.add_relay(url).and_connect().await.ok(); } - client.connect().await; Ok::<_, Error>(()) } @@ -1137,32 +1137,6 @@ impl Backend { task.detach(); } - /// Add relays and connect to them. - pub fn add_relays(&mut self, urls: Vec, cx: &mut Context) { - let client = self.client.clone(); - - let task = cx.background_spawn(async move { - for url in urls { - client.add_relay(&url).await?; - } - client.connect().await; - Ok::<(), Error>(()) - }); - - let notify_task: Task> = cx.spawn(async move |this, cx| { - match task.await { - Ok(()) => { - this.update(cx, |_this, cx| cx.notify())?; - } - Err(e) => { - this.update(cx, |_this, cx| cx.emit(BackendEvent::error(e.to_string())))?; - } - } - Ok(()) - }); - notify_task.detach(); - } - /// Connect to a repository's announced relays, its NIP-34 `relays` tag. /// /// Callers are responsible for not repeating this for relays they already @@ -1396,8 +1370,7 @@ async fn connect_repo_relays( // Ensure relay connections for url in relays.iter() { - client.add_relay(url).await?; - client.connect_relay(url).await?; + client.add_relay(url).and_connect().await?; } // Run neg sync for each filter @@ -1724,9 +1697,9 @@ async fn stage_event_on_relay( ) -> Result<(), String> { client .add_relay(relay) + .and_connect() .await .map_err(|e| format!("could not add relay {relay}: {e}"))?; - client.connect().await; let output = client .send_event(event) diff --git a/docs/backend-rearchitecture.md b/docs/backend-rearchitecture.md index fb50a31..0c9fc78 100644 --- a/docs/backend-rearchitecture.md +++ b/docs/backend-rearchitecture.md @@ -30,6 +30,17 @@ Each is addressed below with concrete file:line references and a verified replac ## 1. `fetch_events` — one call site, and it should go too +> **Status: done.** `bootstrap_user` now calls `sync_bootstrap_only` (the +> same helper `Backend::sync_bootstrap` already used) against +> `filters::grasp_list(public_key)`, then reads the result back out through +> the existing `user_grasp_list_servers` query helper instead of hand-rolling +> a second `BTreeSet` → `Vec` collect. `client.add_relay(url)` +> no longer round-trips through `.as_str()`, and connects with +> `.and_connect()` (see §8) instead of a trailing pool-wide `client.connect()`. +> `grep -rn "fetch_events" crates/` now returns nothing in the whole +> workspace. `cargo check --workspace`, `cargo clippy -p signed_state`, and +> `cargo test -p signed_state` (24 tests, unchanged) all pass. + ``` grep -rn "fetch_events" crates/ crates/signed_state/src/backend.rs:1065 @@ -574,6 +585,26 @@ to `RepoListStore`. ## 8. Relay add/connect: stop round-tripping through strings, stop reconnecting the whole pool +> **Status: done.** Every `add_relay` call site now passes the `RelayUrl` +> directly (no `.as_str()`/`ToString` round trip) and chains `.and_connect()` +> instead of a separate, pool-wide `client.connect()`/`client.connect_relay()` +> call: `Backend::bootstrap` (both the `BOOTSTRAP_RELAYS` and `INDEXER_RELAYS` +> loops), `bootstrap_user`, `create_repository`, `publish_local_repo`, +> `connect_repo_relays`, and `stage_event_on_relay`. `Backend::add_relays` is +> deleted entirely — its two callers (`create_repository`, `publish_local_repo`) +> now capture `client` once before the surrounding `cx.spawn` and loop +> `client.add_relay(url).and_connect().await.ok();` directly over the +> `Vec` they already had, with no `Vec` conversion at all. +> Verified `.and_connect()` (`client/api/add.rs`) and pool-wide `.connect()` +> semantics (`client/api/connect.rs`) against the pinned nostr-sdk source +> before making the change (see chat history), plus that `pool.sync()` +> requires relays to already be present in the pool +> (`pool/mod.rs:679-693`, `relays.get(&url).ok_or_else(...)`), which is why +> `sync_bootstrap_only`/`bootstrap_user` can safely assume `BOOTSTRAP_RELAYS` +> are already added by `Backend::bootstrap` before any sync runs. +> `cargo check --workspace`, `cargo clippy -p signed_state`, and +> `cargo test -p signed_state` (24 tests) all pass. + Flagged example (`backend.rs:1071-1074`): ```rust @@ -1257,14 +1288,18 @@ method. bug in `RepoDetailView`/`NewPullRequestView` along the way. Done: both halves are complete, see §6 and §14 for details. -3. **Fix the relay add/connect calls** (§8): drop `.as_str()`/`ToString` +3. ✅ **Fix the relay add/connect calls** (§8): drop `.as_str()`/`ToString` round trips, replace `add_relay` + blanket `client.connect()`/ `connect_relay` pairs with `add_relay(url).and_connect()`, and delete `Backend::add_relays`. Mechanical, no behavior change beyond "connect only what was just added." -4. **Fix `bootstrap_user`** to sync+query instead of `fetch_events` (§1). + + Done: see §8 for the full list of call sites and verification notes. +4. ✅ **Fix `bootstrap_user`** to sync+query instead of `fetch_events` (§1). One function, fully covered by existing tests for `latest_grasp_list_servers`. + + Done: see §1. `fetch_events` no longer appears anywhere in the workspace. 5. **Generalize the 3 `signed_git` URL-list signatures** to `&[impl AsRef]` (§15), then delete the now-redundant `.map(ToString::to_string).collect()` at all 7 call sites. Self-contained to `signed_git`'s public API plus a