mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2024-11-09 21:29:06 -05:00
refactor: create utils/hexrange utility modules
This commit is contained in:
parent
26a0ce2b32
commit
753df47443
165
src/db.rs
165
src/db.rs
|
@ -3,8 +3,11 @@ use crate::config::SETTINGS;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
use crate::hexrange::hex_range;
|
||||||
|
use crate::hexrange::HexSearch;
|
||||||
use crate::nip05;
|
use crate::nip05;
|
||||||
use crate::subscription::Subscription;
|
use crate::subscription::Subscription;
|
||||||
|
use crate::utils::is_hex;
|
||||||
use governor::clock::Clock;
|
use governor::clock::Clock;
|
||||||
use governor::{Quota, RateLimiter};
|
use governor::{Quota, RateLimiter};
|
||||||
use hex;
|
use hex;
|
||||||
|
@ -560,86 +563,6 @@ pub struct QueryResult {
|
||||||
pub event: String,
|
pub event: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a string contains only hex characters.
|
|
||||||
fn is_hex(s: &str) -> bool {
|
|
||||||
s.chars().all(|x| char::is_ascii_hexdigit(&x))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a string contains only f chars
|
|
||||||
fn is_all_fs(s: &str) -> bool {
|
|
||||||
s.chars().all(|x| x == 'f' || x == 'F')
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Types of hexadecimal queries.
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
|
||||||
enum HexSearch {
|
|
||||||
// when no range is needed, exact 32-byte
|
|
||||||
Exact(Vec<u8>),
|
|
||||||
// lower (inclusive) and upper range (exclusive)
|
|
||||||
Range(Vec<u8>, Vec<u8>),
|
|
||||||
// lower bound only, upper bound is MAX inclusive
|
|
||||||
LowerOnly(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find the next hex sequence greater than the argument.
|
|
||||||
fn hex_range(s: &str) -> Option<HexSearch> {
|
|
||||||
// handle special cases
|
|
||||||
if !is_hex(s) || s.len() > 64 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if s.len() == 64 {
|
|
||||||
return Some(HexSearch::Exact(hex::decode(s).ok()?));
|
|
||||||
}
|
|
||||||
// if s is odd, add a zero
|
|
||||||
let mut hash_base = s.to_owned();
|
|
||||||
let mut odd = hash_base.len() % 2 != 0;
|
|
||||||
if odd {
|
|
||||||
// extend the string to make it even
|
|
||||||
hash_base.push('0');
|
|
||||||
}
|
|
||||||
let base = hex::decode(hash_base).ok()?;
|
|
||||||
// check for all ff's
|
|
||||||
if is_all_fs(s) {
|
|
||||||
// there is no higher bound, we only want to search for blobs greater than this.
|
|
||||||
return Some(HexSearch::LowerOnly(base));
|
|
||||||
}
|
|
||||||
|
|
||||||
// return a range
|
|
||||||
let mut upper = base.clone();
|
|
||||||
let mut byte_len = upper.len();
|
|
||||||
|
|
||||||
// for odd strings, we made them longer, but we want to increment the upper char (+16).
|
|
||||||
// we know we can do this without overflowing because we explicitly set the bottom half to 0's.
|
|
||||||
while byte_len > 0 {
|
|
||||||
byte_len -= 1;
|
|
||||||
// check if byte can be incremented, or if we need to carry.
|
|
||||||
let b = upper[byte_len];
|
|
||||||
if b == u8::MAX {
|
|
||||||
// reset and carry
|
|
||||||
upper[byte_len] = 0;
|
|
||||||
} else if odd {
|
|
||||||
// check if first char in this byte is NOT 'f'
|
|
||||||
if b < 240 {
|
|
||||||
upper[byte_len] = b + 16; // bump up the first character in this byte
|
|
||||||
// increment done, stop iterating through the vec
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// if it is 'f', reset the byte to 0 and do a carry
|
|
||||||
// reset and carry
|
|
||||||
upper[byte_len] = 0;
|
|
||||||
}
|
|
||||||
// done with odd logic, so don't repeat this
|
|
||||||
odd = false;
|
|
||||||
} else {
|
|
||||||
// bump up the first character in this byte
|
|
||||||
upper[byte_len] = b + 1;
|
|
||||||
// increment done, stop iterating
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(HexSearch::Range(base, upper))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce a arbitrary list of '?' parameters.
|
/// Produce a arbitrary list of '?' parameters.
|
||||||
fn repeat_vars(count: usize) -> String {
|
fn repeat_vars(count: usize) -> String {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
|
@ -683,7 +606,6 @@ fn query_from_sub(sub: &Subscription) -> (String, Vec<Box<dyn ToSql>>) {
|
||||||
params.push(Box::new(upper));
|
params.push(Box::new(upper));
|
||||||
}
|
}
|
||||||
Some(HexSearch::LowerOnly(lower)) => {
|
Some(HexSearch::LowerOnly(lower)) => {
|
||||||
// info!("{:?} => lower; {:?} ", auth, hex::encode(lower));
|
|
||||||
auth_searches.push("author>?".to_owned());
|
auth_searches.push("author>?".to_owned());
|
||||||
params.push(Box::new(lower));
|
params.push(Box::new(lower));
|
||||||
}
|
}
|
||||||
|
@ -836,84 +758,3 @@ pub async fn db_query(
|
||||||
ok
|
ok
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_range_exact() -> Result<()> {
|
|
||||||
let hex = "abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00";
|
|
||||||
let r = hex_range(hex);
|
|
||||||
assert_eq!(
|
|
||||||
r,
|
|
||||||
Some(HexSearch::Exact(hex::decode(hex).expect("invalid hex")))
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn hex_full_range() -> Result<()> {
|
|
||||||
//let hex = "abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00";
|
|
||||||
let hex = "aaaa";
|
|
||||||
let hex_upper = "aaab";
|
|
||||||
let r = hex_range(hex);
|
|
||||||
assert_eq!(
|
|
||||||
r,
|
|
||||||
Some(HexSearch::Range(
|
|
||||||
hex::decode(hex).expect("invalid hex"),
|
|
||||||
hex::decode(hex_upper).expect("invalid hex")
|
|
||||||
))
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_full_range_odd() -> Result<()> {
|
|
||||||
let r = hex_range("abc");
|
|
||||||
assert_eq!(
|
|
||||||
r,
|
|
||||||
Some(HexSearch::Range(
|
|
||||||
hex::decode("abc0").expect("invalid hex"),
|
|
||||||
hex::decode("abd0").expect("invalid hex")
|
|
||||||
))
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_full_range_odd_end_f() -> Result<()> {
|
|
||||||
let r = hex_range("abf");
|
|
||||||
assert_eq!(
|
|
||||||
r,
|
|
||||||
Some(HexSearch::Range(
|
|
||||||
hex::decode("abf0").expect("invalid hex"),
|
|
||||||
hex::decode("ac00").expect("invalid hex")
|
|
||||||
))
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_no_upper() -> Result<()> {
|
|
||||||
let r = hex_range("ffff");
|
|
||||||
assert_eq!(
|
|
||||||
r,
|
|
||||||
Some(HexSearch::LowerOnly(
|
|
||||||
hex::decode("ffff").expect("invalid hex")
|
|
||||||
))
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_no_upper_odd() -> Result<()> {
|
|
||||||
let r = hex_range("fff");
|
|
||||||
assert_eq!(
|
|
||||||
r,
|
|
||||||
Some(HexSearch::LowerOnly(
|
|
||||||
hex::decode("fff0").expect("invalid hex")
|
|
||||||
))
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
10
src/event.rs
10
src/event.rs
|
@ -3,6 +3,7 @@ use crate::config;
|
||||||
use crate::error::Error::*;
|
use crate::error::Error::*;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::nip05;
|
use crate::nip05;
|
||||||
|
use crate::utils::unix_time;
|
||||||
use bitcoin_hashes::{sha256, Hash};
|
use bitcoin_hashes::{sha256, Hash};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
@ -13,7 +14,6 @@ use serde_json::Number;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Secp256k1 verification instance.
|
/// Secp256k1 verification instance.
|
||||||
|
@ -72,14 +72,6 @@ impl From<EventCmd> for Result<Event> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Seconds since 1970.
|
|
||||||
pub fn unix_time() -> u64 {
|
|
||||||
SystemTime::now()
|
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.map(|x| x.as_secs())
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
pub fn is_kind_metadata(&self) -> bool {
|
pub fn is_kind_metadata(&self) -> bool {
|
||||||
self.kind == 0
|
self.kind == 0
|
||||||
|
|
158
src/hexrange.rs
Normal file
158
src/hexrange.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
//! Utilities for searching hexadecimal
|
||||||
|
use crate::utils::is_hex;
|
||||||
|
use hex;
|
||||||
|
|
||||||
|
/// Types of hexadecimal queries.
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
pub enum HexSearch {
|
||||||
|
// when no range is needed, exact 32-byte
|
||||||
|
Exact(Vec<u8>),
|
||||||
|
// lower (inclusive) and upper range (exclusive)
|
||||||
|
Range(Vec<u8>, Vec<u8>),
|
||||||
|
// lower bound only, upper bound is MAX inclusive
|
||||||
|
LowerOnly(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a string contains only f chars
|
||||||
|
fn is_all_fs(s: &str) -> bool {
|
||||||
|
s.chars().all(|x| x == 'f' || x == 'F')
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the next hex sequence greater than the argument.
|
||||||
|
pub fn hex_range(s: &str) -> Option<HexSearch> {
|
||||||
|
// handle special cases
|
||||||
|
if !is_hex(s) || s.len() > 64 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if s.len() == 64 {
|
||||||
|
return Some(HexSearch::Exact(hex::decode(s).ok()?));
|
||||||
|
}
|
||||||
|
// if s is odd, add a zero
|
||||||
|
let mut hash_base = s.to_owned();
|
||||||
|
let mut odd = hash_base.len() % 2 != 0;
|
||||||
|
if odd {
|
||||||
|
// extend the string to make it even
|
||||||
|
hash_base.push('0');
|
||||||
|
}
|
||||||
|
let base = hex::decode(hash_base).ok()?;
|
||||||
|
// check for all ff's
|
||||||
|
if is_all_fs(s) {
|
||||||
|
// there is no higher bound, we only want to search for blobs greater than this.
|
||||||
|
return Some(HexSearch::LowerOnly(base));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a range
|
||||||
|
let mut upper = base.clone();
|
||||||
|
let mut byte_len = upper.len();
|
||||||
|
|
||||||
|
// for odd strings, we made them longer, but we want to increment the upper char (+16).
|
||||||
|
// we know we can do this without overflowing because we explicitly set the bottom half to 0's.
|
||||||
|
while byte_len > 0 {
|
||||||
|
byte_len -= 1;
|
||||||
|
// check if byte can be incremented, or if we need to carry.
|
||||||
|
let b = upper[byte_len];
|
||||||
|
if b == u8::MAX {
|
||||||
|
// reset and carry
|
||||||
|
upper[byte_len] = 0;
|
||||||
|
} else if odd {
|
||||||
|
// check if first char in this byte is NOT 'f'
|
||||||
|
if b < 240 {
|
||||||
|
upper[byte_len] = b + 16; // bump up the first character in this byte
|
||||||
|
// increment done, stop iterating through the vec
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// if it is 'f', reset the byte to 0 and do a carry
|
||||||
|
// reset and carry
|
||||||
|
upper[byte_len] = 0;
|
||||||
|
}
|
||||||
|
// done with odd logic, so don't repeat this
|
||||||
|
odd = false;
|
||||||
|
} else {
|
||||||
|
// bump up the first character in this byte
|
||||||
|
upper[byte_len] = b + 1;
|
||||||
|
// increment done, stop iterating
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(HexSearch::Range(base, upper))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_range_exact() -> Result<()> {
|
||||||
|
let hex = "abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00abcdef00";
|
||||||
|
let r = hex_range(hex);
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(HexSearch::Exact(hex::decode(hex).expect("invalid hex")))
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn hex_full_range() -> Result<()> {
|
||||||
|
let hex = "aaaa";
|
||||||
|
let hex_upper = "aaab";
|
||||||
|
let r = hex_range(hex);
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(HexSearch::Range(
|
||||||
|
hex::decode(hex).expect("invalid hex"),
|
||||||
|
hex::decode(hex_upper).expect("invalid hex")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_full_range_odd() -> Result<()> {
|
||||||
|
let r = hex_range("abc");
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(HexSearch::Range(
|
||||||
|
hex::decode("abc0").expect("invalid hex"),
|
||||||
|
hex::decode("abd0").expect("invalid hex")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_full_range_odd_end_f() -> Result<()> {
|
||||||
|
let r = hex_range("abf");
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(HexSearch::Range(
|
||||||
|
hex::decode("abf0").expect("invalid hex"),
|
||||||
|
hex::decode("ac00").expect("invalid hex")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_no_upper() -> Result<()> {
|
||||||
|
let r = hex_range("ffff");
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(HexSearch::LowerOnly(
|
||||||
|
hex::decode("ffff").expect("invalid hex")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_no_upper_odd() -> Result<()> {
|
||||||
|
let r = hex_range("fff");
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(HexSearch::LowerOnly(
|
||||||
|
hex::decode("fff0").expect("invalid hex")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,9 @@ pub mod conn;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
|
pub mod hexrange;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod nip05;
|
pub mod nip05;
|
||||||
pub mod protostream;
|
pub mod protostream;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
|
pub mod utils;
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
use crate::config::SETTINGS;
|
use crate::config::SETTINGS;
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::event::{unix_time, Event};
|
use crate::event::Event;
|
||||||
|
use crate::utils::unix_time;
|
||||||
use hyper::body::HttpBody;
|
use hyper::body::HttpBody;
|
||||||
use hyper::client::connect::HttpConnector;
|
use hyper::client::connect::HttpConnector;
|
||||||
use hyper::Client;
|
use hyper::Client;
|
||||||
|
|
15
src/utils.rs
Normal file
15
src/utils.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//! Common utility functions
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
/// Seconds since 1970.
|
||||||
|
pub fn unix_time() -> u64 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.map(|x| x.as_secs())
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a string contains only hex characters.
|
||||||
|
pub fn is_hex(s: &str) -> bool {
|
||||||
|
s.chars().all(|x| char::is_ascii_hexdigit(&x))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user