mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2024-11-25 01:59:08 -05:00
feat: reject future-dated events
If configured, reject events than are more than N seconds in the future. Fixes https://todo.sr.ht/~gheartsfield/nostr-rs-relay/5
This commit is contained in:
parent
d730bf0c59
commit
f415295184
|
@ -1,4 +1,11 @@
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
// initialize a singleton default configuration
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SETTINGS: RwLock<Settings> = RwLock::new(Settings::default());
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -7,6 +14,13 @@ pub struct Network {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Options {
|
||||||
|
pub reject_future_seconds: Option<usize>, // if defined, reject any events with a timestamp more than X seconds in the future
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct Retention {
|
pub struct Retention {
|
||||||
|
@ -34,6 +48,7 @@ pub struct Settings {
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
pub limits: Limits,
|
pub limits: Limits,
|
||||||
pub retention: Retention,
|
pub retention: Retention,
|
||||||
|
pub options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
@ -76,6 +91,9 @@ impl Default for Settings {
|
||||||
persist_days: None, // oldest message
|
persist_days: None, // oldest message
|
||||||
whitelist_addresses: vec![], // whitelisted addresses (never delete)
|
whitelist_addresses: vec![], // whitelisted addresses (never delete)
|
||||||
},
|
},
|
||||||
|
options: Options {
|
||||||
|
reject_future_seconds: Some(30 * 60), // Reject events 30min in the future or greater
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/event.rs
27
src/event.rs
|
@ -1,4 +1,5 @@
|
||||||
//! Event parsing and validation
|
//! Event parsing and validation
|
||||||
|
use crate::config;
|
||||||
use crate::error::Error::*;
|
use crate::error::Error::*;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use bitcoin_hashes::{sha256, Hash};
|
use bitcoin_hashes::{sha256, Hash};
|
||||||
|
@ -8,6 +9,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::value::Value;
|
use serde_json::value::Value;
|
||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// Event command in network format
|
/// Event command in network format
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||||
|
@ -56,6 +58,14 @@ impl From<EventCmd> for Result<Event> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Seconds since 1970
|
||||||
|
fn unix_time() -> u64 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.map(|x| x.as_secs())
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
/// Create a short event identifier, suitable for logging.
|
/// Create a short event identifier, suitable for logging.
|
||||||
pub fn get_event_id_prefix(&self) -> String {
|
pub fn get_event_id_prefix(&self) -> String {
|
||||||
|
@ -64,6 +74,23 @@ impl Event {
|
||||||
|
|
||||||
/// Check if this event has a valid signature.
|
/// Check if this event has a valid signature.
|
||||||
fn is_valid(&self) -> bool {
|
fn is_valid(&self) -> bool {
|
||||||
|
// TODO: return a Result with a reason for invalid events
|
||||||
|
// don't bother to validate an event with a timestamp in the distant future.
|
||||||
|
let config = config::SETTINGS.read().unwrap();
|
||||||
|
let max_future_sec = config.options.reject_future_seconds;
|
||||||
|
if max_future_sec.is_some() {
|
||||||
|
let allowable_future = max_future_sec.unwrap();
|
||||||
|
let curr_time = unix_time();
|
||||||
|
// calculate difference, plus how far future we allow
|
||||||
|
if curr_time + (allowable_future as u64) < self.created_at {
|
||||||
|
let delta = self.created_at - curr_time;
|
||||||
|
info!(
|
||||||
|
"Event is too far in the future ({} seconds), rejecting",
|
||||||
|
delta
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// validation is performed by:
|
// validation is performed by:
|
||||||
// * parsing JSON string into event fields
|
// * parsing JSON string into event fields
|
||||||
// * create an array:
|
// * create an array:
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -1,7 +1,6 @@
|
||||||
//! Server process
|
//! Server process
|
||||||
use futures::SinkExt;
|
use futures::SinkExt;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use nostr_rs_relay::close::Close;
|
use nostr_rs_relay::close::Close;
|
||||||
use nostr_rs_relay::config;
|
use nostr_rs_relay::config;
|
||||||
|
@ -13,7 +12,6 @@ use nostr_rs_relay::protostream;
|
||||||
use nostr_rs_relay::protostream::NostrMessage::*;
|
use nostr_rs_relay::protostream::NostrMessage::*;
|
||||||
use nostr_rs_relay::protostream::NostrResponse::*;
|
use nostr_rs_relay::protostream::NostrResponse::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::RwLock;
|
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
@ -21,23 +19,19 @@ use tokio::sync::broadcast::{Receiver, Sender};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use tungstenite::protocol::WebSocketConfig;
|
use tungstenite::protocol::WebSocketConfig;
|
||||||
// initialize a singleton default configuration
|
|
||||||
lazy_static! {
|
|
||||||
static ref SETTINGS: RwLock<config::Settings> = RwLock::new(config::Settings::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start running a Nostr relay server.
|
/// Start running a Nostr relay server.
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
// setup logger
|
// setup logger
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
{
|
{
|
||||||
let mut settings = SETTINGS.write().unwrap();
|
let mut settings = config::SETTINGS.write().unwrap();
|
||||||
// replace default settings with those read from config.toml
|
// replace default settings with those read from config.toml
|
||||||
let c = config::Settings::new();
|
let c = config::Settings::new();
|
||||||
debug!("using settings: {:?}", c);
|
debug!("using settings: {:?}", c);
|
||||||
*settings = c;
|
*settings = c;
|
||||||
}
|
}
|
||||||
let config = SETTINGS.read().unwrap();
|
let config = config::SETTINGS.read().unwrap();
|
||||||
let addr = format!("{}:{}", config.network.address.trim(), config.network.port);
|
let addr = format!("{}:{}", config.network.address.trim(), config.network.port);
|
||||||
// configure tokio runtime
|
// configure tokio runtime
|
||||||
let rt = Builder::new_multi_thread()
|
let rt = Builder::new_multi_thread()
|
||||||
|
@ -47,7 +41,7 @@ fn main() -> Result<(), Error> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// start tokio
|
// start tokio
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
let settings = SETTINGS.read().unwrap();
|
let settings = config::SETTINGS.read().unwrap();
|
||||||
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");
|
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");
|
||||||
info!("listening on: {}", addr);
|
info!("listening on: {}", addr);
|
||||||
// all client-submitted valid events are broadcast to every
|
// all client-submitted valid events are broadcast to every
|
||||||
|
@ -109,7 +103,7 @@ async fn nostr_server(
|
||||||
let mut bcast_rx = broadcast.subscribe();
|
let mut bcast_rx = broadcast.subscribe();
|
||||||
let mut config = WebSocketConfig::default();
|
let mut config = WebSocketConfig::default();
|
||||||
{
|
{
|
||||||
let settings = SETTINGS.read().unwrap();
|
let settings = config::SETTINGS.read().unwrap();
|
||||||
config.max_message_size = settings.limits.max_ws_message_bytes;
|
config.max_message_size = settings.limits.max_ws_message_bytes;
|
||||||
config.max_frame_size = settings.limits.max_ws_frame_bytes;
|
config.max_frame_size = settings.limits.max_ws_frame_bytes;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user