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<Box<dyn Future<...>>>` 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.
This commit is contained in:
DanConwayDev
2026-08-01 14:51:28 +00:00
parent f90107c679
commit b02ed59c67
44 changed files with 254 additions and 188 deletions
+6
View File
@@ -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.
Generated
+41 -49
View File
@@ -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]]
+18 -7
View File
@@ -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"
+2 -1
View File
@@ -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"] }
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+5 -5
View File
@@ -879,7 +879,7 @@ pub async fn process_newly_available_git_data(
source_repo_path: &Path,
new_oids: &HashSet<String>,
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>,
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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;
+7 -5
View File
@@ -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<nostr_relay_builder::LocalRelay> {
pub(crate) fn local_relay(&self) -> Option<nostr_sdk::local_relay::LocalRelay> {
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<Box<dyn Future<Output = WritePolicyResult> + Send + 'a>> {
Box::pin(async move {
// Check event blacklist FIRST - it overrides everything
if let Some(reason) = self.check_event_blacklist(event) {
+2 -2
View File
@@ -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<EventId>,
source: DeletionSource,
) -> DeletionOutcome {
+1 -1
View File
@@ -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,
};
+2 -2
View File
@@ -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;
+4 -4
View File
@@ -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, "")
+1 -1
View File
@@ -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;
+7 -7
View File
@@ -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<nostr_relay_builder::prelude::EventId>,
removed_ids: &mut HashSet<nostr_sdk::prelude::EventId>,
) -> 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<nostr_relay_builder::prelude::EventId>,
removed_ids: &mut HashSet<nostr_sdk::prelude::EventId>,
) -> DeletionOutcome {
let mut outcome = DeletionOutcome::default();
// Parse coordinate: `<kind>:<pubkey>:<d-identifier>`
@@ -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<nostr_relay_builder::prelude::EventId>,
removed_ids: &mut HashSet<nostr_sdk::prelude::EventId>,
) -> DeletionOutcome {
let mut outcome = DeletionOutcome::default();
// Get repo path before removing
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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,
};
+2 -2
View File
@@ -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,
};
+7 -9
View File
@@ -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)
+2 -2
View File
@@ -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::*;
+1 -1
View File
@@ -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,
};
+1 -1
View File
@@ -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,
};
+1 -1
View File
@@ -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() {
+1 -1
View File
@@ -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,
};
+1 -1
View File
@@ -5,4 +5,4 @@ pub mod persistence;
pub mod policy;
/// Shared database type used across nostr modules.
pub type SharedDatabase = std::sync::Arc<dyn nostr_relay_builder::prelude::NostrDatabase>;
pub type SharedDatabase = std::sync::Arc<dyn nostr_sdk::prelude::NostrDatabase>;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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;
+1 -3
View File
@@ -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;
+1 -1
View File
@@ -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.
///
+2 -2
View File
@@ -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;
+83 -35
View File
@@ -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,
+2 -2
View File
@@ -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};
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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};
+3 -3
View File
@@ -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<RejectedEventsIndex>,
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");
+22 -17
View File
@@ -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();
+2 -2
View File
@@ -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
+7 -4
View File
@@ -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");
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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());
+3 -1
View File
@@ -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