From b02ed59c678e7c501d289d060015f7e3611504e9 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sat, 1 Aug 2026 14:51:28 +0000 Subject: [PATCH] fix(relay): keep sessions open on malformed client messages Production traffic containing unparseable client messages tore down the entire WebSocket connection. A single bad message - most visibly requests carrying invalid event IDs, failing deserialization with `Invalid input length 64` - propagated out of the relay's read loop and closed the session, so every affected client had to reconnect and re-subscribe. One malformed frame cost a client all of its live subscriptions, and clients that retried the same payload produced sustained connection churn. The defect was upstream in rust-nostr's local relay, not in ngit-grasp, and was reported as nostr:nevent1qqsqmmfv6fxk995yr5858eev7z6zmchchgmk90d4rffuuwe6ewcvx0cpz3mhxue69uhhyetvv9ujumn8d96zuer9wckedd82 and fixed by nostr:nevent1qqs24l0rv6qw3mdqdaj8nj4wlds2gy8a9wrqs3gj5kcmz27c8gm7kagpz3mhxue69uhhyetvv9ujumn8d96zuer9wc7gp6cp Deserialization failures are now contained inside the WebSocket loop and answered with a `NOTICE`, leaving the session and its subscriptions intact. Upgrade the NostrDevKit crates from 0.45.0-alpha.3 to 0.45.0-alpha.8, the first published release containing the fix. Inclusion was verified three ways rather than by release notes: upstream commit 7c0f3aa2cf2fbeb9f0067cb2349579754919eabd is an ancestor of the `Bump to v0.45.0-alpha.8` commit on upstream master; the alpha.8 crate downloaded from crates.io contains the repaired match arm in `src/local_relay/local/inner.rs`; and it also ships the upstream regression test `test_malformed_client_message_does_not_close_connection`, which drives a real WebSocket with a short-author REQ and asserts a subsequent valid REQ still reaches EOSE on the same socket. That upstream test covers the behaviour directly, so no equivalent test is duplicated here. All four rust-nostr crates move together to keep the tree off a mixture of alpha versions. `nostr-relay-builder` is gone: upstream merged it into `nostr-sdk` at alpha.4, so its imports become `nostr_sdk::local_relay` and `nostr_sdk::prelude`, and `nostr-sdk` now enables the `local-relay` feature. Three further alpha-to-alpha API removals are absorbed: `nostr::hashes` is no longer re-exported, so the `Sec-WebSocket-Accept` derivation depends on `bitcoin_hashes` directly - the same crate `nostr` still uses internally, so no second hash implementation enters the tree; `BoxedFuture` became crate-private, so `WritePolicy::admit_event` spells out its `Pin>>` return type; and `EventBuilder::text_note` was removed in favour of `EventBuilder::new(Kind::TextNote, ..)`, which affects test code only. These requirements are pinned exactly as `=0.45.0-alpha.n` rather than left as caret requirements. Cargo reads `"0.45.0-alpha.3"` as `^0.45.0-alpha.3`, which admits *any* later prerelease of 0.45.0 even though prereleases promise no compatibility - exactly the surprise reported against ngit in nostr:nevent1qqs0yvj4z302cjsnqx9jpp78jrfujhse0w47adjncwkxr922rjltk8spz3mhxue69uhhyetvv9ujumn8d96zuer9wcr42j8n where a declared alpha.2 resolved to alpha.7. This upgrade is direct evidence that the risk is real: alpha.4 deleted a whole crate this project depended on, which a caret requirement would have accepted silently. Pinning is safe because it only narrows resolution, and the committed `Cargo.lock` already selects these versions; what it adds is protection for builds that do not honour the lockfile - `cargo install`, and downstream consumers of the `ngit_grasp` library. The pins should be relaxed to caret requirements once 0.45.0 is released. Validated in the project Nix development shell with `CARGO_BUILD_JOBS=2`: `cargo fmt --all --check` clean, `cargo clippy --workspace --all-targets -D warnings` clean, and `cargo test --workspace --no-fail-fast` at 1937 passed / 32 failed. The 32 failures were confirmed pre-existing by running the identical suite on unmodified master in this environment: the same 1937/32 split and a byte-identical set of failing test names, so this upgrade introduces no regressions. All dependencies remain crates.io sources, so no `flake.nix` or `nix/module.nix` hashes required updating. --- CHANGELOG.md | 6 ++ Cargo.lock | 90 ++++++++--------- Cargo.toml | 25 +++-- grasp-audit/Cargo.toml | 3 +- src/git/authorization.rs | 2 +- src/git/handlers.rs | 2 +- src/git/sync.rs | 10 +- src/grasp06/receive.rs | 2 +- src/http/mod.rs | 6 +- src/nostr/builder.rs | 12 ++- src/nostr/lifecycle/deletion/archival.rs | 4 +- src/nostr/lifecycle/deletion/cascade.rs | 2 +- src/nostr/lifecycle/deletion/cleanup.rs | 4 +- src/nostr/lifecycle/deletion/policy.rs | 8 +- src/nostr/lifecycle/deletion/pr_refs.rs | 2 +- src/nostr/lifecycle/deletion/purgatory.rs | 14 +-- src/nostr/lifecycle/deletion/recovery.rs | 2 +- src/nostr/lifecycle/deletion/rollback.rs | 2 +- src/nostr/lifecycle/deletion/runtime.rs | 4 +- src/nostr/lifecycle/deletion/service.rs | 16 ++- src/nostr/lifecycle/deletion/startup.rs | 4 +- src/nostr/lifecycle/history.rs | 2 +- src/nostr/lifecycle/holding.rs | 2 +- src/nostr/lifecycle/repository_locks.rs | 2 +- src/nostr/lifecycle/tombstones.rs | 2 +- src/nostr/mod.rs | 2 +- src/nostr/persistence.rs | 2 +- src/nostr/policy/announcement.rs | 2 +- src/nostr/policy/mod.rs | 2 +- src/nostr/policy/pr_event.rs | 2 +- src/nostr/policy/related.rs | 4 +- src/nostr/policy/result.rs | 2 +- src/nostr/policy/state.rs | 4 +- src/purgatory/mod.rs | 118 +++++++++++++++------- src/purgatory/promotion_hooks.rs | 4 +- src/purgatory/sync/context.rs | 2 +- src/server.rs | 2 +- src/sync/mod.rs | 6 +- src/sync/rejected_index.rs | 39 +++---- src/sync/relay_connection.rs | 4 +- tests/common/mock_relay.rs | 11 +- tests/git_response_streaming.rs | 2 +- tests/lifecycle/replaceable_history.rs | 2 +- tests/purgatory_persistence.rs | 4 +- 44 files changed, 254 insertions(+), 188 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d430a7..dd90cb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed malformed client messages tearing down the whole WebSocket connection. + A single unparseable message - in production, requests carrying invalid event + IDs that fail with `Invalid input length 64` - closed the session, forcing + clients to reconnect and re-subscribe. Invalid messages are now answered with + a `NOTICE` and the connection stays open. Fixed upstream in rust-nostr and + picked up by upgrading to `0.45.0-alpha.8`. - Fixed proactive sync losing repository events when public relays cap active subscriptions. Compatible GRASP filters now share bounded NIP-01 REQs while retaining per-filter history pagination. diff --git a/Cargo.lock b/Cargo.lock index a8e51f1..39d6715 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c69f24cf7a798027c6b0801de5bfd05d01fe343e87a8329f7c8b46a6d3bc4a79" +checksum = "efa9ef232c1731ddbe4a306b5b5dcf9e50a924d6bd086d4784939d14f8e78daa" dependencies = [ "futures", "futures-util", @@ -132,12 +132,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -152,9 +146,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bech32" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" [[package]] name = "bip39" @@ -1210,9 +1204,9 @@ checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "lru" -version = "0.16.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" [[package]] name = "matchers" @@ -1280,6 +1274,7 @@ dependencies = [ "anyhow", "async-trait", "base64", + "bitcoin_hashes", "clap", "dashmap", "dotenvy", @@ -1295,7 +1290,6 @@ dependencies = [ "nostr", "nostr-lmdb", "nostr-memory", - "nostr-relay-builder", "nostr-sdk", "prometheus", "rand 0.10.1", @@ -1311,9 +1305,9 @@ dependencies = [ [[package]] name = "nostr" -version = "0.45.0-alpha.3" +version = "0.45.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901cdb1982b9bf73a8d8a7270978345aabd0c62544f6a9a5c35a13d0a7f5824" +checksum = "0334ad7a19f229983c808840f65d813d09c7fc030775db5d325abffa1295fe6c" dependencies = [ "base64", "bech32", @@ -1335,9 +1329,9 @@ dependencies = [ [[package]] name = "nostr-database" -version = "0.45.0-alpha.3" +version = "0.45.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc883371f13c7c6ab53e9a541fd76955d5fafe8418ccae1439fc64ff0c6bb2f" +checksum = "82e3827aa36205889d9950bb07e9ab9380f7881ab3b059956f2d87651fa64281" dependencies = [ "btreecap", "flatbuffers", @@ -1347,9 +1341,9 @@ dependencies = [ [[package]] name = "nostr-gossip" -version = "0.45.0-alpha.3" +version = "0.45.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719c20a592b898e636f85a030fdba3689c7a07cc8707c9c2b34f1fb72a3db70" +checksum = "a4696e01b02eb84daa6d8419f529c6e6f3063b742a54925dc0300a3230871477" dependencies = [ "nostr", "opaquerr", @@ -1357,9 +1351,9 @@ dependencies = [ [[package]] name = "nostr-lmdb" -version = "0.45.0-alpha.3" +version = "0.45.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352aa0d58315128c581c3046f91bbb9b39b482d5ff3300ceea748726b889d4fc" +checksum = "2832ab8243313733630c90208f36b9859dd939b13c745c977f2a0c465b5ec117" dependencies = [ "async-utility", "flume", @@ -1372,9 +1366,9 @@ dependencies = [ [[package]] name = "nostr-memory" -version = "0.45.0-alpha.3" +version = "0.45.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fce3c7c698afe88d9ac28f78e8dd68de6ecd3accfa99f2b7b76791e0ac24c2" +checksum = "1688a4bfea60f72ca212c55f23cc9f068bfb5e787829d85fc7f689c3df9de842" dependencies = [ "btreecap", "nostr", @@ -1382,31 +1376,11 @@ dependencies = [ "tokio", ] -[[package]] -name = "nostr-relay-builder" -version = "0.45.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d6f997d7bdbd18ab6fd5bf7d402c80dd1d313599461723a111a3ace10b2440" -dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "faster-hex", - "negentropy", - "nostr", - "nostr-database", - "nostr-memory", - "nostr-sdk", - "opaquerr", - "tokio", - "tracing", -] - [[package]] name = "nostr-sdk" -version = "0.45.0-alpha.3" +version = "0.45.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa5827340f0296084ead006938172fa946531185741af15c8bf4e60b6f3ad7f" +checksum = "c2ffff8a1d69b9828574ead3b34d615bffe4c75ff3e3335a28f111867c7f7cdd" dependencies = [ "async-utility", "async-wsocket", @@ -1417,7 +1391,9 @@ dependencies = [ "nostr", "nostr-database", "nostr-gossip", + "nostr-memory", "opaquerr", + "rand 0.10.1", "tokio", "tokio-stream", "tracing", @@ -1703,6 +1679,8 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -1712,7 +1690,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -1727,6 +1705,16 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1742,6 +1730,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -1924,12 +1915,13 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "secp256k1" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ + "bitcoin_hashes", + "rand 0.8.6", "secp256k1-sys", - "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f578902..d5194ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,16 +16,27 @@ hyper = { version = "1.4", features = ["full"] } hyper-util = { version = "0.1", features = ["tokio", "server", "http1", "http2"] } http-body-util = "0.1" -# Nostr relay -nostr-relay-builder = "0.45.0-alpha.3" - # Nostr -nostr = "0.45.0-alpha.3" -nostr-sdk = "0.45.0-alpha.3" -nostr-lmdb = "0.45.0-alpha.3" -nostr-memory = "0.45.0-alpha.3" +# +# These are prerelease (alpha) requirements and are pinned exactly with `=`. +# Cargo treats `"0.45.0-alpha.8"` as `^0.45.0-alpha.8`, which accepts *any* +# later prerelease of 0.45.0 even though alphas carry no compatibility +# guarantee (alpha.4 deleted the `nostr-relay-builder` crate outright). Exact +# pins keep lockfile-less builds - `cargo install`, downstream consumers of the +# `ngit_grasp` library - on the version this tree is tested against. +# Relax to caret requirements once 0.45.0 is released. +nostr = "=0.45.0-alpha.8" +# `local-relay` carries the embedded relay implementation, previously the +# separate `nostr-relay-builder` crate. +nostr-sdk = { version = "=0.45.0-alpha.8", features = ["local-relay"] } +nostr-lmdb = "=0.45.0-alpha.8" +nostr-memory = "=0.45.0-alpha.8" # Utilities +# SHA-1 for the `Sec-WebSocket-Accept` handshake. `nostr` uses this same crate +# internally but stopped re-exporting it as `nostr::hashes` in 0.45.0-alpha.8, +# so depend on it directly rather than pulling in a second hash implementation. +bitcoin_hashes = "0.14" futures-util = "0.3" base64 = "0.22" flate2 = "1.0" diff --git a/grasp-audit/Cargo.toml b/grasp-audit/Cargo.toml index d6b5386..a418e07 100644 --- a/grasp-audit/Cargo.toml +++ b/grasp-audit/Cargo.toml @@ -13,7 +13,8 @@ path = "src/bin/grasp-audit.rs" [dependencies] # Nostr -nostr-sdk = "0.45.0-alpha.3" +# Pinned exactly: see the rationale in the workspace root Cargo.toml. +nostr-sdk = "=0.45.0-alpha.8" # Async tokio = { version = "1", features = ["full"] } diff --git a/src/git/authorization.rs b/src/git/authorization.rs index e59f614..a4bd260 100644 --- a/src/git/authorization.rs +++ b/src/git/authorization.rs @@ -29,7 +29,7 @@ use anyhow::{anyhow, Result}; use hyper::body::Bytes; -use nostr_relay_builder::prelude::*; +use nostr_sdk::prelude::*; use nostr_sdk::prelude::{EventId, ToBech32}; use std::collections::{HashMap, HashSet}; use std::sync::Arc; diff --git a/src/git/handlers.rs b/src/git/handlers.rs index cf72832..5ab0b77 100644 --- a/src/git/handlers.rs +++ b/src/git/handlers.rs @@ -5,7 +5,7 @@ use futures_util::stream; use http_body_util::{BodyExt, StreamBody}; use hyper::{body::Bytes, body::Frame, Response, StatusCode}; -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use std::collections::HashSet; use std::io; use std::path::PathBuf; diff --git a/src/git/sync.rs b/src/git/sync.rs index 1f4f151..89a2b1b 100644 --- a/src/git/sync.rs +++ b/src/git/sync.rs @@ -879,7 +879,7 @@ pub async fn process_newly_available_git_data( source_repo_path: &Path, new_oids: &HashSet, database: &SharedDatabase, - local_relay: Option<&nostr_relay_builder::LocalRelay>, + local_relay: Option<&nostr_sdk::local_relay::LocalRelay>, purgatory: &Purgatory, git_data_path: &Path, promotion_hooks: Option<&dyn PurgatoryPromotionHooks>, @@ -995,7 +995,7 @@ pub async fn process_repos_populated_by_state_copy( state: &RepositoryState, repo_data: &RepositoryData, database: &SharedDatabase, - local_relay: Option<&nostr_relay_builder::LocalRelay>, + local_relay: Option<&nostr_sdk::local_relay::LocalRelay>, purgatory: &Purgatory, git_data_path: &Path, promotion_hooks: Option<&dyn PurgatoryPromotionHooks>, @@ -1053,7 +1053,7 @@ async fn process_purgatory_state_events( identifier: &str, source_repo_path: &Path, database: &SharedDatabase, - local_relay: Option<&nostr_relay_builder::LocalRelay>, + local_relay: Option<&nostr_sdk::local_relay::LocalRelay>, purgatory: &Purgatory, git_data_path: &Path, promotion_hooks: Option<&dyn PurgatoryPromotionHooks>, @@ -1432,7 +1432,7 @@ async fn process_purgatory_pr_events( identifier: &str, source_repo_path: &Path, database: &SharedDatabase, - local_relay: Option<&nostr_relay_builder::LocalRelay>, + local_relay: Option<&nostr_sdk::local_relay::LocalRelay>, purgatory: &Purgatory, git_data_path: &Path, promotion_hooks: Option<&dyn PurgatoryPromotionHooks>, @@ -1574,7 +1574,7 @@ async fn process_purgatory_announcements( identifier: &str, source_repo_path: &Path, database: &SharedDatabase, - local_relay: Option<&nostr_relay_builder::LocalRelay>, + local_relay: Option<&nostr_sdk::local_relay::LocalRelay>, purgatory: &Purgatory, git_data_path: &Path, promotion_hooks: Option<&dyn PurgatoryPromotionHooks>, diff --git a/src/grasp06/receive.rs b/src/grasp06/receive.rs index 735a953..9987383 100644 --- a/src/grasp06/receive.rs +++ b/src/grasp06/receive.rs @@ -64,7 +64,7 @@ use std::sync::Arc; use dashmap::DashMap; use hyper::body::{Bytes, Frame}; use hyper::Response; -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use nostr_sdk::prelude::*; use std::sync::Mutex; use tokio::io::AsyncWriteExt; diff --git a/src/http/mod.rs b/src/http/mod.rs index 7e7cd1d..2128293 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -10,6 +10,8 @@ use std::pin::Pin; use std::sync::Arc; use base64::Engine; +use bitcoin_hashes::sha1::Hash as Sha1Hash; +use bitcoin_hashes::{Hash, HashEngine}; use http_body_util::BodyExt; use hyper::body::{Bytes, Incoming}; use hyper::header::{CONNECTION, SEC_WEBSOCKET_ACCEPT, UPGRADE}; @@ -17,9 +19,7 @@ use hyper::server::conn::http1; use hyper::service::Service; use hyper::{Method, Request, Response}; use hyper_util::rt::TokioIo; -use nostr::hashes::sha1::Hash as Sha1Hash; -use nostr::hashes::{Hash, HashEngine}; -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use nostr_sdk::prelude::PublicKey; use tokio::net::TcpListener; diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index bc6093b..655164b 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs @@ -4,16 +4,18 @@ /// Deletion behaviour is owned by the `nostr::lifecycle` subsystem; /// this module keeps top-level `admit_event` /// dispatch and non-deletion admission/router policy logic. +use std::future::Future; use std::net::SocketAddr; use std::num::NonZeroUsize; use std::path::Path; +use std::pin::Pin; use std::sync::{Arc, RwLock}; use anyhow::Result; use nostr::nips::nip19::ToBech32; use nostr_lmdb::NostrLmdb; use nostr_memory::MemoryDatabase; -use nostr_relay_builder::prelude::*; +use nostr_sdk::prelude::*; use crate::config::{Config, DatabaseBackend}; use crate::nostr::events::RepositoryAnnouncement; @@ -143,7 +145,7 @@ impl Nip34WritePolicy { /// /// This must be called after the relay is created since the relay depends /// on this policy, but purgatory sync needs the relay to notify subscribers. - pub fn set_local_relay(&self, relay: nostr_relay_builder::LocalRelay) { + pub fn set_local_relay(&self, relay: nostr_sdk::local_relay::LocalRelay) { self.ctx.set_local_relay(relay); } @@ -163,7 +165,7 @@ impl Nip34WritePolicy { .clone() } - pub(crate) fn local_relay(&self) -> Option { + pub(crate) fn local_relay(&self) -> Option { self.ctx.get_local_relay() } @@ -793,9 +795,9 @@ impl Nip34WritePolicy { impl WritePolicy for Nip34WritePolicy { fn admit_event<'a>( &'a self, - event: &'a nostr_relay_builder::prelude::Event, + event: &'a nostr_sdk::prelude::Event, addr: &'a SocketAddr, - ) -> BoxedFuture<'a, WritePolicyResult> { + ) -> Pin + Send + 'a>> { Box::pin(async move { // Check event blacklist FIRST - it overrides everything if let Some(reason) = self.check_event_blacklist(event) { diff --git a/src/nostr/lifecycle/deletion/archival.rs b/src/nostr/lifecycle/deletion/archival.rs index 6952791..13fc35e 100644 --- a/src/nostr/lifecycle/deletion/archival.rs +++ b/src/nostr/lifecycle/deletion/archival.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use flate2::write::GzEncoder; use flate2::Compression; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag, Timestamp, }; use tar::Builder as TarBuilder; @@ -255,7 +255,7 @@ impl DeletionPolicy { &self, author: &PublicKey, coordinate: &str, - deletion_created_at: nostr_relay_builder::prelude::Timestamp, + deletion_created_at: nostr_sdk::prelude::Timestamp, moved_ids: &mut HashSet, source: DeletionSource, ) -> DeletionOutcome { diff --git a/src/nostr/lifecycle/deletion/cascade.rs b/src/nostr/lifecycle/deletion/cascade.rs index 5990251..9f5c624 100644 --- a/src/nostr/lifecycle/deletion/cascade.rs +++ b/src/nostr/lifecycle/deletion/cascade.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet, VecDeque}; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag, Timestamp, }; diff --git a/src/nostr/lifecycle/deletion/cleanup.rs b/src/nostr/lifecycle/deletion/cleanup.rs index 73942f9..b352e10 100644 --- a/src/nostr/lifecycle/deletion/cleanup.rs +++ b/src/nostr/lifecycle/deletion/cleanup.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use nostr_relay_builder::prelude::{Filter, Kind, Timestamp}; +use nostr_sdk::prelude::{Filter, Kind, Timestamp}; use crate::nostr::lifecycle::RequestLifecycleRecord; @@ -129,7 +129,7 @@ mod tests { use std::path::PathBuf; use std::sync::Arc; - use nostr_relay_builder::prelude::{Event, EventBuilder, EventId, FinalizeEvent, Keys, Tag}; + use nostr_sdk::prelude::{Event, EventBuilder, EventId, FinalizeEvent, Keys, Tag}; use super::*; use crate::grasp06::receive::new_repo_init_locks; diff --git a/src/nostr/lifecycle/deletion/policy.rs b/src/nostr/lifecycle/deletion/policy.rs index 1237ebd..ef0c776 100644 --- a/src/nostr/lifecycle/deletion/policy.rs +++ b/src/nostr/lifecycle/deletion/policy.rs @@ -33,7 +33,7 @@ use std::collections::HashSet; use nostr::nips::nip19::ToBech32; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag, Timestamp, WritePolicyResult, }; @@ -414,7 +414,7 @@ mod tests { use crate::nostr::lifecycle::RequestLifecycleRecord; use crate::nostr::lifecycle::Tombstones; use crate::purgatory::Purgatory; - use nostr_relay_builder::prelude::*; + use nostr_sdk::prelude::*; use std::collections::HashSet; use std::path::PathBuf; use std::sync::Arc; @@ -804,10 +804,10 @@ mod tests { let attacker_keys = Keys::generate(); let victim_keys = Keys::generate(); - let foreign_target = EventBuilder::text_note("real event from victim") + let foreign_target = EventBuilder::new(Kind::TextNote, "real event from victim") .finalize(&victim_keys) .unwrap(); - let valid_target = EventBuilder::text_note("real event from deleter") + let valid_target = EventBuilder::new(Kind::TextNote, "real event from deleter") .finalize(&attacker_keys) .unwrap(); let deletion = EventBuilder::new(Kind::EventDeletion, "") diff --git a/src/nostr/lifecycle/deletion/pr_refs.rs b/src/nostr/lifecycle/deletion/pr_refs.rs index bad5ba1..bb46bc4 100644 --- a/src/nostr/lifecycle/deletion/pr_refs.rs +++ b/src/nostr/lifecycle/deletion/pr_refs.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use std::path::PathBuf; -use nostr_relay_builder::prelude::{Event, Kind}; +use nostr_sdk::prelude::{Event, Kind}; use super::policy::{owner_directory_component, DeletionPolicy}; use crate::git; diff --git a/src/nostr/lifecycle/deletion/purgatory.rs b/src/nostr/lifecycle/deletion/purgatory.rs index 6046d0b..d87c4cb 100644 --- a/src/nostr/lifecycle/deletion/purgatory.rs +++ b/src/nostr/lifecycle/deletion/purgatory.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::process::Command; -use nostr_relay_builder::prelude::{Event, Kind, Timestamp}; +use nostr_sdk::prelude::{Event, Kind, Timestamp}; use super::{policy::DeletionPolicy, DeletionOutcome}; use crate::nostr::events::RepositoryAnnouncement; @@ -181,10 +181,10 @@ impl DeletionPolicy { /// Only removes entries whose author matches `author`. fn remove_by_event_id( &self, - author: &nostr_relay_builder::prelude::PublicKey, + author: &nostr_sdk::prelude::PublicKey, target_id_hex: &str, _deletion_created_at: u64, - removed_ids: &mut HashSet, + removed_ids: &mut HashSet, ) -> DeletionOutcome { // --- Check PR events (kind 1617/1618) first — O(1) direct lookup --- // PR purgatory is keyed by event ID hex, so this is the cheapest check. @@ -273,10 +273,10 @@ impl DeletionPolicy { /// Per NIP-09, all versions up to `deletion_created_at` are considered deleted. fn remove_by_coordinate( &self, - author: &nostr_relay_builder::prelude::PublicKey, + author: &nostr_sdk::prelude::PublicKey, coordinate: &str, deletion_created_at: u64, - removed_ids: &mut HashSet, + removed_ids: &mut HashSet, ) -> DeletionOutcome { let mut outcome = DeletionOutcome::default(); // Parse coordinate: `::` @@ -356,9 +356,9 @@ impl DeletionPolicy { /// Remove a purgatory announcement and delete its bare repository from disk. fn evict_purgatory_entry( &self, - author: &nostr_relay_builder::prelude::PublicKey, + author: &nostr_sdk::prelude::PublicKey, identifier: &str, - removed_ids: &mut HashSet, + removed_ids: &mut HashSet, ) -> DeletionOutcome { let mut outcome = DeletionOutcome::default(); // Get repo path before removing diff --git a/src/nostr/lifecycle/deletion/recovery.rs b/src/nostr/lifecycle/deletion/recovery.rs index ec2e8ac..11d134f 100644 --- a/src/nostr/lifecycle/deletion/recovery.rs +++ b/src/nostr/lifecycle/deletion/recovery.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use flate2::read::GzDecoder; -use nostr_relay_builder::prelude::{Event, Kind, PublicKey, Timestamp}; +use nostr_sdk::prelude::{Event, Kind, PublicKey, Timestamp}; use tar::Archive as TarArchive; use super::DeletionService; diff --git a/src/nostr/lifecycle/deletion/rollback.rs b/src/nostr/lifecycle/deletion/rollback.rs index 7475ee5..85e3177 100644 --- a/src/nostr/lifecycle/deletion/rollback.rs +++ b/src/nostr/lifecycle/deletion/rollback.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeSet, HashSet}; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag, Timestamp, }; diff --git a/src/nostr/lifecycle/deletion/runtime.rs b/src/nostr/lifecycle/deletion/runtime.rs index 4d6783a..f5b4709 100644 --- a/src/nostr/lifecycle/deletion/runtime.rs +++ b/src/nostr/lifecycle/deletion/runtime.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Result; use clap::Args; -use nostr_relay_builder::prelude::{PublicKey, Timestamp}; +use nostr_sdk::prelude::{PublicKey, Timestamp}; use tokio::sync::watch; use tokio::task::JoinHandle; @@ -263,7 +263,7 @@ mod tests { use std::sync::Arc; use nostr_memory::MemoryDatabase; - use nostr_relay_builder::prelude::{ + use nostr_sdk::prelude::{ EventBuilder, EventId, FinalizeEvent, Keys, Kind, NostrDatabase, Tag, }; diff --git a/src/nostr/lifecycle/deletion/service.rs b/src/nostr/lifecycle/deletion/service.rs index 10f14f4..22e8578 100644 --- a/src/nostr/lifecycle/deletion/service.rs +++ b/src/nostr/lifecycle/deletion/service.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ nip62, Alphabet, Event, Filter, Kind, PublicKey, RelayUrl, SingleLetterTag, Timestamp, WritePolicyResult, }; @@ -876,7 +876,7 @@ mod tests { self.lifecycle_for_request_result(request_id).await.unwrap() } } - use nostr_relay_builder::prelude::{EventBuilder, EventId, FinalizeEvent, Keys, Tag}; + use nostr_sdk::prelude::{EventBuilder, EventId, FinalizeEvent, Keys, Tag}; use std::path::PathBuf; use std::sync::Arc; @@ -904,7 +904,7 @@ mod tests { fn vanish(keys: &Keys, content: &str) -> Event { EventBuilder::new(Kind::RequestToVanish, content) - .tags(vec![nostr_relay_builder::prelude::Tag::custom( + .tags(vec![nostr_sdk::prelude::Tag::custom( "relay", vec!["ALL_RELAYS".to_string()], )]) @@ -914,7 +914,7 @@ mod tests { fn non_targeting_vanish(keys: &Keys) -> Event { EventBuilder::new(Kind::RequestToVanish, "non-targeting") - .tags(vec![nostr_relay_builder::prelude::Tag::custom( + .tags(vec![nostr_sdk::prelude::Tag::custom( "relay", vec!["wss://other.example".to_string()], )]) @@ -1230,9 +1230,7 @@ mod tests { let service = DeletionService::new(ctx.clone()); let keys = Keys::generate(); let state = EventBuilder::new(Kind::RepoState, "") - .tags(vec![nostr_relay_builder::prelude::Tag::identifier( - "purgatory-only", - )]) + .tags(vec![nostr_sdk::prelude::Tag::identifier("purgatory-only")]) .finalize(&keys) .unwrap(); ctx.purgatory.add_state( @@ -1330,7 +1328,7 @@ mod tests { .unwrap(); ctx.database.save_event(&target).await.unwrap(); let state = EventBuilder::new(Kind::RepoState, "") - .tags(vec![nostr_relay_builder::prelude::Tag::identifier( + .tags(vec![nostr_sdk::prelude::Tag::identifier( "preserve-purgatory", )]) .finalize(&keys) @@ -1376,7 +1374,7 @@ mod tests { let service = DeletionService::new(ctx.clone()); let keys = Keys::generate(); let state = EventBuilder::new(Kind::RepoState, "") - .tags(vec![nostr_relay_builder::prelude::Tag::identifier( + .tags(vec![nostr_sdk::prelude::Tag::identifier( "disrespector-purgatory", )]) .finalize(&keys) diff --git a/src/nostr/lifecycle/deletion/startup.rs b/src/nostr/lifecycle/deletion/startup.rs index 0a5d5df..726f757 100644 --- a/src/nostr/lifecycle/deletion/startup.rs +++ b/src/nostr/lifecycle/deletion/startup.rs @@ -2,7 +2,7 @@ use nostr::nips::nip19::ToBech32; use std::collections::{HashMap, HashSet}; use anyhow::Result; -use nostr_relay_builder::prelude::{Event, Filter, Kind, PublicKey, Timestamp}; +use nostr_sdk::prelude::{Event, Filter, Kind, PublicKey, Timestamp}; use crate::nostr::events::RepositoryAnnouncement; use crate::nostr::lifecycle::{DeletionSource, RequestClassification}; @@ -704,7 +704,7 @@ mod tests { use std::path::PathBuf; use std::sync::Arc; - use nostr_relay_builder::prelude::{EventBuilder, FinalizeEvent, Keys, Tag}; + use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Tag}; use super::super::DeletionContext; use super::*; diff --git a/src/nostr/lifecycle/history.rs b/src/nostr/lifecycle/history.rs index 37fd1d1..385ce6b 100644 --- a/src/nostr/lifecycle/history.rs +++ b/src/nostr/lifecycle/history.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use nostr_lmdb::NostrLmdb; use nostr_memory::MemoryDatabase; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventBuilder, EventId, Filter, FinalizeEvent, Keys, Kind, NostrDatabase, SingleLetterTag, Tag, Timestamp, }; diff --git a/src/nostr/lifecycle/holding.rs b/src/nostr/lifecycle/holding.rs index 216f4d6..eaba16a 100644 --- a/src/nostr/lifecycle/holding.rs +++ b/src/nostr/lifecycle/holding.rs @@ -11,7 +11,7 @@ use std::{collections::BTreeSet, io::ErrorKind}; use nostr_lmdb::NostrLmdb; use nostr_memory::MemoryDatabase; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventBuilder, EventId, Filter, FinalizeEvent, Keys, Kind, NostrDatabase, SingleLetterTag, Tag, Timestamp, }; diff --git a/src/nostr/lifecycle/repository_locks.rs b/src/nostr/lifecycle/repository_locks.rs index 99afb7e..a206943 100644 --- a/src/nostr/lifecycle/repository_locks.rs +++ b/src/nostr/lifecycle/repository_locks.rs @@ -217,7 +217,7 @@ impl RepositoryLifecycle { #[cfg(test)] mod tests { use super::*; - use nostr_relay_builder::prelude::Keys; + use nostr_sdk::prelude::Keys; #[tokio::test] async fn lifecycle_read_locks_can_be_held_concurrently() { diff --git a/src/nostr/lifecycle/tombstones.rs b/src/nostr/lifecycle/tombstones.rs index c29633c..c44b39a 100644 --- a/src/nostr/lifecycle/tombstones.rs +++ b/src/nostr/lifecycle/tombstones.rs @@ -50,7 +50,7 @@ use tokio::sync::{Mutex, MutexGuard}; use nostr_lmdb::NostrLmdb; use nostr_memory::MemoryDatabase; -use nostr_relay_builder::prelude::{ +use nostr_sdk::prelude::{ Alphabet, Event, EventBuilder, EventId, Filter, FinalizeEvent, Keys, Kind, NostrDatabase, PublicKey, SingleLetterTag, Tag, Timestamp, }; diff --git a/src/nostr/mod.rs b/src/nostr/mod.rs index 1d367ba..979dc1c 100644 --- a/src/nostr/mod.rs +++ b/src/nostr/mod.rs @@ -5,4 +5,4 @@ pub mod persistence; pub mod policy; /// Shared database type used across nostr modules. -pub type SharedDatabase = std::sync::Arc; +pub type SharedDatabase = std::sync::Arc; diff --git a/src/nostr/persistence.rs b/src/nostr/persistence.rs index 7091027..1e3f1df 100644 --- a/src/nostr/persistence.rs +++ b/src/nostr/persistence.rs @@ -5,7 +5,7 @@ //! module provides the direct accepted-event save facade for paths outside the //! relay builder. -use nostr_relay_builder::prelude::Event; +use nostr_sdk::prelude::Event; use crate::nostr::SharedDatabase; diff --git a/src/nostr/policy/announcement.rs b/src/nostr/policy/announcement.rs index d6e628e..5097f05 100644 --- a/src/nostr/policy/announcement.rs +++ b/src/nostr/policy/announcement.rs @@ -2,7 +2,7 @@ /// /// Handles validation of NIP-34 repository announcements (kind 30617) /// according to GRASP-01 specification. -use nostr_relay_builder::prelude::{Alphabet, Event, Filter, Kind, PublicKey, SingleLetterTag}; +use nostr_sdk::prelude::{Alphabet, Event, Filter, Kind, PublicKey, SingleLetterTag}; use std::collections::HashSet; use std::time::Duration; diff --git a/src/nostr/policy/mod.rs b/src/nostr/policy/mod.rs index 7ba3f55..f48c7a6 100644 --- a/src/nostr/policy/mod.rs +++ b/src/nostr/policy/mod.rs @@ -26,7 +26,7 @@ pub use crate::git::sync::AlignmentResult; use super::SharedDatabase; use crate::purgatory::Purgatory; -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use std::sync::Arc; /// Shared context for all sub-policies diff --git a/src/nostr/policy/pr_event.rs b/src/nostr/policy/pr_event.rs index fcf5f1d..63970c9 100644 --- a/src/nostr/policy/pr_event.rs +++ b/src/nostr/policy/pr_event.rs @@ -3,7 +3,7 @@ /// Handles validation of NIP-34 PR events (kind 1618) and PR Update events (kind 1619) /// according to GRASP-01 specification. use anyhow::{bail, Result}; -use nostr_relay_builder::prelude::Event; +use nostr_sdk::prelude::Event; use super::PolicyContext; use crate::git; diff --git a/src/nostr/policy/related.rs b/src/nostr/policy/related.rs index cfe04a7..3967f61 100644 --- a/src/nostr/policy/related.rs +++ b/src/nostr/policy/related.rs @@ -3,9 +3,7 @@ /// Handles validation of events that reference accepted repositories or events /// (backward references) and events that are referenced by accepted events /// (forward references). -use nostr_relay_builder::prelude::{ - Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag, -}; +use nostr_sdk::prelude::{Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag}; use super::PolicyContext; diff --git a/src/nostr/policy/result.rs b/src/nostr/policy/result.rs index fc05522..6fad81f 100644 --- a/src/nostr/policy/result.rs +++ b/src/nostr/policy/result.rs @@ -16,7 +16,7 @@ use std::borrow::Cow; use std::sync::OnceLock; use nostr::message::relay::SingleWord; -use nostr_relay_builder::prelude::{MachineReadablePrefix, WritePolicyResult}; +use nostr_sdk::prelude::{MachineReadablePrefix, WritePolicyResult}; /// The `purgatory:` machine-readable prefix. /// diff --git a/src/nostr/policy/state.rs b/src/nostr/policy/state.rs index 988e2e4..113d3af 100644 --- a/src/nostr/policy/state.rs +++ b/src/nostr/policy/state.rs @@ -1,12 +1,12 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; -use nostr_relay_builder::builder::WritePolicyResult; +use nostr_sdk::local_relay::WritePolicyResult; /// State Policy - State event validation + ref alignment /// /// Handles validation of NIP-34 repository state events (kind 30618) /// and aligns git refs with authorized state according to GRASP-01. -use nostr_relay_builder::prelude::Event; +use nostr_sdk::prelude::Event; use super::{accepted_purgatory, duplicate, reject_invalid, reject_restricted, PolicyContext}; use crate::git; diff --git a/src/purgatory/mod.rs b/src/purgatory/mod.rs index db8cfca..27a125d 100644 --- a/src/purgatory/mod.rs +++ b/src/purgatory/mod.rs @@ -1843,7 +1843,9 @@ mod tests { // Add some test data let keys = Keys::generate(); - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); purgatory.add_state( event.clone(), @@ -1905,7 +1907,9 @@ mod tests { assert!(!purgatory.has_pending_events("test-repo")); // Add a state event - let event = EventBuilder::text_note("state").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "state") + .finalize(&keys) + .unwrap(); purgatory.add_state(event, "test-repo".to_string(), keys.public_key(), false); // Now should have pending events @@ -1997,7 +2001,9 @@ mod tests { fn test_pr_event_vs_placeholder() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let event = EventBuilder::text_note("test PR").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test PR") + .finalize(&keys) + .unwrap(); // Add a PR event with actual event purgatory.add_pr( @@ -2049,10 +2055,12 @@ fn test_cleanup_removes_expired_entries() { let keys = Keys::generate(); // Create events - let state_event = EventBuilder::text_note("state event") + let state_event = EventBuilder::new(Kind::TextNote, "state event") + .finalize(&keys) + .unwrap(); + let pr_event = EventBuilder::new(Kind::TextNote, "pr event") .finalize(&keys) .unwrap(); - let pr_event = EventBuilder::text_note("pr event").finalize(&keys).unwrap(); // Add entries to purgatory purgatory.add_state( @@ -2105,10 +2113,12 @@ fn test_cleanup_preserves_non_expired_entries() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let state_event = EventBuilder::text_note("state event") + let state_event = EventBuilder::new(Kind::TextNote, "state event") + .finalize(&keys) + .unwrap(); + let pr_event = EventBuilder::new(Kind::TextNote, "pr event") .finalize(&keys) .unwrap(); - let pr_event = EventBuilder::text_note("pr event").finalize(&keys).unwrap(); // Add fresh entries purgatory.add_state( @@ -2145,8 +2155,12 @@ fn test_cleanup_mixed_expired_and_fresh() { let keys = Keys::generate(); // Add multiple state events for same repo - let event1 = EventBuilder::text_note("event1").finalize(&keys).unwrap(); - let event2 = EventBuilder::text_note("event2").finalize(&keys).unwrap(); + let event1 = EventBuilder::new(Kind::TextNote, "event1") + .finalize(&keys) + .unwrap(); + let event2 = EventBuilder::new(Kind::TextNote, "event2") + .finalize(&keys) + .unwrap(); purgatory.add_state(event1, "test-repo".to_string(), keys.public_key(), false); purgatory.add_state(event2, "test-repo".to_string(), keys.public_key(), false); @@ -2159,8 +2173,12 @@ fn test_cleanup_mixed_expired_and_fresh() { } // Add PR events - let pr1 = EventBuilder::text_note("pr1").finalize(&keys).unwrap(); - let pr2 = EventBuilder::text_note("pr2").finalize(&keys).unwrap(); + let pr1 = EventBuilder::new(Kind::TextNote, "pr1") + .finalize(&keys) + .unwrap(); + let pr2 = EventBuilder::new(Kind::TextNote, "pr2") + .finalize(&keys) + .unwrap(); purgatory.add_pr(pr1, "pr-1".to_string(), "commit-1".to_string(), false); purgatory.add_pr(pr2, "pr-2".to_string(), "commit-2".to_string(), false); @@ -2190,8 +2208,12 @@ fn test_remove_expired_legacy_method() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let state_event = EventBuilder::text_note("state").finalize(&keys).unwrap(); - let pr_event = EventBuilder::text_note("pr").finalize(&keys).unwrap(); + let state_event = EventBuilder::new(Kind::TextNote, "state") + .finalize(&keys) + .unwrap(); + let pr_event = EventBuilder::new(Kind::TextNote, "pr") + .finalize(&keys) + .unwrap(); purgatory.add_state(state_event, "repo".to_string(), keys.public_key(), false); purgatory.add_pr(pr_event, "pr-id".to_string(), "commit".to_string(), false); @@ -2219,8 +2241,12 @@ fn test_expired_event_tracking() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let state_event = EventBuilder::text_note("state").finalize(&keys).unwrap(); - let pr_event = EventBuilder::text_note("pr").finalize(&keys).unwrap(); + let state_event = EventBuilder::new(Kind::TextNote, "state") + .finalize(&keys) + .unwrap(); + let pr_event = EventBuilder::new(Kind::TextNote, "pr") + .finalize(&keys) + .unwrap(); let state_event_id = state_event.id; let pr_event_id = pr_event.id; @@ -2268,8 +2294,12 @@ fn test_cleanup_expired_events() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let event1 = EventBuilder::text_note("event1").finalize(&keys).unwrap(); - let event2 = EventBuilder::text_note("event2").finalize(&keys).unwrap(); + let event1 = EventBuilder::new(Kind::TextNote, "event1") + .finalize(&keys) + .unwrap(); + let event2 = EventBuilder::new(Kind::TextNote, "event2") + .finalize(&keys) + .unwrap(); let event1_id = event1.id; let event2_id = event2.id; @@ -2320,7 +2350,9 @@ fn test_expired_events_prevent_readdition() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); let event_id = event.id; // Add event to purgatory @@ -2381,7 +2413,9 @@ fn test_user_can_resubmit_expired_event() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); let event_id = event.id; // Add event to purgatory @@ -2424,10 +2458,10 @@ async fn test_save_and_restore_state_events() { let keys = Keys::generate(); // Add multiple state events for the same identifier - let event1 = EventBuilder::text_note("state event 1") + let event1 = EventBuilder::new(Kind::TextNote, "state event 1") .finalize(&keys) .unwrap(); - let event2 = EventBuilder::text_note("state event 2") + let event2 = EventBuilder::new(Kind::TextNote, "state event 2") .finalize(&keys) .unwrap(); @@ -2568,7 +2602,9 @@ async fn test_save_and_restore_expired_events() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); let event_id = event.id; // Add and expire event @@ -2707,7 +2743,9 @@ async fn test_downtime_calculation() { let keys = Keys::generate(); // Add state event - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); purgatory.add_state(event.clone(), "repo".to_string(), keys.public_key(), false); @@ -2761,7 +2799,9 @@ async fn test_expiry_times_preserved() { let keys = Keys::generate(); // Add state event - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); purgatory.add_state(event.clone(), "repo".to_string(), keys.public_key(), false); @@ -2808,13 +2848,13 @@ async fn test_multiple_state_events_same_identifier() { let keys3 = Keys::generate(); // Add multiple state events for the same identifier from different authors - let event1 = EventBuilder::text_note("maintainer 1") + let event1 = EventBuilder::new(Kind::TextNote, "maintainer 1") .finalize(&keys1) .unwrap(); - let event2 = EventBuilder::text_note("maintainer 2") + let event2 = EventBuilder::new(Kind::TextNote, "maintainer 2") .finalize(&keys2) .unwrap(); - let event3 = EventBuilder::text_note("maintainer 3") + let event3 = EventBuilder::new(Kind::TextNote, "maintainer 3") .finalize(&keys3) .unwrap(); @@ -2973,7 +3013,9 @@ async fn test_file_cleanup_after_successful_restore() { let keys = Keys::generate(); // Add some data - let event = EventBuilder::text_note("test").finalize(&keys).unwrap(); + let event = EventBuilder::new(Kind::TextNote, "test") + .finalize(&keys) + .unwrap(); purgatory.add_state(event, "repo".to_string(), keys.public_key(), false); // Save to disk @@ -3002,7 +3044,7 @@ async fn test_save_and_restore_announcement_events() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let ann_event = EventBuilder::text_note("announcement event") + let ann_event = EventBuilder::new(Kind::TextNote, "announcement event") .finalize(&keys) .unwrap(); let ann_event_id = ann_event.id; @@ -3057,7 +3099,7 @@ async fn test_soft_expired_announcements_not_persisted() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let ann_event = EventBuilder::text_note("announcement event") + let ann_event = EventBuilder::new(Kind::TextNote, "announcement event") .finalize(&keys) .unwrap(); @@ -3100,7 +3142,7 @@ async fn test_announcement_with_missing_repo_skipped_on_restore() { let purgatory = Purgatory::new(PathBuf::new()); let keys = Keys::generate(); - let ann_event = EventBuilder::text_note("announcement event") + let ann_event = EventBuilder::new(Kind::TextNote, "announcement event") .finalize(&keys) .unwrap(); @@ -3140,7 +3182,7 @@ async fn test_comprehensive_roundtrip() { let keys2 = Keys::generate(); // Add announcement - let ann_event = EventBuilder::text_note("announcement") + let ann_event = EventBuilder::new(Kind::TextNote, "announcement") .finalize(&keys1) .unwrap(); let ann_event_id = ann_event.id; @@ -3153,8 +3195,12 @@ async fn test_comprehensive_roundtrip() { ); // Add multiple state events - let state1 = EventBuilder::text_note("state 1").finalize(&keys1).unwrap(); - let state2 = EventBuilder::text_note("state 2").finalize(&keys2).unwrap(); + let state1 = EventBuilder::new(Kind::TextNote, "state 1") + .finalize(&keys1) + .unwrap(); + let state2 = EventBuilder::new(Kind::TextNote, "state 2") + .finalize(&keys2) + .unwrap(); purgatory.add_state( state1.clone(), @@ -3186,7 +3232,9 @@ async fn test_comprehensive_roundtrip() { purgatory.add_pr_placeholder("pr-2".to_string(), "commit-2".to_string()); // Add and expire an event - let expired_event = EventBuilder::text_note("expired").finalize(&keys1).unwrap(); + let expired_event = EventBuilder::new(Kind::TextNote, "expired") + .finalize(&keys1) + .unwrap(); let expired_id = expired_event.id; purgatory.add_state( expired_event, diff --git a/src/purgatory/promotion_hooks.rs b/src/purgatory/promotion_hooks.rs index 6de9b98..291afb5 100644 --- a/src/purgatory/promotion_hooks.rs +++ b/src/purgatory/promotion_hooks.rs @@ -9,9 +9,9 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::Arc; use async_trait::async_trait; -use nostr_relay_builder::prelude::{Event, WritePolicy, WritePolicyResult}; -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use nostr_sdk::prelude::PublicKey; +use nostr_sdk::prelude::{Event, WritePolicy, WritePolicyResult}; use tracing::{debug, info, warn}; use crate::git::sync::{PurgatoryPromotionHooks, PurgatorySaveContext}; diff --git a/src/purgatory/sync/context.rs b/src/purgatory/sync/context.rs index 897c0ca..18b4893 100644 --- a/src/purgatory/sync/context.rs +++ b/src/purgatory/sync/context.rs @@ -189,7 +189,7 @@ pub trait SyncContext: Send + Sync { // Real Implementation // ============================================================================= -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use std::process::Command; use std::sync::Arc; use tracing::debug; diff --git a/src/server.rs b/src/server.rs index 3b2878f..b605b32 100644 --- a/src/server.rs +++ b/src/server.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use std::time::Duration; use anyhow::{Context, Result}; -use nostr_relay_builder::LocalRelay; +use nostr_sdk::local_relay::LocalRelay; use tokio::net::TcpListener; use tokio::task::JoinHandle; use tracing::{error, info, warn}; diff --git a/src/sync/mod.rs b/src/sync/mod.rs index c506a5f..7c1dea2 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -52,7 +52,7 @@ use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use crate::config::Config; use crate::nostr::builder::Nip34WritePolicy; use crate::nostr::SharedDatabase; -use nostr_relay_builder::prelude::LocalRelay; +use nostr_sdk::prelude::LocalRelay; const MAX_PURGATORY_DEPENDENCY_EVENTS_PER_TICK: usize = 32; const MAX_PURGATORY_FILTER_ACTIONS_PER_TICK: usize = 1; @@ -3699,7 +3699,7 @@ impl SyncManager { rejected_events_index: &Arc, save_context: crate::nostr::persistence::SaveContext, ) -> ProcessResult { - use nostr_relay_builder::prelude::{WritePolicy, WritePolicyResult}; + use nostr_sdk::prelude::{WritePolicy, WritePolicyResult}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; // Check if event already exists match database.event_by_id(&event.id).await { @@ -5076,7 +5076,7 @@ mod tests { .expect("build metadata event"); pagination.record_event(&event); } - let note = EventBuilder::text_note("one note") + let note = EventBuilder::new(Kind::TextNote, "one note") .custom_created_at(Timestamp::from_secs(100)) .finalize(&keys) .expect("build text note"); diff --git a/src/sync/rejected_index.rs b/src/sync/rejected_index.rs index dac53e4..f687db1 100644 --- a/src/sync/rejected_index.rs +++ b/src/sync/rejected_index.rs @@ -1036,11 +1036,11 @@ impl RejectedEventsIndex { #[cfg(test)] mod tests { use super::*; - use nostr_sdk::prelude::{EventBuilder, FinalizeUnsignedEvent, Keys, SignEvent}; + use nostr_sdk::prelude::{EventBuilder, FinalizeUnsignedEvent, Keys, Kind, SignEvent}; async fn create_test_event() -> Event { let keys = Keys::generate(); - let unsigned = nostr_sdk::prelude::EventBuilder::text_note("test") + let unsigned = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "test") .finalize_unsigned(keys.public_key()); keys.sign_event(unsigned).unwrap() } @@ -1290,10 +1290,15 @@ mod tests { RejectedEventsIndex::new(Duration::from_millis(50), Duration::from_secs(604800)); let keys = Keys::generate(); let dependency_event = keys - .sign_event(EventBuilder::text_note("dependency").finalize_unsigned(keys.public_key())) + .sign_event( + EventBuilder::new(Kind::TextNote, "dependency") + .finalize_unsigned(keys.public_key()), + ) .unwrap(); let permanent_event = keys - .sign_event(EventBuilder::text_note("permanent").finalize_unsigned(keys.public_key())) + .sign_event( + EventBuilder::new(Kind::TextNote, "permanent").finalize_unsigned(keys.public_key()), + ) .unwrap(); let pubkey = keys.public_key(); let identifier = "test-repo".to_string(); @@ -1329,11 +1334,11 @@ mod tests { let keys1 = Keys::generate(); let keys2 = Keys::generate(); - let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("test1") + let unsigned1 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "test1") .finalize_unsigned(keys1.public_key()); let event1 = keys1.sign_event(unsigned1).unwrap(); - let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("test2") + let unsigned2 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "test2") .finalize_unsigned(keys2.public_key()); let event2 = keys2.sign_event(unsigned2).unwrap(); @@ -1374,12 +1379,12 @@ mod tests { let keys = Keys::generate(); // Create an announcement event - let unsigned_ann = nostr_sdk::prelude::EventBuilder::text_note("announcement") + let unsigned_ann = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "announcement") .finalize_unsigned(keys.public_key()); let event_ann = keys.sign_event(unsigned_ann).unwrap(); // Create a state event - let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state") + let unsigned_state = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "state") .finalize_unsigned(keys.public_key()); let event_state = keys.sign_event(unsigned_state).unwrap(); @@ -1434,12 +1439,12 @@ mod tests { let keys = Keys::generate(); // Create an announcement event - let unsigned_ann = nostr_sdk::prelude::EventBuilder::text_note("announcement") + let unsigned_ann = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "announcement") .finalize_unsigned(keys.public_key()); let event_ann = keys.sign_event(unsigned_ann).unwrap(); // Create a state event - let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state") + let unsigned_state = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "state") .finalize_unsigned(keys.public_key()); let event_state = keys.sign_event(unsigned_state).unwrap(); @@ -1581,11 +1586,11 @@ mod tests { let keys = Keys::generate(); // Create two events - let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("event1") + let unsigned1 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "event1") .finalize_unsigned(keys.public_key()); let event1 = keys.sign_event(unsigned1).unwrap(); - let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("event2") + let unsigned2 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "event2") .finalize_unsigned(keys.public_key()); let event2 = keys.sign_event(unsigned2).unwrap(); @@ -1794,12 +1799,12 @@ mod tests { let keys = Keys::generate(); // Create announcement event - let unsigned_ann = nostr_sdk::prelude::EventBuilder::text_note("announcement") + let unsigned_ann = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "announcement") .finalize_unsigned(keys.public_key()); let event_ann = keys.sign_event(unsigned_ann).unwrap(); // Create state event - let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state") + let unsigned_state = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "state") .finalize_unsigned(keys.public_key()); let event_state = keys.sign_event(unsigned_state).unwrap(); @@ -1849,15 +1854,15 @@ mod tests { let keys = Keys::generate(); // Create events with different rejection reasons - let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("event1") + let unsigned1 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "event1") .finalize_unsigned(keys.public_key()); let event1 = keys.sign_event(unsigned1).unwrap(); - let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("event2") + let unsigned2 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "event2") .finalize_unsigned(keys.public_key()); let event2 = keys.sign_event(unsigned2).unwrap(); - let unsigned3 = nostr_sdk::prelude::EventBuilder::text_note("event3") + let unsigned3 = nostr_sdk::prelude::EventBuilder::new(Kind::TextNote, "event3") .finalize_unsigned(keys.public_key()); let event3 = keys.sign_event(unsigned3).unwrap(); diff --git a/src/sync/relay_connection.rs b/src/sync/relay_connection.rs index ad0df4f..8c245b5 100644 --- a/src/sync/relay_connection.rs +++ b/src/sync/relay_connection.rs @@ -735,7 +735,7 @@ impl RelayConnection { #[cfg(test)] mod tests { use super::*; - use nostr_relay_builder::prelude::LocalRelayBuilder; + use nostr_sdk::prelude::LocalRelayBuilder; use std::future::pending; #[tokio::test] @@ -804,7 +804,7 @@ mod tests { let other = LocalRelayBuilder::default().build(); other.run().await.expect("start other relay"); - let expected = EventBuilder::text_note("only on the other relay") + let expected = EventBuilder::new(Kind::TextNote, "only on the other relay") .finalize(&Keys::generate()) .expect("build event"); other diff --git a/tests/common/mock_relay.rs b/tests/common/mock_relay.rs index 377784b..7e3caff 100644 --- a/tests/common/mock_relay.rs +++ b/tests/common/mock_relay.rs @@ -43,7 +43,7 @@ use hyper::server::conn::http1; use hyper::service::service_fn; use hyper::{Request, Response, StatusCode}; use hyper_util::rt::TokioIo; -use nostr_relay_builder::prelude::*; +use nostr_sdk::prelude::*; use tokio::net::TcpListener; use tokio::sync::oneshot; @@ -267,8 +267,8 @@ async fn handle_request( /// Derive the Sec-WebSocket-Accept key from the request key. fn derive_accept_key(request_key: &[u8]) -> String { - use nostr::hashes::sha1::Hash as Sha1Hash; - use nostr::hashes::{Hash, HashEngine}; + use bitcoin_hashes::sha1::Hash as Sha1Hash; + use bitcoin_hashes::{Hash, HashEngine}; const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; @@ -307,6 +307,9 @@ async fn wait_for_server_ready(port: u16) { #[cfg(test)] mod tests { use super::*; + // Disambiguate from `nostr_sdk::local_relay::MockRelay`, which the SDK + // prelude also exports since the relay builder was merged into nostr-sdk. + use super::MockRelay; use nostr_sdk::prelude::*; use std::time::Duration; @@ -339,7 +342,7 @@ mod tests { tokio::time::sleep(Duration::from_millis(500)).await; // Create and send a simple event - let event = EventBuilder::text_note("Test note from MockRelay test") + let event = EventBuilder::new(Kind::TextNote, "Test note from MockRelay test") .finalize(&keys) .expect("Failed to sign event"); diff --git a/tests/git_response_streaming.rs b/tests/git_response_streaming.rs index 1df781c..6128250 100644 --- a/tests/git_response_streaming.rs +++ b/tests/git_response_streaming.rs @@ -22,7 +22,7 @@ use ngit_grasp::nostr::lifecycle::{ use ngit_grasp::nostr::SharedDatabase; use ngit_grasp::purgatory::Purgatory; use ngit_grasp::sync::rejected_index::RejectedEventsIndex; -use nostr_relay_builder::prelude::LocalRelayBuilder; +use nostr_sdk::prelude::LocalRelayBuilder; use nostr_sdk::prelude::*; use tokio::sync::Semaphore; use tokio::time::timeout; diff --git a/tests/lifecycle/replaceable_history.rs b/tests/lifecycle/replaceable_history.rs index 96aff47..5c9cc1f 100644 --- a/tests/lifecycle/replaceable_history.rs +++ b/tests/lifecycle/replaceable_history.rs @@ -357,7 +357,7 @@ async fn newer_30617_supersedes_older_and_preserves_old_in_history() { #[tokio::test] async fn sync_style_admit_then_save_captures_superseded_history_once() { - use nostr_relay_builder::prelude::{WritePolicy, WritePolicyResult}; + use nostr_sdk::prelude::{WritePolicy, WritePolicyResult}; let git_dir = tempfile::tempdir().expect("git tempdir"); let db: SharedDatabase = Arc::new(nostr_memory::MemoryDatabase::unbounded()); diff --git a/tests/purgatory_persistence.rs b/tests/purgatory_persistence.rs index 5d4a21f..94664d0 100644 --- a/tests/purgatory_persistence.rs +++ b/tests/purgatory_persistence.rs @@ -40,7 +40,9 @@ use std::time::Duration; /// Helper to create a test event async fn create_test_event(keys: &Keys, content: &str) -> Event { - EventBuilder::text_note(content).finalize(keys).unwrap() + EventBuilder::new(Kind::TextNote, content) + .finalize(keys) + .unwrap() } /// Helper to create a state event with specific refs