feat: improve negentropy sync

This commit is contained in:
2024-10-11 08:56:43 +07:00
parent 790fee6c05
commit adad048873
6 changed files with 129 additions and 109 deletions

View File

@@ -10,12 +10,8 @@ use std::{
time::Duration, time::Duration,
}; };
use tauri::{Emitter, Manager, State}; use tauri::{Emitter, Manager, State};
use tokio::time::sleep;
use crate::{ use crate::{Nostr, NOTIFICATION_SUB_ID};
common::{get_latest_event, init_nip65},
Nostr, NOTIFICATION_SUB_ID,
};
#[derive(Debug, Clone, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Serialize, Deserialize, Type)]
struct Account { struct Account {
@@ -284,7 +280,7 @@ pub async fn login(
}; };
// NIP-65: Connect to user's relay list // NIP-65: Connect to user's relay list
init_nip65(client, &public_key).await; // init_nip65(client, &public_key).await;
// NIP-03: Get user's contact list // NIP-03: Get user's contact list
let contact_list = { let contact_list = {
@@ -325,8 +321,8 @@ pub async fn login(
.reconcile( .reconcile(
Filter::new() Filter::new()
.authors(authors.clone()) .authors(authors.clone())
.kinds(vec![Kind::Metadata, Kind::ContactList, Kind::EventDeletion]) .kinds(vec![Kind::Metadata, Kind::ContactList])
.limit(authors.len() * 20), .limit(authors.len() * 10),
NegentropyOptions::default(), NegentropyOptions::default(),
) )
.await .await
@@ -339,8 +335,8 @@ pub async fn login(
.reconcile( .reconcile(
Filter::new() Filter::new()
.authors(authors.clone()) .authors(authors.clone())
.kinds(vec![Kind::TextNote, Kind::Repost]) .kinds(vec![Kind::TextNote, Kind::Repost, Kind::EventDeletion])
.limit(authors.len() * 50), .limit(authors.len() * 40),
NegentropyOptions::default(), NegentropyOptions::default(),
) )
.await .await
@@ -350,38 +346,73 @@ pub async fn login(
// Create the trusted public key list from contact list // Create the trusted public key list from contact list
// TODO: create a cached file // TODO: create a cached file
let mut trusted_list: HashSet<PublicKey> = HashSet::new(); if let Ok(events) = client
.database()
for author in authors.into_iter() { .query(vec![Filter::new().kind(Kind::ContactList)])
trusted_list.insert(author); .await
let filter = Filter::new()
.author(author)
.kind(Kind::ContactList)
.limit(1);
if let Ok(events) = client.database().query(vec![filter]).await {
if let Some(event) = get_latest_event(&events) {
for tag in event.tags.iter() {
if let Some(TagStandard::PublicKey {
public_key,
uppercase: false,
..
}) = tag.to_owned().to_standardized()
{ {
trusted_list.insert(public_key); let keys: Vec<&str> = events
}; .iter()
} .flat_map(|event| event.get_tags_content(TagKind::p()))
} .collect();
}
let trusted_list: HashSet<PublicKey> = keys
.into_iter()
.filter_map(|item| {
if let Ok(pk) = PublicKey::from_str(item) {
Some(pk)
} else {
None
} }
})
.collect();
// Update app's state // Update app's state
state.trusted_list.lock().await.clone_from(&trusted_list); state.trusted_list.lock().await.clone_from(&trusted_list);
let trusted_users: Vec<PublicKey> = trusted_list.into_iter().collect();
println!("Total trusted users: {}", trusted_users.len());
if let Ok(report) = client
.reconcile(
Filter::new()
.authors(trusted_users)
.kinds(vec![
Kind::Metadata,
Kind::TextNote,
Kind::Repost,
Kind::EventDeletion,
])
.limit(5000),
NegentropyOptions::default(),
)
.await
{
println!("Received: {}", report.received.len())
}
};
};
// Syncing all user's events // Syncing all user's events
if let Ok(report) = client if let Ok(report) = client
.reconcile(Filter::new().author(author), NegentropyOptions::default()) .reconcile(
Filter::new().author(author).kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::FollowSet,
Kind::InterestSet,
Kind::Interests,
Kind::EventDeletion,
Kind::MuteList,
Kind::BookmarkSet,
Kind::BlockedRelays,
Kind::EmojiSet,
Kind::RelaySet,
Kind::RelayList,
Kind::ApplicationSpecificData,
]),
NegentropyOptions::default(),
)
.await .await
{ {
println!("Received: {}", report.received.len()) println!("Received: {}", report.received.len())
@@ -401,32 +432,7 @@ pub async fn login(
.await .await
{ {
println!("Received: {}", report.received.len()) println!("Received: {}", report.received.len())
} };
// Syncing all events for trusted list
let trusted: Vec<PublicKey> = trusted_list.into_iter().collect();
if let Ok(report) = client
.reconcile(
Filter::new()
.authors(trusted)
.kinds(vec![
Kind::Metadata,
Kind::TextNote,
Kind::Repost,
Kind::EventDeletion,
])
.limit(30000),
NegentropyOptions::default(),
)
.await
{
println!("Received: {}", report.received.len())
}
// Wait a little longer
// TODO: remove?
sleep(Duration::from_secs(5)).await;
}
handle handle
.emit("neg_synchronized", ()) .emit("neg_synchronized", ())

View File

@@ -40,25 +40,26 @@ pub async fn get_event(id: String, state: State<'_, Nostr>) -> Result<RichEvent,
Ok(RichEvent { raw, parsed }) Ok(RichEvent { raw, parsed })
} else { } else {
println!("Not found, getting event from relays...");
match client match client
.stream_events_of(vec![filter], Some(Duration::from_secs(10))) .get_events_of(
vec![filter],
EventSource::relays(Some(Duration::from_secs(10))),
)
.await .await
{ {
Ok(mut rx) => { Ok(events) => {
let mut raw: String = String::new(); if let Some(event) = get_latest_event(&events) {
let mut parsed: Option<Meta> = None; let raw = event.as_json();
let parsed = if event.kind == Kind::TextNote {
while let Some(event) = rx.next().await {
raw = event.as_json();
parsed = if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await) Some(parse_event(&event.content).await)
} else { } else {
None None
}; };
}
Ok(RichEvent { raw, parsed }) Ok(RichEvent { raw, parsed })
} else {
Err("Not found.".into())
}
} }
Err(err) => Err(err.to_string()), Err(err) => Err(err.to_string()),
} }

View File

@@ -58,23 +58,23 @@ pub async fn get_profile(id: Option<String>, state: State<'_, Nostr>) -> Result<
Err("Parse metadata failed".into()) Err("Parse metadata failed".into())
} }
} else { } else {
println!("Not found, getting event from relays...");
match client match client
.stream_events_of(vec![filter], Some(Duration::from_secs(10))) .get_events_of(
vec![filter],
EventSource::relays(Some(Duration::from_secs(10))),
)
.await .await
{ {
Ok(mut rx) => { Ok(events) => {
let mut metadata: String = Metadata::new().as_json(); if let Some(event) = get_latest_event(&events) {
if let Ok(metadata) = Metadata::from_json(&event.content) {
while let Some(event) = rx.next().await { Ok(metadata.as_json())
println!("Event: {}", event.as_json()); } else {
if let Ok(m) = Metadata::from_json(&event.content) { Err("Metadata is not valid.".into())
metadata = m.as_json();
break;
} }
} else {
Err("Not found.".into())
} }
Ok(metadata)
} }
Err(e) => Err(e.to_string()), Err(e) => Err(e.to_string()),
} }

View File

@@ -273,8 +273,12 @@ fn main() {
println!("Add discovery relay failed: {}", e) println!("Add discovery relay failed: {}", e)
} }
if let Err(e) = client.add_discovery_relay("wss://user.kindpag.es/").await {
println!("Add discovery relay failed: {}", e)
}
// Connect // Connect
client.connect_with_timeout(Duration::from_secs(20)).await; client.connect_with_timeout(Duration::from_secs(10)).await;
client client
}); });

