diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 627fad2a6..f29bfe0c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,8 +60,8 @@ jobs: - nostr-keyring - nostr-keyring --features async - nostr-sdk + - nostr-sdk --features local-relay - nostr-sdk --target wasm32-unknown-unknown - - nostr-relay-builder - nostr-connect - nwc - nwc --target wasm32-unknown-unknown diff --git a/Cargo.lock b/Cargo.lock index e15bd9bba..8f68ae441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1849,7 +1849,6 @@ dependencies = [ "dialoguer", "futures-core", "nostr", - "nostr-relay-builder", "nostr-sdk", "opaquerr", "tokio", @@ -1967,36 +1966,16 @@ dependencies = [ ] [[package]] -name = "nostr-relay-builder" +name = "nostr-sdk" version = "0.45.0-alpha.3" dependencies = [ "async-utility", "async-wsocket", - "atomic-destructor", "base64", "faster-hex", + "futures", "hyper", "hyper-util", - "negentropy", - "nostr", - "nostr-database", - "nostr-lmdb", - "nostr-memory", - "nostr-sdk", - "opaquerr", - "tokio", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "nostr-sdk" -version = "0.45.0-alpha.3" -dependencies = [ - "async-utility", - "async-wsocket", - "faster-hex", - "futures", "lru", "negentropy", "nostr", @@ -2007,7 +1986,6 @@ dependencies = [ "nostr-lmdb", "nostr-memory", "nostr-ndb", - "nostr-relay-builder", "opaquerr", "tokio", "tokio-stream", @@ -2024,12 +2002,9 @@ dependencies = [ "nostr", "nostr-database", "nostr-database-test-suite", - "nostr-relay-builder", "rusqlite", "serde_json", - "tempfile", "tokio", - "tracing-subscriber", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 90450203e..714e54ff5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,6 @@ nostr-gossip-sqlite = { version = "0.45.0-alpha.3", path = "./gossip/nostr-gossi nostr-gossip-test-suite = { path = "./gossip/nostr-gossip-test-suite" } nostr-lmdb = { version = "0.45.0-alpha.3", path = "./database/nostr-lmdb", default-features = false } nostr-ndb = { version = "0.45.0-alpha.3", path = "./database/nostr-ndb", default-features = false } -nostr-relay-builder = { version = "0.45.0-alpha.3", path = "./crates/nostr-relay-builder", default-features = false } nostr-sdk = { version = "0.45.0-alpha.3", path = "./sdk", default-features = false } opaquerr = "0.1" reqwest = { version = "0.12", default-features = false } diff --git a/README.md b/README.md index cfcbd495d..fea2f80af 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ The project is split up into several crates: - Remote File Storage implementations: - [**nostr-blossom**](./rfs/nostr-blossom): A library for interacting with the Blossom protocol - [**nostr-keyring**](./crates/nostr-keyring): Nostr Keyring -- [**nostr-relay-builder**](./crates/nostr-relay-builder): Build your own custom nostr relay - [**nostr-sdk**](./sdk): A full-featured SDK for building high-performance and reliable nostr applications - [**nwc**](./crates/nwc): Nostr Wallet Connect (NWC) client (NIP-47) diff --git a/contrib/scripts/check-crates.sh b/contrib/scripts/check-crates.sh index 8fd248b7f..248ef51b3 100755 --- a/contrib/scripts/check-crates.sh +++ b/contrib/scripts/check-crates.sh @@ -34,8 +34,8 @@ buildargs=( "-p nostr-keyring" "-p nostr-keyring --features async" "-p nostr-sdk" + "-p nostr-sdk --features local-relay" "-p nostr-sdk --target wasm32-unknown-unknown" - "-p nostr-relay-builder" "-p nostr-connect" "-p nwc" "-p nwc --target wasm32-unknown-unknown" diff --git a/crates/nostr-relay-builder/CHANGELOG.md b/crates/nostr-relay-builder/CHANGELOG.md deleted file mode 100644 index 0ffd6bf1d..000000000 --- a/crates/nostr-relay-builder/CHANGELOG.md +++ /dev/null @@ -1,151 +0,0 @@ -# Changelog - - - - - - - - -## Unreleased - -### Breaking changes - -- Change `LocalRelay::new` constructor signature (https://github.com/rust-nostr/nostr/pull/1147) -- Allow only a single `write` and `query` policy (https://github.com/rust-nostr/nostr/pull/1165) -- Replace `PolicyResult` with `WritePolicyResult` and `QueryPolicyResult` (https://github.com/rust-nostr/nostr/pull/1166) -- Remove `tor` feature (https://github.com/rust-nostr/nostr/pull/1253) -- Make the filter mutable in `QueryPolicy::admit_query` (https://github.com/rust-nostr/nostr/pull/1302) - -### Changes - -- Rename all `RelayBuilder*` structs and enums to `LocalRelayBuilder*` (https://github.com/rust-nostr/nostr/pull/1145) -- Move write policy checks between mode and ephemeral kind checks (https://github.com/rust-nostr/nostr/pull/1155) -- Bump MSRV to 1.85.0 (https://github.com/rust-nostr/nostr/pull/1267) -- Replace `hex` dependency with `faster-hex` (https://github.com/rust-nostr/nostr/pull/1319) - -### Added - -- Add `LocalRelay::add_event` -- Add `LocalRelay::builder` constructor (https://github.com/rust-nostr/nostr/pull/1147) -- Add `LocalRelayBuilder::build` method (https://github.com/rust-nostr/nostr/pull/1147) -- Impl `Default` for `LocalRelay` (https://github.com/rust-nostr/nostr/pull/1147) -- Add `LocalRelay::sync_with` method (https://github.com/rust-nostr/nostr/pull/1146) -- Reject expired events and ensure they are not sent to clients (https://github.com/rust-nostr/nostr/pull/1183) - -## v0.44.1 - 2026/05/19 - -### Fixed - -- Check port availability with actual relay IP (https://github.com/rust-nostr/nostr/pull/1207) - -## v0.44.0 - 2025/11/06 - -### Breaking change - -- Change `LocalRelay::new`, `LocalRelay::run`, `LocalRelay::url` and `LocalRelay::hidden_service` signatures (https://github.com/rust-nostr/nostr/pull/1122) - -### Added - -- Add `RelayBuilder::max_filter_limit` and `RelayBuilder::default_filter_limit` to limit the filter's limit (https://github.com/rust-nostr/nostr/pull/1096) -- Add `RelayBuilder::max_subid_length` to enforce a limit on subscription ID size (https://github.com/rust-nostr/nostr/pull/1098) -- Add support for multi-filter REQ (https://github.com/rust-nostr/nostr/pull/1099) -- Add support for NIP-42 DMs, DMs returned for the mentioned public key only (https://github.com/rust-nostr/nostr/pull/1104) - -### Fixed - -- Consider a PoW difficulty if it’s greater than 0 in `RelayBuilder::min_pow` (https://github.com/rust-nostr/nostr/pull/1085) - -## v0.43.0 - 2025/07/28 - -No notable changes in this release. - -## v0.42.0 - 2025/05/20 - -### Added - -- Add support for NIP-70 protected events (https://github.com/rust-nostr/nostr/pull/875) - -## v0.41.0 - 2025/04/15 - -### Changed - -- Send `CLOSED` if all possible events have been served (https://github.com/rust-nostr/nostr/pull/778) - -## v0.40.0 - 2025/03/18 - -No notable changes in this release. - -## v0.39.0 - 2025/01/31 - -### Changed - -- Refactor shutdown mechanism to use `Notify` over `broadcast` -- Increase default max REQs to 500 - -### Added - -- Custom http server - -### Removed - -- Remove `thiserror` dep -- Remove `async-trait` dep - -## v0.38.0 - 2024/12/31 - -### Added - -- Add NIP42 support -- Add negentropy support -- Add read/write policy plugins - -## v0.37.0 - 2024/11/27 - -### Changed - -- Port selection by using random port generation - -### Added - -- Add `RelayTestOptions` - -## v0.36.0 - 2024/11/05 - -### Changed - -- Refactor `Session::check_rate_limit` method -- Return error if event was deleted - -### Added - -- Add `LocalRelay` and `RelayBuilder` -- Allow to serve local relay as hidden service -- Allow to set number of max connections allowed -- Add `RelayBuilderMode` -- Add min POW difficulty option to `RelayBuilder` -- Handle ephemeral events - -## v0.35.0 - 2024/09/19 - -First release. diff --git a/crates/nostr-relay-builder/Cargo.toml b/crates/nostr-relay-builder/Cargo.toml deleted file mode 100644 index 65a377afa..000000000 --- a/crates/nostr-relay-builder/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "nostr-relay-builder" -version = "0.45.0-alpha.3" -edition.workspace = true -description = "Build your own custom nostr relay!" -authors.workspace = true -homepage.workspace = true -repository.workspace = true -license.workspace = true -readme = "README.md" -rust-version.workspace = true -keywords = ["nostr", "relay", "builder"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[features] -default = ["ring", "rustls-tls-webpki-roots"] -aws_lc_rs = ["async-wsocket/aws_lc_rs", "nostr-sdk/aws_lc_rs"] -native-tls = ["async-wsocket/native-tls", "nostr-sdk/native-tls"] -native-tls-vendored = ["async-wsocket/native-tls-vendored", "nostr-sdk/native-tls-vendored"] -ring = ["async-wsocket/ring", "nostr-sdk/ring"] -rustls-tls-native-roots = ["async-wsocket/rustls-tls-native-roots", "nostr-sdk/rustls-tls-native-roots"] -rustls-tls-webpki-roots = ["async-wsocket/rustls-tls-webpki-roots", "nostr-sdk/rustls-tls-webpki-roots"] - -[dependencies] -async-utility.workspace = true -async-wsocket.workspace = true -atomic-destructor.workspace = true -faster-hex = { workspace = true, features = ["std"] } -negentropy = { workspace = true, features = ["std"] } -nostr = { workspace = true, default-features = false, features = ["std", "rand"] } -nostr-database.workspace = true -nostr-memory.workspace = true -nostr-sdk.workspace = true -opaquerr = { workspace = true, features = ["alloc"] } -tokio = { workspace = true, features = ["macros", "net", "sync"] } -tracing.workspace = true - -[dev-dependencies] -base64 = { workspace = true, features = ["std"] } -hyper = { version = "1.9", features = ["server", "http1"] } -hyper-util = { version = "0.1", features = ["tokio"] } -nostr-lmdb = { path = "../../database/nostr-lmdb", default-features = false } -tokio = { workspace = true, features = ["signal"] } -tracing-subscriber = { workspace = true, features = ["env-filter"] } - -[[example]] -name = "hyper" - -[[example]] -name = "mock" - -[[example]] -name = "policy" - -[[example]] -name = "simple_relay" diff --git a/crates/nostr-relay-builder/README.md b/crates/nostr-relay-builder/README.md deleted file mode 100644 index 8b442608e..000000000 --- a/crates/nostr-relay-builder/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Nostr Relay Builder - -This library helps you stand up fully configurable relays without re-implementing policies, storage, or protocol minutiae. - -The crate exposes two main entry points: - -- `LocalRelay` – run a fully fledged relay inside your process. -- `MockRelay` – run an ephemeral relay for unit/integration tests. - -## Quick start - -```rust,no_run -use nostr_lmdb::NostrLmdb; -use nostr_relay_builder::prelude::*; - -#[tokio::main] -async fn main() -> Result<(), Box> { - // Open a database (all databases that implements `NostrDatabase` trait can be used). - let database = NostrLmdb::open("nostr-relay").await?; - - // Create the relay. - let relay = LocalRelay::builder() - .port(7777) - .database(database) - .rate_limit(RateLimit { - max_reqs: 128, - notes_per_minute: 30, - }) - .build(); - - // Start the relay. - relay.run().await?; - - println!("Relay listening on {}", relay.url().await); - - // Keep the process running. - tokio::signal::ctrl_c().await?; - - Ok(()) -} -``` - -More examples can be found in the [examples directory](./examples). - -## Supported NIPs - -| Supported | NIP | -|:---------:|------------------------------------------------------------------------------------------------------| -| ✅ | [01 - Basic protocol flow description](https://github.com/nostr-protocol/nips/blob/master/01.md) | -| ✅ | [09 - Event Deletion](https://github.com/nostr-protocol/nips/blob/master/09.md) | -| ❌ | [11 - Relay Information Document](https://github.com/nostr-protocol/nips/blob/master/11.md) | -| ✅ | [17 - Private Direct Messages](https://github.com/nostr-protocol/nips/blob/master/17.md) | -| 🔧* | [40 - Expiration Timestamp](https://github.com/nostr-protocol/nips/blob/master/40.md) | -| ✅ | [42 - Authentication of clients to relays](https://github.com/nostr-protocol/nips/blob/master/42.md) | -| 🔧 | [50 - Search Capability](https://github.com/nostr-protocol/nips/blob/master/50.md) | -| 🔧 | [62 - Request to Vanish](https://github.com/nostr-protocol/nips/blob/master/62.md) | -| ✅ | [70 - Protected Events](https://github.com/nostr-protocol/nips/blob/master/70.md) | -| ✅ | [77 - Negentropy Syncing](https://github.com/nostr-protocol/nips/blob/master/77.md) | - -**Legend:** -- ✅ Fully supported -- 🔧 Depends on the database implementation -- ❌ Not supported - -*: The relay does not accept or send expired events. The database have to delete them. - -## Changelog - -All notable changes to this library are documented in the [CHANGELOG.md](CHANGELOG.md). - -## State - -**This library is in an ALPHA state**, things that are implemented generally work but the API will change in breaking ways. - -## Donations - -`rust-nostr` is free and open-source. This means we do not earn any revenue by selling it. Instead, we rely on your financial support. If you actively use any of the `rust-nostr` libs/software/services, then please [donate](https://rust-nostr.org/donate). - -## License - -This project is distributed under the MIT software license - see the [LICENSE](../../LICENSE) file for details diff --git a/crates/nostr-relay-builder/src/error.rs b/crates/nostr-relay-builder/src/error.rs deleted file mode 100644 index ca5585157..000000000 --- a/crates/nostr-relay-builder/src/error.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2022-2023 Yuki Kishimoto -// Copyright (c) 2023-2025 Rust Nostr Developers -// Distributed under the MIT software license - -//! Relay builder error - -use nostr::Event; -use tokio::sync::broadcast; - -opaquerr::define_kind! { - /// Relay builder error kind - pub ErrorKind { - /// Nostr protocol error - Protocol => "nostr protocol error", - /// Database error - Database => "database error", - /// I/O error - IO => "I/O error", - /// Anything not covered by the stable categories above. - Other => "other error", - } -} - -opaquerr::define_error! { - /// Relay builder error - pub Error(ErrorKind) - - from { - nostr::error::Error => ErrorKind::Protocol, - nostr_database::error::Error => ErrorKind::Database, - std::io::Error => ErrorKind::IO, - nostr_sdk::error::Error => ErrorKind::Other, - async_wsocket::Error => ErrorKind::Other, - negentropy::Error => ErrorKind::Other, - tokio::sync::TryAcquireError => ErrorKind::Other, - broadcast::error::SendError => ErrorKind::Other, - faster_hex::Error => ErrorKind::Other, - } -} diff --git a/crates/nostr-relay-builder/src/lib.rs b/crates/nostr-relay-builder/src/lib.rs deleted file mode 100644 index 543cf379e..000000000 --- a/crates/nostr-relay-builder/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2022-2023 Yuki Kishimoto -// Copyright (c) 2023-2025 Rust Nostr Developers -// Distributed under the MIT software license - -//! Nostr Relay Builder and Mock Relay for tests - -#![forbid(unsafe_code)] -#![warn(missing_docs)] -#![warn(rustdoc::bare_urls)] -#![warn(clippy::large_futures)] -#![doc = include_str!("../README.md")] - -pub mod builder; -pub mod error; -pub mod local; -pub mod mock; -pub mod prelude; - -pub use self::builder::LocalRelayBuilder; -pub use self::error::Error; -pub use self::local::LocalRelay; -pub use self::mock::MockRelay; diff --git a/crates/nostr-relay-builder/src/prelude.rs b/crates/nostr-relay-builder/src/prelude.rs deleted file mode 100644 index 5ef70f38b..000000000 --- a/crates/nostr-relay-builder/src/prelude.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2022-2023 Yuki Kishimoto -// Copyright (c) 2023-2025 Rust Nostr Developers -// Distributed under the MIT software license - -//! Prelude - -#![allow(unknown_lints)] -#![allow(unused_imports)] -#![allow(ambiguous_glob_reexports)] -#![doc(hidden)] - -pub use nostr::prelude::*; -pub use nostr_database::prelude::*; -pub use nostr_sdk::relay::SyncOptions; - -pub use crate::builder::{self, *}; -pub use crate::error::{Error, ErrorKind}; -pub use crate::local::{self, *}; -pub use crate::mock::{self, *}; -pub use crate::*; diff --git a/crates/nostr-relay-builder/tests/plugins.rs b/crates/nostr-relay-builder/tests/plugins.rs deleted file mode 100644 index 946511f53..000000000 --- a/crates/nostr-relay-builder/tests/plugins.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2022-2023 Yuki Kishimoto -// Copyright (c) 2023-2025 Rust Nostr Developers -// Distributed under the MIT software license - -use nostr::event::{EventBuilder, FinalizeEvent, Tag}; -use nostr::filter::Filter; -use nostr::key::Keys; -use nostr::util::BoxedFuture; -use nostr_memory::MemoryDatabase; -use nostr_relay_builder::LocalRelay; -use nostr_relay_builder::builder::{QueryPolicy, QueryPolicyResult}; -use nostr_sdk::client::Client; - -const UPDATE_TAG: &str = "updated"; - -#[derive(Debug)] -struct UpdateFilterPlugin; - -impl QueryPolicy for UpdateFilterPlugin { - fn admit_query<'a>( - &'a self, - query: &'a mut Filter, - _addr: &'a std::net::SocketAddr, - ) -> BoxedFuture<'a, QueryPolicyResult> { - Box::pin(async move { - *query = query.clone().hashtag(UPDATE_TAG); - QueryPolicyResult::Accept - }) - } -} - -#[tokio::test] -async fn update_filter() { - let relay = LocalRelay::builder() - .database(MemoryDatabase::unbounded()) - .query_policy(UpdateFilterPlugin) - .build(); - relay.run().await.unwrap(); - - let keys = Keys::generate(); - let client = Client::default(); - - client - .add_relay(relay.url().await) - .and_connect() - .await - .unwrap(); - - // Event with our target tag - let event = EventBuilder::text_note(":)") - .tag(Tag::hashtag(UPDATE_TAG)) - .finalize(&keys) - .unwrap(); - client.send_event(&event).await.unwrap(); - - // This event has a random tag and should be filtered out in the REQ. - // It would only appear if the filter had not been updated correctly. - let event = EventBuilder::text_note(":)") - .tag(Tag::hashtag("TEST")) - .finalize(&keys) - .unwrap(); - client.send_event(&event).await.unwrap(); - - // Empty filter to get all events. It should be updated to have `UPDATE_TAG` - let events = client.fetch_events(Filter::new()).await.unwrap(); - - assert!(!events.is_empty(), "Should not be empty"); - assert!( - events - .iter() - .all(|e| { e.tags.hashtags().all(|hashtag| hashtag == UPDATE_TAG) }), - "All tags should have the updated filter tag" - ); -} diff --git a/database/nostr-sqlite/Cargo.toml b/database/nostr-sqlite/Cargo.toml index 46941d193..e1bb619e2 100644 --- a/database/nostr-sqlite/Cargo.toml +++ b/database/nostr-sqlite/Cargo.toml @@ -29,7 +29,4 @@ tokio = { workspace = true, features = ["sync"] } [dev-dependencies] nostr-database-test-suite.workspace = true -nostr-relay-builder = { path = "../../crates/nostr-relay-builder", default-features = false } -tempfile.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] } -tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index d0fd88859..66c6a929e 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -23,6 +23,7 @@ native-tls-vendored = ["async-wsocket/native-tls-vendored"] ring = ["async-wsocket/ring"] rustls-tls-native-roots = ["async-wsocket/rustls-tls-native-roots"] rustls-tls-webpki-roots = ["async-wsocket/rustls-tls-webpki-roots"] +local-relay = ["dep:nostr-memory", "tokio/net"] [dependencies] async-utility.workspace = true @@ -34,6 +35,7 @@ negentropy = { workspace = true, features = ["std"] } nostr = { workspace = true, features = ["std", "rand", "os-rng"] } nostr-database.workspace = true nostr-gossip.workspace = true +nostr-memory = { workspace = true, optional = true } opaquerr = { workspace = true, features = ["alloc"] } tokio = { workspace = true, features = ["macros", "sync"] } tokio-stream = { version = "0.1", features = ["sync"] } @@ -41,14 +43,16 @@ tracing = { workspace = true, features = ["std"] } universal-time = { workspace = true, features = ["std"] } [dev-dependencies] +base64 = { workspace = true, features = ["std"] } +hyper = { version = "1.9", features = ["server", "http1"] } +hyper-util = { version = "0.1", features = ["tokio"] } nostr = { path = "../crates/nostr", default-features = false, features = ["all-nips"] } nostr-connect = { path = "../signer/nostr-connect", default-features = false } nostr-lmdb = { path = "../database/nostr-lmdb", default-features = false } nostr-ndb = { path = "../database/nostr-ndb", default-features = false } nostr-memory = { path = "../database/nostr-memory", default-features = false } nostr-gossip-memory = { path = "../gossip/nostr-gossip-memory", default-features = false } -nostr-relay-builder = { path = "../crates/nostr-relay-builder", default-features = false } -tokio = { workspace = true, features = ["macros", "rt"] } +tokio = { workspace = true, features = ["full"] } tracing-subscriber = { workspace = true, features = ["env-filter"] } [[example]] @@ -96,5 +100,21 @@ name = "lmdb" [[example]] name = "whitelist" +[[example]] +name = "local-relay-hyper" +required-features = ["local-relay"] + +[[example]] +name = "local-relay-mock" +required-features = ["local-relay"] + +[[example]] +name = "local-relay-policy" +required-features = ["local-relay"] + +[[example]] +name = "local-relay-simple" +required-features = ["local-relay"] + [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bench)'] } diff --git a/sdk/README.md b/sdk/README.md index cdde84ecc..1f32c1737 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -10,8 +10,15 @@ A full-featured SDK for building high-performance and reliable nostr applications. +The SDK can be used to build both sides of a nostr application: + +- clients, bots, and services that connect to existing relays; +- local relays that run inside your process, including mock relays for tests. + ## Getting started +### Client + ```rust,no_run use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; @@ -37,7 +44,7 @@ async fn main() -> Result<(), Box> { // Add relays client.add_relay("wss://relay.damus.io").await?; client.add_relay("ws://jgqaglhautb4k6e6i2g34jakxiemqp6z4wynlirltuukgkft2xuglmqd.onion").await?; - + // Add read relay client.add_relay("wss://relay.nostr.info").capabilities(RelayCapabilities::READ).await?; @@ -52,8 +59,80 @@ async fn main() -> Result<(), Box> { } ``` +### Local Relay + +The `local-relay` feature enables in-process relays without re-implementing policies, storage, or protocol handling. + +- `LocalRelay` runs a fully fledged relay inside your process. +- `MockRelay` runs an ephemeral relay for unit and integration tests. + +```rust,ignore +use std::time::Duration; + +use nostr_sdk::prelude::*; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create the relay. If no database is provided, an in-memory database is used. + let relay = LocalRelay::builder() + .port(7777) + .rate_limit(RateLimit { + max_reqs: 128, + notes_per_minute: 30, + }) + .build(); + + // Start the relay. + relay.run().await?; + + println!("Relay listening on {}", relay.url().await); + + // Keep the process running + loop { + tokio::time::sleep(Duration::from_secs(60)).await; + } +} +``` + More examples can be found in the [examples directory](./examples). +## Crate Feature Flags + +The following crate feature flags are available: + +| Feature | Default | Description | +|---------------------------|:-------:|--------------------------------------------------| +| `ring` | Yes | Enable `ring` crypto provider | +| `rustls-tls-webpki-roots` | Yes | Enable rustls with bundled Mozilla root certs | +| `aws_lc_rs` | No | Enable `aws-lc-rs` crypto provider | +| `native-tls` | No | Enable platform-native TLS | +| `native-tls-vendored` | No | Enable platform-native TLS with vendored OpenSSL | +| `rustls-tls-native-roots` | No | Enable rustls with platform-native root certs | +| `local-relay` | No | Enable `nostr_sdk::local_relay` module | + +## Local Relay supported NIPs + +| Supported | NIP | +|:---------:|------------------------------------------------------------------------------------------------------| +| ✅ | [01 - Basic protocol flow description](https://github.com/nostr-protocol/nips/blob/master/01.md) | +| ✅ | [09 - Event Deletion](https://github.com/nostr-protocol/nips/blob/master/09.md) | +| ❌ | [11 - Relay Information Document](https://github.com/nostr-protocol/nips/blob/master/11.md) | +| ✅ | [17 - Private Direct Messages](https://github.com/nostr-protocol/nips/blob/master/17.md) | +| 🔧* | [40 - Expiration Timestamp](https://github.com/nostr-protocol/nips/blob/master/40.md) | +| ✅ | [42 - Authentication of clients to relays](https://github.com/nostr-protocol/nips/blob/master/42.md) | +| 🔧 | [50 - Search Capability](https://github.com/nostr-protocol/nips/blob/master/50.md) | +| 🔧 | [62 - Request to Vanish](https://github.com/nostr-protocol/nips/blob/master/62.md) | +| ✅ | [70 - Protected Events](https://github.com/nostr-protocol/nips/blob/master/70.md) | +| ✅ | [77 - Negentropy Syncing](https://github.com/nostr-protocol/nips/blob/master/77.md) | + +**Legend:** + +- ✅ Fully supported +- 🔧 Depends on the database implementation +- ❌ Not supported + +*: The relay does not accept or send expired events. The database has to delete them. + ## WASM This crate supports the `wasm32` targets. diff --git a/crates/nostr-relay-builder/examples/hyper.rs b/sdk/examples/local-relay-hyper.rs similarity index 99% rename from crates/nostr-relay-builder/examples/hyper.rs rename to sdk/examples/local-relay-hyper.rs index 1aba1b50f..faa6461b4 100644 --- a/crates/nostr-relay-builder/examples/hyper.rs +++ b/sdk/examples/local-relay-hyper.rs @@ -15,7 +15,7 @@ use hyper::{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 tokio::net::TcpListener; struct HttpServer { diff --git a/crates/nostr-relay-builder/examples/mock.rs b/sdk/examples/local-relay-mock.rs similarity index 92% rename from crates/nostr-relay-builder/examples/mock.rs rename to sdk/examples/local-relay-mock.rs index 55609b7d4..8b6c97af6 100644 --- a/crates/nostr-relay-builder/examples/mock.rs +++ b/sdk/examples/local-relay-mock.rs @@ -4,7 +4,7 @@ use std::time::Duration; -use nostr_relay_builder::prelude::*; +use nostr_sdk::prelude::*; #[tokio::main] async fn main() -> Result<(), Box> { diff --git a/crates/nostr-relay-builder/examples/policy.rs b/sdk/examples/local-relay-policy.rs similarity index 98% rename from crates/nostr-relay-builder/examples/policy.rs rename to sdk/examples/local-relay-policy.rs index b22333f0b..6d49f7b7b 100644 --- a/crates/nostr-relay-builder/examples/policy.rs +++ b/sdk/examples/local-relay-policy.rs @@ -6,7 +6,7 @@ use std::collections::HashSet; use std::net::SocketAddr; use std::time::Duration; -use nostr_relay_builder::prelude::*; +use nostr_sdk::prelude::*; /// Accept only certain event kinds #[derive(Debug)] diff --git a/crates/nostr-relay-builder/examples/simple_relay.rs b/sdk/examples/local-relay-simple.rs similarity index 92% rename from crates/nostr-relay-builder/examples/simple_relay.rs rename to sdk/examples/local-relay-simple.rs index 77d49f88a..850a4dd32 100644 --- a/crates/nostr-relay-builder/examples/simple_relay.rs +++ b/sdk/examples/local-relay-simple.rs @@ -1,7 +1,7 @@ use std::time::Duration; use nostr_lmdb::NostrLmdb; -use nostr_relay_builder::prelude::*; +use nostr_sdk::prelude::*; #[tokio::main] async fn main() -> Result<(), Box> { diff --git a/sdk/src/client/api/fetch_events.rs b/sdk/src/client/api/fetch_events.rs index 8a06364a7..2a1d86353 100644 --- a/sdk/src/client/api/fetch_events.rs +++ b/sdk/src/client/api/fetch_events.rs @@ -97,8 +97,8 @@ where mod tests { use std::time::Duration; + use nostr::event::FinalizeEvent; use nostr::{EventBuilder, Filter, Keys, Kind}; - use nostr_relay_builder::prelude::*; use crate::authenticator::SignerAuthenticator; use crate::test_utils::{ diff --git a/sdk/src/client/api/send_event.rs b/sdk/src/client/api/send_event.rs index e0607df5f..39e0f0153 100644 --- a/sdk/src/client/api/send_event.rs +++ b/sdk/src/client/api/send_event.rs @@ -443,11 +443,11 @@ mod tests { use nostr::prelude::*; use nostr_gossip::GossipAllowedRelays; use nostr_gossip_memory::store::NostrGossipMemory; - use nostr_relay_builder::MockRelay; use super::*; use crate::client::{GossipConfig, GossipRelayLimits}; use crate::error::ErrorKind; + use crate::local_relay::*; #[tokio::test] async fn test_send_event() { diff --git a/sdk/src/client/api/send_msg.rs b/sdk/src/client/api/send_msg.rs index b9888402e..df6fbac23 100644 --- a/sdk/src/client/api/send_msg.rs +++ b/sdk/src/client/api/send_msg.rs @@ -111,9 +111,9 @@ where #[cfg(test)] mod tests { use nostr::prelude::*; - use nostr_relay_builder::MockRelay; use super::*; + use crate::local_relay::*; #[tokio::test] async fn test_send_msg() { diff --git a/sdk/src/client/api/stream_events.rs b/sdk/src/client/api/stream_events.rs index a6a245a7f..95b9ddcbf 100644 --- a/sdk/src/client/api/stream_events.rs +++ b/sdk/src/client/api/stream_events.rs @@ -102,12 +102,13 @@ mod tests { use std::time::Duration; use futures::StreamExt; + use nostr::event::FinalizeEvent; use nostr::message::MachineReadablePrefix; - use nostr::{Filter, SubscriptionId}; - use nostr_relay_builder::prelude::*; + use nostr::{EventBuilder, Filter, Keys, Kind, SubscriptionId}; use super::*; use crate::authenticator::SignerAuthenticator; + use crate::local_relay::*; use crate::test_utils::{ setup_client, setup_client_with_authenticator, setup_nip42_read_local_relay, }; diff --git a/sdk/src/client/api/subscribe.rs b/sdk/src/client/api/subscribe.rs index 12e4a6696..f2ce92127 100644 --- a/sdk/src/client/api/subscribe.rs +++ b/sdk/src/client/api/subscribe.rs @@ -71,8 +71,8 @@ mod tests { use std::time::Duration; use futures::StreamExt; - use nostr::{Filter, SubscriptionId}; - use nostr_relay_builder::prelude::*; + use nostr::event::{EventBuilder, FinalizeEvent}; + use nostr::{Filter, Keys, Kind, SubscriptionId}; use crate::authenticator::SignerAuthenticator; use crate::client::ClientNotification; diff --git a/sdk/src/client/gossip/updater.rs b/sdk/src/client/gossip/updater.rs index a99303bb5..12d61ee77 100644 --- a/sdk/src/client/gossip/updater.rs +++ b/sdk/src/client/gossip/updater.rs @@ -500,10 +500,10 @@ impl Client { #[cfg(test)] mod tests { use nostr_gossip_memory::prelude::*; - use nostr_relay_builder::prelude::*; use super::*; use crate::client::GossipConfig; + use crate::local_relay::*; fn client_with_gossip() -> Client { let gossip = NostrGossipMemory::unbounded(); diff --git a/sdk/src/client/mod.rs b/sdk/src/client/mod.rs index f120087a5..86141f7a1 100644 --- a/sdk/src/client/mod.rs +++ b/sdk/src/client/mod.rs @@ -1193,10 +1193,10 @@ impl Client { #[cfg(test)] mod tests { use nostr_gossip_memory::prelude::*; - use nostr_relay_builder::MockRelay; use super::*; use crate::error::ErrorKind; + use crate::local_relay::*; use crate::relay::RelayStatus; #[tokio::test] diff --git a/sdk/src/error.rs b/sdk/src/error.rs index b3f7030ef..2aeb81f22 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -13,6 +13,8 @@ opaquerr::define_kind! { pub ErrorKind { /// Nostr protocol error. Protocol => "nostr protocol error", + /// I/O error + IO => "I/O error", /// Transport error. Transport => "transport error", /// Database error. @@ -46,13 +48,20 @@ opaquerr::define_error! { from { nostr::error::Error => ErrorKind::Protocol, + std::io::Error => ErrorKind::IO, nostr_database::error::Error => ErrorKind::Database, nostr_gossip::error::Error => ErrorKind::Gossip, serde_json::Error => ErrorKind::Protocol, faster_hex::Error => ErrorKind::Protocol, negentropy::Error => ErrorKind::Protocol, + #[cfg(any(feature = "local-relay", test))] + async_wsocket::Error => ErrorKind::Other, tokio::sync::oneshot::error::RecvError => ErrorKind::Other, tokio::sync::broadcast::error::RecvError => ErrorKind::Other, + #[cfg(any(feature = "local-relay", test))] + tokio::sync::TryAcquireError => ErrorKind::Other, + #[cfg(any(feature = "local-relay", test))] + tokio::sync::broadcast::error::SendError => ErrorKind::Other, } } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index fe82eba51..204028261 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -11,6 +11,8 @@ pub mod client; pub mod error; mod events_tracker; mod future; +#[cfg(any(feature = "local-relay", test))] +pub mod local_relay; pub mod monitor; pub mod policy; mod pool; diff --git a/crates/nostr-relay-builder/src/builder.rs b/sdk/src/local_relay/builder.rs similarity index 99% rename from crates/nostr-relay-builder/src/builder.rs rename to sdk/src/local_relay/builder.rs index e4eb5b0f0..82feed7fd 100644 --- a/crates/nostr-relay-builder/src/builder.rs +++ b/sdk/src/local_relay/builder.rs @@ -2,8 +2,6 @@ // Copyright (c) 2023-2025 Rust Nostr Developers // Distributed under the MIT software license -//! Relay Builder - use std::borrow::Cow; use std::fmt; use std::net::{IpAddr, SocketAddr}; @@ -12,7 +10,7 @@ use std::time::Duration; use nostr_database::prelude::*; -use crate::local::LocalRelay; +use super::local::LocalRelay; /// Rate limit #[derive(Debug, Clone)] diff --git a/crates/nostr-relay-builder/src/local/inner.rs b/sdk/src/local_relay/local/inner.rs similarity index 99% rename from crates/nostr-relay-builder/src/local/inner.rs rename to sdk/src/local_relay/local/inner.rs index a6ea9e6be..b8f549d04 100644 --- a/crates/nostr-relay-builder/src/local/inner.rs +++ b/sdk/src/local_relay/local/inner.rs @@ -12,22 +12,22 @@ use std::sync::atomic::{AtomicBool, Ordering}; use async_utility::futures_util::stream::{self, SplitSink}; use async_utility::futures_util::{SinkExt, StreamExt}; use async_wsocket::native::{self, Message, WebSocketStream}; -use atomic_destructor::AtomicDestroyer; use negentropy::{Id, Negentropy, NegentropyStorageVector}; +use nostr::prelude::*; use nostr_memory::prelude::*; -use nostr_sdk::client::SyncSummary; -use nostr_sdk::prelude::*; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpListener; use tokio::sync::{Notify, OnceCell, Semaphore, broadcast}; -use super::session::{Nip42Session, RateLimiterResponse, Session, Tokens}; -use super::util; -use crate::builder::{ +use super::super::builder::{ LocalRelayBuilder, LocalRelayBuilderMode, LocalRelayBuilderNip42, LocalRelayTestOptions, QueryPolicy, QueryPolicyResult, RateLimit, WritePolicy, WritePolicyResult, }; +use super::session::{Nip42Session, RateLimiterResponse, Session, Tokens}; +use super::util; +use crate::client::{Client, ClientNotification, Output, SyncSummary}; use crate::error::{Error, ErrorKind}; +use crate::relay::SyncOptions; type WsTx = SplitSink, Message>; const P_TAG: SingleLetterTag = SingleLetterTag::lowercase(Alphabet::P); @@ -57,12 +57,6 @@ pub(super) struct InnerLocalRelay { running: Arc, } -impl AtomicDestroyer for InnerLocalRelay { - fn on_destroy(&self) { - self.shutdown(); - } -} - impl InnerLocalRelay { pub fn new(builder: LocalRelayBuilder) -> Self { // Get IP @@ -116,11 +110,16 @@ impl InnerLocalRelay { .await } + #[inline] + pub(super) fn is_running(&self) -> bool { + self.running.load(Ordering::SeqCst) + } + /// Start socket to listen for new websocket connections /// /// Returns `true` if the relay has started, `false` if it's already running. pub async fn run(&self) -> Result { - if self.running.load(Ordering::SeqCst) { + if self.is_running() { return Ok(false); } diff --git a/crates/nostr-relay-builder/src/local/mod.rs b/sdk/src/local_relay/local/mod.rs similarity index 51% rename from crates/nostr-relay-builder/src/local/mod.rs rename to sdk/src/local_relay/local/mod.rs index 1cdba45ae..2ad46f472 100644 --- a/crates/nostr-relay-builder/src/local/mod.rs +++ b/sdk/src/local_relay/local/mod.rs @@ -2,13 +2,12 @@ // Copyright (c) 2023-2025 Rust Nostr Developers // Distributed under the MIT software license -//! A local nostr relay - use std::net::SocketAddr; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; -use atomic_destructor::AtomicDestructor; -use nostr_sdk::client::SyncSummary; -use nostr_sdk::prelude::*; +use nostr::{Event, Filter, RelayUrl, RelayUrlArg}; +use nostr_database::SaveEventStatus; use tokio::io::{AsyncRead, AsyncWrite}; mod inner; @@ -16,15 +15,39 @@ mod session; mod util; use self::inner::InnerLocalRelay; -use crate::builder::LocalRelayBuilder; +use super::builder::LocalRelayBuilder; +use crate::client::{Output, SyncSummary}; use crate::error::Error; +use crate::relay::SyncOptions; /// A local nostr relay /// /// This is automatically shutdown when all instances/clones are dropped! -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct LocalRelay { - inner: AtomicDestructor, + inner: InnerLocalRelay, + // Keep track of the atomic reference count to know when shutdown the relay. + atomic_counter: Arc, +} + +impl Clone for LocalRelay { + fn clone(&self) -> Self { + self.atomic_counter.fetch_add(1, Ordering::SeqCst); + + Self { + inner: self.inner.clone(), + atomic_counter: self.atomic_counter.clone(), + } + } +} + +impl Drop for LocalRelay { + fn drop(&mut self) { + // Shutdown exactly once when the last handle is dropped. + if self.atomic_counter.fetch_sub(1, Ordering::SeqCst) == 1 { + self.shutdown(); + } + } } impl Default for LocalRelay { @@ -52,7 +75,8 @@ impl LocalRelay { #[inline] pub(super) fn from_builder(builder: LocalRelayBuilder) -> Self { Self { - inner: AtomicDestructor::new(InnerLocalRelay::new(builder)), + inner: InnerLocalRelay::new(builder), + atomic_counter: Arc::new(AtomicUsize::new(1)), } } @@ -119,3 +143,70 @@ impl LocalRelay { self.inner.handle_upgraded_connection(stream, addr).await } } + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use tokio::time; + + use super::*; + + #[tokio::test] + async fn test_shutdown() { + let relay = LocalRelay::new(); + + assert!(!relay.inner.is_running()); + + relay.run().await.unwrap(); + + time::sleep(Duration::from_secs(1)).await; + + assert!(relay.inner.is_running()); + + relay.shutdown(); + + time::sleep(Duration::from_millis(100)).await; + + assert!(!relay.inner.is_running()); + } + + #[tokio::test] + async fn test_shutdown_on_drop() { + let inner: InnerLocalRelay = { + let relay: LocalRelay = LocalRelay::new(); + + assert!(!relay.inner.is_running()); + + relay.run().await.unwrap(); + + time::sleep(Duration::from_secs(1)).await; + + assert!(relay.inner.is_running()); + + // Clone the inner relay + let inner: InnerLocalRelay = relay.inner.clone(); + + { + let r2: LocalRelay = relay.clone(); + tokio::spawn(async move { + assert_eq!(r2.atomic_counter.load(Ordering::SeqCst), 2); + + time::sleep(Duration::from_secs(1)).await; + + // r2 dropped here + }); + } + + time::sleep(Duration::from_secs(2)).await; + + assert_eq!(relay.atomic_counter.load(Ordering::SeqCst), 1); + + inner + }; // relay dropped here + + time::sleep(Duration::from_secs(1)).await; + + assert!(!inner.is_running()); + } +} diff --git a/crates/nostr-relay-builder/src/local/session.rs b/sdk/src/local_relay/local/session.rs similarity index 100% rename from crates/nostr-relay-builder/src/local/session.rs rename to sdk/src/local_relay/local/session.rs diff --git a/crates/nostr-relay-builder/src/local/util.rs b/sdk/src/local_relay/local/util.rs similarity index 100% rename from crates/nostr-relay-builder/src/local/util.rs rename to sdk/src/local_relay/local/util.rs diff --git a/crates/nostr-relay-builder/src/mock.rs b/sdk/src/local_relay/mock.rs similarity index 89% rename from crates/nostr-relay-builder/src/mock.rs rename to sdk/src/local_relay/mock.rs index 3aa90309d..ef46a3975 100644 --- a/crates/nostr-relay-builder/src/mock.rs +++ b/sdk/src/local_relay/mock.rs @@ -2,13 +2,11 @@ // Copyright (c) 2023-2025 Rust Nostr Developers // Distributed under the MIT software license -//! A mock relay for (unit) tests. - use std::ops::Deref; -use crate::builder::{LocalRelayBuilder, LocalRelayTestOptions}; +use super::builder::{LocalRelayBuilder, LocalRelayTestOptions}; +use super::local::LocalRelay; use crate::error::Error; -use crate::local::LocalRelay; /// A mock relay for (unit) tests. /// diff --git a/sdk/src/local_relay/mod.rs b/sdk/src/local_relay/mod.rs new file mode 100644 index 000000000..798cb9900 --- /dev/null +++ b/sdk/src/local_relay/mod.rs @@ -0,0 +1,87 @@ +// Copyright (c) 2022-2023 Yuki Kishimoto +// Copyright (c) 2023-2025 Rust Nostr Developers +// Distributed under the MIT software license + +//! Nostr Relay Builder and Mock Relay for tests + +mod builder; +mod local; +mod mock; + +pub use self::builder::*; +pub use self::local::*; +pub use self::mock::*; + +#[cfg(test)] +mod tests { + use nostr::event::{EventBuilder, FinalizeEvent, Tag}; + use nostr::filter::Filter; + use nostr::key::Keys; + use nostr::util::BoxedFuture; + use nostr_memory::MemoryDatabase; + + use super::*; + use crate::client::Client; + + const UPDATE_TAG: &str = "updated"; + + #[derive(Debug)] + struct UpdateFilterPlugin; + + impl QueryPolicy for UpdateFilterPlugin { + fn admit_query<'a>( + &'a self, + query: &'a mut Filter, + _addr: &'a std::net::SocketAddr, + ) -> BoxedFuture<'a, QueryPolicyResult> { + Box::pin(async move { + *query = query.clone().hashtag(UPDATE_TAG); + QueryPolicyResult::Accept + }) + } + } + + #[tokio::test] + async fn update_filter() { + let relay = LocalRelay::builder() + .database(MemoryDatabase::unbounded()) + .query_policy(UpdateFilterPlugin) + .build(); + relay.run().await.unwrap(); + + let keys = Keys::generate(); + let client = Client::default(); + + client + .add_relay(relay.url().await) + .and_connect() + .await + .unwrap(); + + // Event with our target tag + let event = EventBuilder::text_note(":)") + .tag(Tag::hashtag(UPDATE_TAG)) + .finalize(&keys) + .unwrap(); + client.send_event(&event).await.unwrap(); + + // This event has a random tag and should be filtered out in the REQ. + // It would only appear if the filter had not been updated correctly. + let event = EventBuilder::text_note(":)") + .tag(Tag::hashtag("TEST")) + .finalize(&keys) + .unwrap(); + client.send_event(&event).await.unwrap(); + + // Empty filter to get all events. It should be updated to have `UPDATE_TAG` + let events = client.fetch_events(Filter::new()).await.unwrap(); + + assert!(!events.is_empty(), "Should not be empty"); + assert!( + events + .iter() + .all(|e| { e.tags.hashtags().all(|hashtag| hashtag == UPDATE_TAG) }), + "All tags should have the updated filter tag" + ); + } +} diff --git a/sdk/src/prelude.rs b/sdk/src/prelude.rs index 42ba84796..9683a3916 100644 --- a/sdk/src/prelude.rs +++ b/sdk/src/prelude.rs @@ -16,6 +16,8 @@ pub use nostr_gossip::prelude::*; pub use crate::authenticator::{self, *}; pub use crate::client::{self, *}; pub use crate::error::{self, Error, ErrorKind}; +#[cfg(feature = "local-relay")] +pub use crate::local_relay::{self, *}; pub use crate::monitor::{self, *}; pub use crate::policy::*; #[cfg(not(target_arch = "wasm32"))] diff --git a/sdk/src/relay/api/fetch_events.rs b/sdk/src/relay/api/fetch_events.rs index 0f9023fde..10ca5e021 100644 --- a/sdk/src/relay/api/fetch_events.rs +++ b/sdk/src/relay/api/fetch_events.rs @@ -97,10 +97,10 @@ mod tests { use nostr::event::FinalizeEvent; use nostr::message::MachineReadablePrefix; use nostr::{EventBuilder, Keys, Kind, Metadata}; - use nostr_relay_builder::prelude::*; use super::*; use crate::authenticator::SignerAuthenticator; + use crate::local_relay::*; use crate::relay::{RelayOptions, RelayStatus}; use crate::test_utils::{ setup_nip42_read_local_relay, setup_relay, setup_relay_with_authenticator, diff --git a/sdk/src/relay/api/send_event.rs b/sdk/src/relay/api/send_event.rs index c27091d4d..05f806363 100644 --- a/sdk/src/relay/api/send_event.rs +++ b/sdk/src/relay/api/send_event.rs @@ -271,10 +271,10 @@ mod tests { use std::time::Duration; use nostr::prelude::*; - use nostr_relay_builder::prelude::*; use super::*; use crate::authenticator::SignerAuthenticator; + use crate::local_relay::*; #[tokio::test] async fn test_ok_msg() { diff --git a/sdk/src/relay/api/stream_events.rs b/sdk/src/relay/api/stream_events.rs index f1d8c94e0..2820483b5 100644 --- a/sdk/src/relay/api/stream_events.rs +++ b/sdk/src/relay/api/stream_events.rs @@ -189,9 +189,9 @@ mod tests { use nostr::key::Keys; use nostr::message::MachineReadablePrefix; use nostr::{Filter, Kind, SubscriptionId}; - use nostr_relay_builder::MockRelay; use crate::authenticator::SignerAuthenticator; + use crate::local_relay::*; use crate::relay::{Relay, RelayOptions, ReqExitPolicy}; use crate::test_utils::{ setup_nip42_read_local_relay, setup_relay, setup_relay_with_authenticator, diff --git a/sdk/src/relay/api/subscribe.rs b/sdk/src/relay/api/subscribe.rs index 9b3baca6f..7d441c607 100644 --- a/sdk/src/relay/api/subscribe.rs +++ b/sdk/src/relay/api/subscribe.rs @@ -157,11 +157,11 @@ mod tests { use futures::StreamExt; use nostr::event::FinalizeEvent; use nostr::{Event, EventBuilder, EventId, Filter, Keys, Kind, SubscriptionId}; - use nostr_relay_builder::prelude::*; use super::*; use crate::authenticator::SignerAuthenticator; use crate::error::ErrorKind; + use crate::local_relay::*; use crate::relay::{RelayNotification, RelayOptions, RelayStatus, ReqExitPolicy}; use crate::test_utils::{ setup_nip42_read_local_relay, setup_relay, setup_relay_with_authenticator, diff --git a/sdk/src/relay/api/sync.rs b/sdk/src/relay/api/sync.rs index e59ecfe90..049f58e81 100644 --- a/sdk/src/relay/api/sync.rs +++ b/sdk/src/relay/api/sync.rs @@ -635,11 +635,11 @@ mod tests { use std::time::Duration; use nostr_memory::prelude::*; - use nostr_relay_builder::prelude::*; use tokio::sync::broadcast; use super::*; use crate::error::ErrorKind; + use crate::local_relay::*; use crate::relay::{SyncDirection, SyncOptions}; #[tokio::test] diff --git a/sdk/src/relay/api/try_connect.rs b/sdk/src/relay/api/try_connect.rs index b87c07438..db3921986 100644 --- a/sdk/src/relay/api/try_connect.rs +++ b/sdk/src/relay/api/try_connect.rs @@ -83,10 +83,10 @@ impl<'relay> IntoFuture for TryConnect<'relay> { mod tests { use async_utility::time; use nostr::RelayUrl; - use nostr_relay_builder::prelude::*; use super::*; use crate::error::ErrorKind; + use crate::local_relay::*; #[tokio::test] async fn test_try_connect() { diff --git a/sdk/src/relay/mod.rs b/sdk/src/relay/mod.rs index 245fe2a07..d5ca4e8cb 100644 --- a/sdk/src/relay/mod.rs +++ b/sdk/src/relay/mod.rs @@ -460,10 +460,10 @@ mod tests { use std::sync::Arc; use async_utility::time; - use nostr_relay_builder::prelude::*; use super::*; use crate::error::{Error, ErrorKind}; + use crate::local_relay::*; use crate::policy::{AdmitPolicy, AdmitStatus}; #[derive(Debug)] diff --git a/sdk/src/test_utils.rs b/sdk/src/test_utils.rs index fd59e35f1..cae0cb897 100644 --- a/sdk/src/test_utils.rs +++ b/sdk/src/test_utils.rs @@ -1,10 +1,10 @@ use std::time::Duration; use nostr::RelayUrl; -use nostr_relay_builder::prelude::*; use crate::authenticator::Authenticator; use crate::client::Client; +use crate::local_relay::*; use crate::relay::Relay; pub(crate) async fn setup_nip42_read_local_relay() -> LocalRelay { diff --git a/signer/nostr-connect/Cargo.toml b/signer/nostr-connect/Cargo.toml index a4b3195c0..fcb5c4065 100644 --- a/signer/nostr-connect/Cargo.toml +++ b/signer/nostr-connect/Cargo.toml @@ -37,7 +37,7 @@ tracing.workspace = true dialoguer = "0.12" tracing-subscriber = { workspace = true, features = ["env-filter"] } webbrowser = "1.1" -nostr-relay-builder = { path = "../../crates/nostr-relay-builder", default-features = false } +nostr-sdk = { path = "../../sdk", features = ["local-relay"] } [[example]] name = "handle-auth-url" diff --git a/signer/nostr-connect/tests/nostr-connect.rs b/signer/nostr-connect/tests/nostr-connect.rs index f83d03b70..377115f7d 100644 --- a/signer/nostr-connect/tests/nostr-connect.rs +++ b/signer/nostr-connect/tests/nostr-connect.rs @@ -12,7 +12,7 @@ use nostr_connect::client::NostrConnect; use nostr_connect::signer::{ NostrConnectKeys, NostrConnectRemoteSigner, NostrConnectSignerActions, }; -use nostr_relay_builder::LocalRelayBuilder; +use nostr_sdk::local_relay::LocalRelayBuilder; struct MySignerActions;