230 lines
8.1 KiB
Rust
230 lines
8.1 KiB
Rust
extern crate console_error_panic_hook;
|
|
|
|
use argon2::{Argon2};
|
|
use secp256k1::{Message, XOnlyPublicKey, SECP256K1};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::json;
|
|
use crate::filter::{FlatReqFilter, ReqFilter};
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
pub mod diff;
|
|
pub mod filter;
|
|
pub mod merge;
|
|
pub mod pow;
|
|
|
|
#[derive(PartialEq, Clone, Serialize, Deserialize)]
|
|
pub struct Event {
|
|
#[serde(rename = "id", skip_serializing_if = "Option::is_none")]
|
|
pub id: Option<String>,
|
|
pub kind: i32,
|
|
pub created_at: u64,
|
|
pub pubkey: String,
|
|
pub content: String,
|
|
#[serde(rename = "sig", skip_serializing_if = "Option::is_none")]
|
|
pub sig: Option<String>,
|
|
pub tags: Vec<Vec<String>>,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn diff_filters(prev: JsValue, next: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let prev_parsed: Vec<FlatReqFilter> = serde_wasm_bindgen::from_value(prev)?;
|
|
let next_parsed: Vec<FlatReqFilter> = serde_wasm_bindgen::from_value(next)?;
|
|
let result = diff::diff_filter(&prev_parsed, &next_parsed);
|
|
Ok(serde_wasm_bindgen::to_value(&result)?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn expand_filter(val: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let parsed: ReqFilter = serde_wasm_bindgen::from_value(val)?;
|
|
let result: Vec<FlatReqFilter> = (&parsed).into();
|
|
Ok(serde_wasm_bindgen::to_value(&result)?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn get_diff(prev: JsValue, next: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let prev_parsed: Vec<ReqFilter> = serde_wasm_bindgen::from_value(prev)?;
|
|
let next_parsed: Vec<ReqFilter> = serde_wasm_bindgen::from_value(next)?;
|
|
let expanded_prev: Vec<FlatReqFilter> = prev_parsed
|
|
.iter()
|
|
.flat_map(|v| {
|
|
let vec: Vec<FlatReqFilter> = v.into();
|
|
vec
|
|
})
|
|
.collect();
|
|
let expanded_next: Vec<FlatReqFilter> = next_parsed
|
|
.iter()
|
|
.flat_map(|v| {
|
|
let vec: Vec<FlatReqFilter> = v.into();
|
|
vec
|
|
})
|
|
.collect();
|
|
let result = diff::diff_filter(&expanded_prev, &expanded_next);
|
|
Ok(serde_wasm_bindgen::to_value(&result)?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn flat_merge(val: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let val_parsed: Vec<FlatReqFilter> = serde_wasm_bindgen::from_value(val)?;
|
|
let result = merge::merge::<FlatReqFilter, ReqFilter>(val_parsed.iter().collect());
|
|
Ok(serde_wasm_bindgen::to_value(&result)?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn compress(val: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let val_parsed: Vec<ReqFilter> = serde_wasm_bindgen::from_value(val)?;
|
|
let result = merge::merge::<ReqFilter, ReqFilter>(val_parsed.iter().collect());
|
|
Ok(serde_wasm_bindgen::to_value(&result)?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn pow(val: JsValue, target: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let mut val_parsed: Event = serde_wasm_bindgen::from_value(val)?;
|
|
let target_parsed: u8 = serde_wasm_bindgen::from_value(target)?;
|
|
pow::pow(&mut val_parsed, target_parsed);
|
|
Ok(serde_wasm_bindgen::to_value(&val_parsed)?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn argon2(password: JsValue, salt: JsValue) -> Result<JsValue, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let password_parsed: String = serde_wasm_bindgen::from_value(password)?;
|
|
let salt_parsed: String = serde_wasm_bindgen::from_value(salt)?;
|
|
let mut key = [0u8; 32];
|
|
Argon2::default().hash_password_into(password_parsed.as_bytes(), salt_parsed.as_bytes(), &mut key).expect("Failed to generate key");
|
|
Ok(serde_wasm_bindgen::to_value(&hex::encode(key))?)
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn schnorr_verify(hash: JsValue, sig: JsValue, pub_key: JsValue) -> Result<bool, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let msg_hex: String = serde_wasm_bindgen::from_value(hash)?;
|
|
let sig_hex: String = serde_wasm_bindgen::from_value(sig)?;
|
|
let pub_key_hex: String = serde_wasm_bindgen::from_value(pub_key)?;
|
|
|
|
let msg = Message::from_digest_slice(&hex::decode(msg_hex).unwrap()).unwrap();
|
|
let key = XOnlyPublicKey::from_slice(&hex::decode(pub_key_hex).unwrap()).unwrap();
|
|
let sig = secp256k1::schnorr::Signature::from_slice(&hex::decode(sig_hex).unwrap()).unwrap();
|
|
Ok(SECP256K1.verify_schnorr(&sig, &msg, &key).is_ok())
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn schnorr_verify_event(event: JsValue) -> Result<bool, JsValue> {
|
|
console_error_panic_hook::set_once();
|
|
let event_obj: Event = serde_wasm_bindgen::from_value(event)?;
|
|
|
|
let json = json!([0, event_obj.pubkey, event_obj.created_at, event_obj.kind, event_obj.tags, event_obj.content]);
|
|
let id = sha256::digest(json.to_string().as_bytes());
|
|
|
|
let msg = Message::from_digest_slice(&hex::decode(id).unwrap()).unwrap();
|
|
let key = XOnlyPublicKey::from_slice(&hex::decode(&event_obj.pubkey).unwrap()).unwrap();
|
|
let sig = secp256k1::schnorr::Signature::from_slice(&hex::decode(&event_obj.sig.unwrap()).unwrap()).unwrap();
|
|
Ok(SECP256K1.verify_schnorr(&sig, &msg, &key).is_ok())
|
|
}
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use itertools::Itertools;
|
|
use std::cmp::Ordering;
|
|
use std::collections::HashSet;
|
|
|
|
#[test]
|
|
fn flat_merge_expanded() {
|
|
let input = vec![
|
|
ReqFilter {
|
|
ids: None,
|
|
kinds: Some(HashSet::from([1, 6969, 6])),
|
|
e_tag: None,
|
|
p_tag: None,
|
|
t_tag: None,
|
|
d_tag: None,
|
|
r_tag: None,
|
|
a_tag: None,
|
|
g_tag: None,
|
|
authors: Some(HashSet::from([
|
|
"kieran".to_string(),
|
|
"snort".to_string(),
|
|
"c".to_string(),
|
|
"d".to_string(),
|
|
"e".to_string(),
|
|
])),
|
|
since: Some(1),
|
|
until: Some(100),
|
|
search: None,
|
|
limit: None,
|
|
},
|
|
ReqFilter {
|
|
ids: None,
|
|
kinds: Some(HashSet::from([4])),
|
|
e_tag: None,
|
|
p_tag: None,
|
|
t_tag: None,
|
|
d_tag: None,
|
|
r_tag: None,
|
|
a_tag: None,
|
|
g_tag: None,
|
|
search: None,
|
|
since: None,
|
|
until: None,
|
|
authors: Some(HashSet::from(["kieran".to_string()])),
|
|
limit: None,
|
|
},
|
|
ReqFilter {
|
|
ids: None,
|
|
authors: None,
|
|
kinds: Some(HashSet::from([4])),
|
|
e_tag: None,
|
|
p_tag: Some(HashSet::from(["kieran".to_string()])),
|
|
t_tag: None,
|
|
d_tag: None,
|
|
r_tag: None,
|
|
a_tag: None,
|
|
g_tag: None,
|
|
search: None,
|
|
since: None,
|
|
until: None,
|
|
limit: None,
|
|
},
|
|
ReqFilter {
|
|
ids: None,
|
|
kinds: Some(HashSet::from([1000])),
|
|
authors: Some(HashSet::from(["snort".to_string()])),
|
|
p_tag: Some(HashSet::from(["kieran".to_string()])),
|
|
t_tag: None,
|
|
d_tag: None,
|
|
r_tag: None,
|
|
a_tag: None,
|
|
g_tag: None,
|
|
search: None,
|
|
since: None,
|
|
until: None,
|
|
e_tag: None,
|
|
limit: None,
|
|
},
|
|
];
|
|
|
|
let expanded: Vec<FlatReqFilter> = input
|
|
.iter()
|
|
.flat_map(|v| {
|
|
let r: Vec<FlatReqFilter> = v.into();
|
|
r
|
|
})
|
|
.sorted_by(|_, _| {
|
|
if rand::random() {
|
|
Ordering::Less
|
|
} else {
|
|
Ordering::Greater
|
|
}
|
|
})
|
|
.collect();
|
|
let merged_expanded: Vec<ReqFilter> = merge::merge(expanded.iter().collect());
|
|
assert_eq!(merged_expanded.len(), input.len());
|
|
assert!(merged_expanded.iter().all(|v| input.contains(v)));
|
|
}
|
|
}
|