From 9d0a98f8bf2cf08f2612850d89fd4514789c34a0 Mon Sep 17 00:00:00 2001 From: benthecarman <15256660+benthecarman@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:47:46 -0500 Subject: [PATCH 1/4] docs: add line for enabling systemd service Signed-off-by: Greg Heartsfield --- docs/run-as-linux-system-process.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/run-as-linux-system-process.md b/docs/run-as-linux-system-process.md index 6d9cf27..66c0fb5 100644 --- a/docs/run-as-linux-system-process.md +++ b/docs/run-as-linux-system-process.md @@ -30,7 +30,8 @@ To get the service running, we need to reload the systemd daemon and enable the 1. `sudo systemctl daemon-reload` 2. `sudo systemctl start nostr-rs-relay.service` -3. `sudo systemctl status nostr-rs-relay.service` +3. `sudo systemctl enable nostr-rs-relay.service` +4. `sudo systemctl status nostr-rs-relay.service` ### Tips From 24b1705a0851e56cef866b51dc858edd5563f1ea Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 17 Oct 2023 16:07:56 +0100 Subject: [PATCH 2/4] fix: value_hex tag query Signed-off-by: Greg Heartsfield --- src/repo/postgres.rs | 157 ++++++++++++++++++++++++++++++++----------- 1 file changed, 117 insertions(+), 40 deletions(-) diff --git a/src/repo/postgres.rs b/src/repo/postgres.rs index 05b3832..75ba1a8 100644 --- a/src/repo/postgres.rs +++ b/src/repo/postgres.rs @@ -60,7 +60,8 @@ async fn cleanup_expired(conn: PostgresPool, frequency: Duration) -> Result<()> } } } - }; + } + ; } }); Ok(()) @@ -150,19 +151,19 @@ impl NostrRepo for PostgresRepo { VALUES($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO NOTHING"#, ) - .bind(&id_blob) - .bind(&pubkey_blob) - .bind(Utc.timestamp_opt(e.created_at as i64, 0).unwrap()) - .bind( - e.expiration() - .and_then(|x| Utc.timestamp_opt(x as i64, 0).latest()), - ) - .bind(e.kind as i64) - .bind(event_str.into_bytes()) - .bind(delegator_blob) - .execute(&mut tx) - .await? - .rows_affected(); + .bind(&id_blob) + .bind(&pubkey_blob) + .bind(Utc.timestamp_opt(e.created_at as i64, 0).unwrap()) + .bind( + e.expiration() + .and_then(|x| Utc.timestamp_opt(x as i64, 0).latest()), + ) + .bind(e.kind as i64) + .bind(event_str.into_bytes()) + .bind(delegator_blob) + .execute(&mut tx) + .await? + .rows_affected(); if ins_count == 0 { // if the event was a duplicate, no need to insert event or @@ -282,10 +283,10 @@ ON CONFLICT (id) DO NOTHING"#, LEFT JOIN tag t ON e.id = t.event_id \ WHERE e.pub_key = $1 AND t.\"name\" = 'e' AND e.kind = 5 AND t.value = $2 LIMIT 1", ) - .bind(&pubkey_blob) - .bind(&id_blob) - .fetch_optional(&mut tx) - .await?; + .bind(&pubkey_blob) + .bind(&id_blob) + .fetch_optional(&mut tx) + .await?; // check if a the query returned a result, meaning we should // hid the current event @@ -576,10 +577,10 @@ ON CONFLICT (id) DO NOTHING"#, sqlx::query( "UPDATE account SET is_admitted = TRUE, balance = balance - $1 WHERE pubkey = $2", ) - .bind(admission_cost as i64) - .bind(pub_key) - .execute(&self.conn_write) - .await?; + .bind(admission_cost as i64) + .bind(pub_key) + .execute(&self.conn_write) + .await?; Ok(()) } @@ -637,14 +638,14 @@ ON CONFLICT (id) DO NOTHING"#, sqlx::query( "INSERT INTO invoice (pubkey, payment_hash, amount, status, description, created_at, invoice) VALUES ($1, $2, $3, $4, $5, now(), $6)", ) - .bind(pub_key) - .bind(invoice_info.payment_hash) - .bind(invoice_info.amount as i64) - .bind(invoice_info.status) - .bind(invoice_info.memo) - .bind(invoice_info.bolt11) - .execute(&mut tx) - .await.unwrap(); + .bind(pub_key) + .bind(invoice_info.payment_hash) + .bind(invoice_info.amount as i64) + .bind(invoice_info.status) + .bind(invoice_info.memo) + .bind(invoice_info.bolt11) + .execute(&mut tx) + .await.unwrap(); debug!("Invoice added"); @@ -880,17 +881,32 @@ fn query_from_filter(f: &ReqFilter) -> Option> { for (key, val) in map.iter() { query.push("e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and (t.\"name\" = ") .push_bind(key.to_string()) - .push(" AND (value in ("); + .push(" AND ("); - // plain value match first - let mut tag_query = query.separated(", "); - for v in val.iter() { - if (v.len() % 2 != 0) && !is_lower_hex(v) { + let has_plain_values = val.iter().any(|v| !is_lower_hex(v)); + let has_hex_values = val.iter().any(|v| v.len() % 2 == 0 && is_lower_hex(v)); + if has_plain_values { + query.push("value in ("); + // plain value match first + let mut tag_query = query.separated(", "); + for v in val.iter() + .filter(|v| !is_lower_hex(v)) { tag_query.push_bind(v.as_bytes()); - } else { + } + } + if has_plain_values && has_hex_values { + query.push(") OR "); + } + if has_hex_values { + query.push("value_hex in ("); + // plain value match first + let mut tag_query = query.separated(", "); + for v in val.iter() + .filter(|v| v.len() % 2 == 0 && is_lower_hex(v)) { tag_query.push_bind(hex::decode(v).ok()); } } + query.push("))))"); } } @@ -925,10 +941,7 @@ fn query_from_filter(f: &ReqFilter) -> Option> { query.push("e.hidden != 1::bit(1)"); } // never display expired events - query - .push(" AND (e.expires_at IS NULL OR e.expires_at > ") - .push_bind(Utc.timestamp_opt(utils::unix_time() as i64, 0).unwrap()) - .push(")"); + query.push(" AND (e.expires_at IS NULL OR e.expires_at > now())"); // Apply per-filter limit to this query. // The use of a LIMIT implies a DESC order, to capture only the most recent events. @@ -963,3 +976,67 @@ impl FromRow<'_, PgRow> for VerificationRecord { }) } } + + +#[cfg(test)] +mod tests { + use std::collections::{HashMap, HashSet}; + use super::*; + + #[test] + fn test_query_gen_tag_value_hex() { + let filter = ReqFilter { + ids: None, + kinds: Some(vec![1000]), + since: None, + until: None, + authors: Some(vec!["84de35e2584d2b144aae823c9ed0b0f3deda09648530b93d1a2a146d1dea9864".to_owned()]), + limit: None, + tags: Some(HashMap::from([ + ('p', HashSet::from(["63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed".to_owned()])) + ])), + force_no_match: false, + }; + + let q = query_from_filter(&filter).unwrap(); + assert_eq!(q.sql(), "SELECT e.\"content\", e.created_at FROM \"event\" e WHERE (e.pub_key in ($1) OR e.delegated_by in ($2)) AND e.kind in ($3) AND e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and (t.\"name\" = $4 AND (value_hex in ($5)))) AND e.hidden != 1::bit(1) AND (e.expires_at IS NULL OR e.expires_at > now()) ORDER BY e.created_at ASC LIMIT 1000") + } + + #[test] + fn test_query_gen_tag_value() { + let filter = ReqFilter { + ids: None, + kinds: Some(vec![1000]), + since: None, + until: None, + authors: Some(vec!["84de35e2584d2b144aae823c9ed0b0f3deda09648530b93d1a2a146d1dea9864".to_owned()]), + limit: None, + tags: Some(HashMap::from([ + ('d', HashSet::from(["test".to_owned()])) + ])), + force_no_match: false, + }; + + let q = query_from_filter(&filter).unwrap(); + assert_eq!(q.sql(), "SELECT e.\"content\", e.created_at FROM \"event\" e WHERE (e.pub_key in ($1) OR e.delegated_by in ($2)) AND e.kind in ($3) AND e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and (t.\"name\" = $4 AND (value in ($5)))) AND e.hidden != 1::bit(1) AND (e.expires_at IS NULL OR e.expires_at > now()) ORDER BY e.created_at ASC LIMIT 1000") + } + + #[test] + fn test_query_gen_tag_value_and_value_hex() { + let filter = ReqFilter { + ids: None, + kinds: Some(vec![1000]), + since: None, + until: None, + authors: Some(vec!["84de35e2584d2b144aae823c9ed0b0f3deda09648530b93d1a2a146d1dea9864".to_owned()]), + limit: None, + tags: Some(HashMap::from([ + ('d', HashSet::from(["test".to_owned(), "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed".to_owned()])) + ])), + force_no_match: false, + }; + + let q = query_from_filter(&filter).unwrap(); + assert_eq!(q.sql(), "SELECT e.\"content\", e.created_at FROM \"event\" e WHERE (e.pub_key in ($1) OR e.delegated_by in ($2)) AND e.kind in ($3) AND e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and (t.\"name\" = $4 AND (value in ($5) OR value_hex in ($6)))) AND e.hidden != 1::bit(1) AND (e.expires_at IS NULL OR e.expires_at > now()) ORDER BY e.created_at ASC LIMIT 1000") + } +} \ No newline at end of file From a7b169c0d38ea3dff3b48bf51cfc16b965c77b46 Mon Sep 17 00:00:00 2001 From: Yuki Kishimoto Date: Tue, 31 Oct 2023 09:49:10 +0100 Subject: [PATCH 3/4] fix: send `OK` message for ephemeral events https://github.com/nostr-protocol/nips/blob/4b9f13d983245e4dd166f102308afc28b8bb1603/01.md?plain=1#L153 Signed-off-by: Greg Heartsfield --- src/db.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/db.rs b/src/db.rs index b7db56a..d0843fd 100644 --- a/src/db.rs +++ b/src/db.rs @@ -401,6 +401,9 @@ pub async fn db_writer( start.elapsed() ); event_write = true; + + // send OK message + notice_tx.try_send(Notice::saved(event.id)).ok(); } else { match repo.write_event(&event).await { Ok(updated) => { From 7650f5f4a39aac0ff2830cf453b2236b3a0dfeef Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Tue, 31 Oct 2023 21:50:12 +0000 Subject: [PATCH 4/4] fix: relay fee in msats Signed-off-by: Greg Heartsfield --- src/info.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/info.rs b/src/info.rs index 2801415..f35f21c 100644 --- a/src/info.rs +++ b/src/info.rs @@ -4,7 +4,7 @@ use crate::config::Settings; use serde::{Deserialize, Serialize}; pub const CARGO_PKG_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); -pub const UNIT: &str = "sats"; +pub const UNIT: &str = "msats"; /// Limitations of the relay as specified in NIP-111 /// (This nip isn't finalized so may change) @@ -45,7 +45,7 @@ pub struct RelayInfo { #[serde(skip_serializing_if = "Option::is_none")] pub contact: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub icon: Option, + pub icon: Option, #[serde(skip_serializing_if = "Option::is_none")] pub supported_nips: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -80,7 +80,7 @@ impl From for RelayInfo { let (payment_url, fees) = if p.enabled { let admission_fee = if p.admission_cost > 0 { Some(vec![Fee { - amount: p.admission_cost, + amount: p.admission_cost * 1000, unit: UNIT.to_string(), }]) } else { @@ -89,7 +89,7 @@ impl From for RelayInfo { let post_fee = if p.cost_per_event > 0 { Some(vec![Fee { - amount: p.cost_per_event, + amount: p.cost_per_event * 1000, unit: UNIT.to_string(), }]) } else {