View File

@@ -113,10 +113,12 @@ const ContentWarning = memo(function ContentWarning({
}: { warning: string }) { }: { warning: string }) {
const [blurred, setBlurred] = useState(() => warning?.length > 0); const [blurred, setBlurred] = useState(() => warning?.length > 0);
if (!blurred) return null; if (!blurred) {
return null;
}
return ( return (
<div className="absolute inset-0 z-10 flex items-center justify-center w-full h-full bg-black/80 backdrop-blur-lg"> <div className="absolute inset-0 z-10 flex items-center justify-center w-full bg-black/80 backdrop-blur-lg">
<div className="flex flex-col items-center justify-center gap-2 text-center"> <div className="flex flex-col items-center justify-center gap-2 text-center">
<p className="text-sm text-white/60"> <p className="text-sm text-white/60">
The content is hidden because the author The content is hidden because the author

View File

@@ -80,7 +80,7 @@ function Screen() {
<div className="flex flex-col gap-1 text-center"> <div className="flex flex-col gap-1 text-center">
<h1 className="leading-tight text-xl font-semibold">Manage Relays</h1> <h1 className="leading-tight text-xl font-semibold">Manage Relays</h1>
<p className="text-sm text-neutral-700 dark:text-neutral-300"> <p className="text-sm text-neutral-700 dark:text-neutral-300">
This relays will be only use to get user's metadata. The default relays that Lume will connected.
</p> </p>
</div> </div>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
@@ -127,6 +127,13 @@ function Screen() {
</div> </div>
))} ))}
</div> </div>
<div className="text-xs text-neutral-600 dark:text-neutral-400">
<p>
Lume is heavily depend on Negentropy for syncing data. You need
to use at least 1 relay that support Negentropy. If you not
sure, you can keep using the default relay list.
</p>
</div>
</Frame> </Frame>
<div className="flex flex-col items-center gap-1"> <div className="flex flex-col items-center gap-1">
<button <button