feat: add support for basic web of trust (#65)
Reviewed-on: #65
This commit was merged in pull request #65.
This commit is contained in:
+45
-5
@@ -14,6 +14,7 @@ use gpui::{
|
||||
use instant::Duration;
|
||||
use nip4e::{DeviceEvent, DeviceRegistry};
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP};
|
||||
|
||||
@@ -139,13 +140,13 @@ impl ChatRegistry {
|
||||
/// Create a new chat registry instance
|
||||
fn new(cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let device = DeviceRegistry::global(cx);
|
||||
let wot = PersonRegistry::global(cx).read(cx).wot();
|
||||
let entity = cx.entity().downgrade();
|
||||
|
||||
let (tx, rx) = flume::unbounded::<Signal>();
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
let device = DeviceRegistry::global(cx);
|
||||
|
||||
subscriptions.push(cx.subscribe(&nostr, |this, _nostr, event, cx| {
|
||||
if event.signer_changed() {
|
||||
this.reset(cx);
|
||||
@@ -163,6 +164,10 @@ impl ChatRegistry {
|
||||
}
|
||||
}));
|
||||
|
||||
subscriptions.push(cx.subscribe(&wot, |this, _wot, _event, cx| {
|
||||
this.promote_trusted(cx);
|
||||
}));
|
||||
|
||||
cx.defer(move |cx| {
|
||||
let _ = entity.update(cx, |this, cx| {
|
||||
this.get_rooms(cx);
|
||||
@@ -567,7 +572,23 @@ impl ChatRegistry {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Add a new room to the start of list.
|
||||
/// Promote request rooms whose senders are inside the web of trust
|
||||
fn promote_trusted(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let Some(me) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
for room in &self.rooms {
|
||||
room.update(cx, |room, cx| {
|
||||
if room.kind == RoomKind::Request && room.auto_trusted(me, cx) {
|
||||
room.set_ongoing(cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Promote a room to ongoing when its latest message is from the current user
|
||||
pub(crate) fn add_room<I>(&mut self, room: I, cx: &mut Context<Self>)
|
||||
where
|
||||
I: Into<Room>,
|
||||
@@ -577,7 +598,12 @@ impl ChatRegistry {
|
||||
return;
|
||||
};
|
||||
|
||||
let room: Room = room.into().organize(&public_key);
|
||||
let mut room: Room = room.into().organize(&public_key);
|
||||
|
||||
if room.auto_trusted(public_key, cx) {
|
||||
room.kind = RoomKind::Ongoing;
|
||||
}
|
||||
|
||||
let room_id = room.id;
|
||||
let entity = cx.new(|_| room);
|
||||
entity.update(cx, |room, cx| room.ensure_directory(cx));
|
||||
@@ -652,6 +678,11 @@ impl ChatRegistry {
|
||||
|
||||
/// Extend the registry with new rooms.
|
||||
fn extend_rooms(&mut self, rooms: HashSet<Room>, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let Some(me) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut room_map: HashMap<u64, usize> = self
|
||||
.rooms
|
||||
.iter()
|
||||
@@ -660,6 +691,13 @@ impl ChatRegistry {
|
||||
.collect();
|
||||
|
||||
for new_room in rooms.into_iter() {
|
||||
// Auto-trust also applies to rooms loaded from the database
|
||||
let new_room = if new_room.kind == RoomKind::Request && new_room.auto_trusted(me, cx) {
|
||||
new_room.kind(RoomKind::Ongoing)
|
||||
} else {
|
||||
new_room
|
||||
};
|
||||
|
||||
// Check if we already have a room with this ID
|
||||
if let Some(&index) = room_map.get(&new_room.id) {
|
||||
self.rooms[index].update(cx, |this, cx| {
|
||||
@@ -789,7 +827,9 @@ impl ChatRegistry {
|
||||
match self.room_index.get(&message.room).cloned() {
|
||||
Some(room) => {
|
||||
room.update(cx, |this, cx| {
|
||||
if this.kind == RoomKind::Request && message.rumor.pubkey == public_key {
|
||||
if this.kind == RoomKind::Request
|
||||
&& (message.rumor.pubkey == public_key || this.auto_trusted(public_key, cx))
|
||||
{
|
||||
this.set_ongoing(cx);
|
||||
}
|
||||
this.push_message(message, cx);
|
||||
|
||||
+57
-2
@@ -8,8 +8,8 @@ use instant::Duration;
|
||||
use itertools::Itertools;
|
||||
use nip4e::DeviceRegistry;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Directory, Person};
|
||||
use settings::{RoomConfig, SignerKind};
|
||||
use person::{Directory, Person, PersonRegistry};
|
||||
use settings::{AppSettings, RoomConfig, SignerKind};
|
||||
use state::{NostrRegistry, TIMEOUT, UniversalSigner};
|
||||
|
||||
use crate::{FileAttachment, KIND_FILE_MESSAGE, NewMessage};
|
||||
@@ -183,6 +183,61 @@ impl Room {
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether the auto-trust setting promotes this room out of the requests:
|
||||
/// everyone other than the current user is inside the web of trust
|
||||
pub fn auto_trusted(&self, me: PublicKey, cx: &App) -> bool {
|
||||
if !AppSettings::get_wot_auto_trust(cx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let wot = PersonRegistry::global(cx).read(cx).wot().read(cx);
|
||||
|
||||
Self::trusted_by(&self.members, me, |key| wot.is_in_network(key))
|
||||
}
|
||||
|
||||
/// Whether the room looks like spam: the web of trust is loaded and
|
||||
/// everyone other than the current user is outside of it
|
||||
pub fn is_spam(&self, cx: &App) -> bool {
|
||||
let person = PersonRegistry::global(cx);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
let Some(me) = nostr.read(cx).current_user() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let wot = person.read(cx).wot().read(cx);
|
||||
|
||||
// An unloaded graph cannot classify anything as spam
|
||||
if !wot.is_loaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Self::trusted_by(&self.members, me, |key| wot.is_outside(key))
|
||||
}
|
||||
|
||||
/// Whether every member other than `me` passes the check
|
||||
fn trusted_by(
|
||||
members: &[PublicKey],
|
||||
me: PublicKey,
|
||||
check: impl Fn(&PublicKey) -> bool,
|
||||
) -> bool {
|
||||
let mut others = false;
|
||||
|
||||
for key in members {
|
||||
if *key == me {
|
||||
continue;
|
||||
}
|
||||
|
||||
others = true;
|
||||
|
||||
if !check(key) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
others
|
||||
}
|
||||
|
||||
/// Sets this room is ongoing conversation
|
||||
pub(crate) fn set_ongoing(&mut self, cx: &mut Context<Self>) {
|
||||
self.kind = RoomKind::Ongoing;
|
||||
|
||||
Reference in New Issue
Block a user