mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-05-13 05:29:34 +00:00
Remove ClientCtx
This commit is contained in:
parent
3bf8ecb904
commit
d8e15dc8d6
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1424,6 +1424,7 @@ dependencies = [
|
|||||||
"double-ratchets",
|
"double-ratchets",
|
||||||
"hex",
|
"hex",
|
||||||
"openmls",
|
"openmls",
|
||||||
|
"openmls_libcrux_crypto 0.3.1",
|
||||||
"openmls_traits 0.5.0",
|
"openmls_traits 0.5.0",
|
||||||
"prost",
|
"prost",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
|||||||
@ -21,6 +21,7 @@ thiserror = "2.0.17"
|
|||||||
x25519-dalek = { version = "2.0.1", features = ["static_secrets", "reusable_secrets", "getrandom"] }
|
x25519-dalek = { version = "2.0.1", features = ["static_secrets", "reusable_secrets", "getrandom"] }
|
||||||
storage = { path = "../storage" }
|
storage = { path = "../storage" }
|
||||||
openmls = { version = "0.8.1", features = ["libcrux-provider"] }
|
openmls = { version = "0.8.1", features = ["libcrux-provider"] }
|
||||||
|
openmls_libcrux_crypto = "0.3.1"
|
||||||
openmls_traits = "0.5.0"
|
openmls_traits = "0.5.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@ -18,11 +18,11 @@ impl LogosAccount {
|
|||||||
/// TODO: (P1) Remove once implementation is ready.
|
/// TODO: (P1) Remove once implementation is ready.
|
||||||
pub fn new_test(explicit_id: impl Into<String>) -> Self {
|
pub fn new_test(explicit_id: impl Into<String>) -> Self {
|
||||||
let signing_key = Ed25519SigningKey::generate();
|
let signing_key = Ed25519SigningKey::generate();
|
||||||
let verifying_key = signing_key.verifying_key()
|
let verifying_key = signing_key.verifying_key();
|
||||||
Self {
|
Self {
|
||||||
id: AccountId::new(explicit_id.into()),
|
id: AccountId::new(explicit_id.into()),
|
||||||
signing_key,
|
signing_key,
|
||||||
verifying_key
|
verifying_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
use std::cell::Ref;
|
use std::cell::{Ref, RefMut};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::account::LogosAccount;
|
use crate::account::LogosAccount;
|
||||||
use crate::conversation::{Convo, GroupConvo, IdentityProvider};
|
use crate::conversation::{Convo, GroupConvo};
|
||||||
use crate::ctx::ClientCtx;
|
|
||||||
|
|
||||||
use crate::account::LogosAccount;
|
|
||||||
use crate::{DeliveryService, RegistrationService};
|
use crate::{DeliveryService, RegistrationService};
|
||||||
use crate::{
|
use crate::{
|
||||||
conversation::{Conversation, Id, PrivateV1Convo},
|
conversation::{Conversation, Id, PrivateV1Convo},
|
||||||
@ -26,13 +24,18 @@ pub use crate::inbox::Introduction;
|
|||||||
// Ctx manages lifetimes of objects to process and generate payloads.
|
// Ctx manages lifetimes of objects to process and generate payloads.
|
||||||
pub struct Context<DS: DeliveryService, RS: RegistrationService, CS: ChatStore> {
|
pub struct Context<DS: DeliveryService, RS: RegistrationService, CS: ChatStore> {
|
||||||
identity: Rc<Identity>,
|
identity: Rc<Identity>,
|
||||||
client_ctx: ClientCtx<DS, RS, CS>,
|
ds: Rc<RefCell<DS>>,
|
||||||
inbox: Inbox<CS>,
|
|
||||||
pq_inbox: InboxV2,
|
|
||||||
store: Rc<RefCell<CS>>,
|
store: Rc<RefCell<CS>>,
|
||||||
|
inbox: Inbox<CS>,
|
||||||
|
pq_inbox: InboxV2<DS, RS, CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Context<DS, RS, CS> {
|
impl<DS, RS, CS> Context<DS, RS, CS>
|
||||||
|
where
|
||||||
|
DS: DeliveryService + 'static,
|
||||||
|
RS: RegistrationService + 'static,
|
||||||
|
CS: ChatStore + 'static,
|
||||||
|
{
|
||||||
/// Opens or creates a Context with the given storage configuration.
|
/// Opens or creates a Context with the given storage configuration.
|
||||||
///
|
///
|
||||||
/// If an identity exists in storage, it will be restored.
|
/// If an identity exists in storage, it will be restored.
|
||||||
@ -40,13 +43,15 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
pub fn new_from_store(
|
pub fn new_from_store(
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
delivery: DS,
|
delivery: DS,
|
||||||
contact_reg: RS,
|
registration: RS,
|
||||||
store: CS,
|
store: CS,
|
||||||
) -> Result<Self, ChatError> {
|
) -> Result<Self, ChatError> {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
|
||||||
|
// Services for sharing with Converastions/Inboxes
|
||||||
|
let ds = Rc::new(RefCell::new(delivery));
|
||||||
|
let contact_registry = Rc::new(RefCell::new(registration));
|
||||||
let store = Rc::new(RefCell::new(store));
|
let store = Rc::new(RefCell::new(store));
|
||||||
let mut ctx = ClientCtx::new(delivery, contact_reg, store.clone());
|
|
||||||
|
|
||||||
// Load or create identity
|
// Load or create identity
|
||||||
let identity = if let Some(identity) = store.borrow().load_identity()? {
|
let identity = if let Some(identity) = store.borrow().load_identity()? {
|
||||||
@ -60,20 +65,24 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
let identity = Rc::new(identity);
|
let identity = Rc::new(identity);
|
||||||
let inbox = Inbox::new(Rc::clone(&store), Rc::clone(&identity));
|
let inbox = Inbox::new(Rc::clone(&store), Rc::clone(&identity));
|
||||||
|
|
||||||
let pq_inbox = InboxV2::new_with_account(LogosAccount::new_test(name));
|
let pq_inbox = InboxV2::new(
|
||||||
|
LogosAccount::new_test(name),
|
||||||
|
ds.clone(),
|
||||||
|
contact_registry.clone(),
|
||||||
|
store.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
// Subscribe
|
// Subscribe
|
||||||
ctx.ds()
|
ds.borrow_mut()
|
||||||
.subscribe(&pq_inbox.delivery_address())
|
.subscribe(&pq_inbox.delivery_address())
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
identity: identity,
|
identity: identity,
|
||||||
client_ctx: ctx,
|
ds,
|
||||||
|
store,
|
||||||
inbox,
|
inbox,
|
||||||
pq_inbox,
|
pq_inbox,
|
||||||
store,
|
|
||||||
account: LogosAccount::new_test(name.as_str()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,47 +92,55 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
pub fn new_with_name(
|
pub fn new_with_name(
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
delivery: DS,
|
delivery: DS,
|
||||||
contact_reg: RS,
|
registration: RS,
|
||||||
chat_store: CS,
|
chat_store: CS,
|
||||||
) -> Result<Self, ChatError> {
|
) -> Result<Self, ChatError> {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
let identity = Identity::new(&name);
|
let identity = Identity::new(&name);
|
||||||
|
|
||||||
let chat_store = Rc::new(RefCell::new(chat_store));
|
// Services for sharing with Converastions/Inboxes
|
||||||
let mut ctx = ClientCtx::new(delivery, contact_reg, chat_store.clone());
|
let ds = Rc::new(RefCell::new(delivery));
|
||||||
chat_store
|
let contact_registry = Rc::new(RefCell::new(registration));
|
||||||
|
let store = Rc::new(RefCell::new(chat_store));
|
||||||
|
|
||||||
|
store
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.save_identity(&identity)
|
.save_identity(&identity)
|
||||||
.expect("in-memory storage should not fail");
|
.expect("in-memory storage should not fail");
|
||||||
|
|
||||||
let identity = Rc::new(identity);
|
let identity = Rc::new(identity);
|
||||||
let inbox = Inbox::new(Rc::clone(&chat_store), Rc::clone(&identity));
|
let inbox = Inbox::new(store.clone(), Rc::clone(&identity));
|
||||||
let mut pq_inbox = InboxV2::new_with_account(LogosAccount::new_test(name));
|
let mut pq_inbox = InboxV2::new(
|
||||||
pq_inbox.register(&mut ctx)?;
|
LogosAccount::new_test(name),
|
||||||
|
ds.clone(),
|
||||||
|
contact_registry.clone(),
|
||||||
|
store.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
ctx.ds()
|
// TODO: (!) This seems weird here
|
||||||
|
pq_inbox.register()?;
|
||||||
|
|
||||||
|
ds.borrow_mut()
|
||||||
.subscribe(&pq_inbox.delivery_address())
|
.subscribe(&pq_inbox.delivery_address())
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
identity,
|
identity,
|
||||||
client_ctx: ctx,
|
ds,
|
||||||
|
store,
|
||||||
pq_inbox,
|
pq_inbox,
|
||||||
inbox,
|
inbox,
|
||||||
|
|
||||||
store: chat_store,
|
|
||||||
account: LogosAccount::new_test(name.as_str()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ds(&self) -> RefMut<'_, DS> {
|
||||||
|
self.ds.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn store(&self) -> Ref<'_, CS> {
|
pub fn store(&self) -> Ref<'_, CS> {
|
||||||
self.store.borrow()
|
self.store.borrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn client_ctx(&mut self) -> &mut ClientCtx<DS, RS, CS> {
|
|
||||||
&mut self.client_ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn identity(&self) -> &Identity {
|
pub fn identity(&self) -> &Identity {
|
||||||
&self.identity
|
&self.identity
|
||||||
}
|
}
|
||||||
@ -164,16 +181,17 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
pub fn create_group_convo(
|
pub fn create_group_convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
participants: &[&AccountId],
|
participants: &[&AccountId],
|
||||||
) -> Result<Box<dyn GroupConvo<DS, RS, CS>>, ChatError> {
|
) -> Result<Box<dyn GroupConvo<DS, RS>>, ChatError> {
|
||||||
let mut convo = self.pq_inbox.create_group_v1(&mut self.client_ctx)?;
|
// TODO: (!) Perform this in InboxV2?
|
||||||
self.client_ctx
|
let mut convo = self.pq_inbox.create_group_v1()?;
|
||||||
.store()
|
self.store
|
||||||
|
.borrow_mut()
|
||||||
.save_conversation(&storage::ConversationMeta {
|
.save_conversation(&storage::ConversationMeta {
|
||||||
local_convo_id: convo.id().to_string(),
|
local_convo_id: convo.id().to_string(),
|
||||||
remote_convo_id: "0".into(),
|
remote_convo_id: "0".into(),
|
||||||
kind: ConversationKind::GroupV1,
|
kind: ConversationKind::GroupV1,
|
||||||
})?;
|
})?;
|
||||||
convo.add_member(&mut self.client_ctx, participants)?;
|
convo.add_member(participants)?;
|
||||||
|
|
||||||
Ok(Box::new(convo))
|
Ok(Box::new(convo))
|
||||||
}
|
}
|
||||||
@ -246,7 +264,7 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
|
|
||||||
// Dispatch encrypted payload to Inbox, and register the created Conversation
|
// Dispatch encrypted payload to Inbox, and register the created Conversation
|
||||||
fn dispatch_to_inbox2(&mut self, payload: &[u8]) -> Result<Option<ContentData>, ChatError> {
|
fn dispatch_to_inbox2(&mut self, payload: &[u8]) -> Result<Option<ContentData>, ChatError> {
|
||||||
self.pq_inbox.handle_frame(&mut self.client_ctx, payload)?;
|
self.pq_inbox.handle_frame(payload)?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -270,7 +288,7 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
pub fn get_convo(
|
pub fn get_convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
convo_id: ConversationId,
|
convo_id: ConversationId,
|
||||||
) -> Result<Box<dyn GroupConvo<DS, RS, CS>>, ChatError> {
|
) -> Result<Box<dyn GroupConvo<DS, RS>>, ChatError> {
|
||||||
self.load_group_convo(convo_id)
|
self.load_group_convo(convo_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,8 +310,7 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
Ok(Box::new(private_convo))
|
Ok(Box::new(private_convo))
|
||||||
}
|
}
|
||||||
ConversationKind::GroupV1 => Ok(Box::new(
|
ConversationKind::GroupV1 => Ok(Box::new(
|
||||||
self.pq_inbox
|
self.pq_inbox.load_mls_convo(record.local_convo_id)?,
|
||||||
.load_mls_convo(&mut self.client_ctx, record.local_convo_id)?,
|
|
||||||
)),
|
)),
|
||||||
ConversationKind::Unknown(_) => Err(ChatError::BadBundleValue(format!(
|
ConversationKind::Unknown(_) => Err(ChatError::BadBundleValue(format!(
|
||||||
"unsupported conversation type: {}",
|
"unsupported conversation type: {}",
|
||||||
@ -306,7 +323,7 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
fn load_group_convo(
|
fn load_group_convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
convo_id: ConversationId,
|
convo_id: ConversationId,
|
||||||
) -> Result<Box<dyn GroupConvo<DS, RS, CS>>, ChatError> {
|
) -> Result<Box<dyn GroupConvo<DS, RS>>, ChatError> {
|
||||||
let record = self
|
let record = self
|
||||||
.store
|
.store
|
||||||
.borrow()
|
.borrow()
|
||||||
@ -318,8 +335,7 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
Err(ChatError::NoConvo("This is not a group convo".into()))
|
Err(ChatError::NoConvo("This is not a group convo".into()))
|
||||||
}
|
}
|
||||||
ConversationKind::GroupV1 => Ok(Box::new(
|
ConversationKind::GroupV1 => Ok(Box::new(
|
||||||
self.pq_inbox
|
self.pq_inbox.load_mls_convo(record.local_convo_id)?,
|
||||||
.load_mls_convo(&mut self.client_ctx, record.local_convo_id)?,
|
|
||||||
)),
|
)),
|
||||||
ConversationKind::Unknown(_) => Err(ChatError::BadBundleValue(format!(
|
ConversationKind::Unknown(_) => Err(ChatError::BadBundleValue(format!(
|
||||||
"unsupported conversation type: {}",
|
"unsupported conversation type: {}",
|
||||||
|
|||||||
@ -2,14 +2,14 @@ pub mod group_v1;
|
|||||||
mod privatev1;
|
mod privatev1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DeliveryService, service_traits::KeyPackageProvider,
|
DeliveryService,
|
||||||
ctx::ClientCtx,
|
service_traits::KeyPackageProvider,
|
||||||
types::{AccountId, AddressedEncryptedPayload, ContentData},
|
types::{AccountId, AddressedEncryptedPayload, ContentData},
|
||||||
};
|
};
|
||||||
use chat_proto::logoschat::encryption::EncryptedPayload;
|
use chat_proto::logoschat::encryption::EncryptedPayload;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use storage::{ChatStore, ConversationKind, ConversationStore, RatchetStore};
|
use storage::{ConversationKind, ConversationStore, RatchetStore};
|
||||||
|
|
||||||
pub use crate::errors::ChatError;
|
pub use crate::errors::ChatError;
|
||||||
pub use group_v1::{GroupV1Convo, IdentityProvider};
|
pub use group_v1::{GroupV1Convo, IdentityProvider};
|
||||||
@ -42,27 +42,12 @@ pub trait Convo: Id + Debug {
|
|||||||
fn convo_type(&self) -> ConversationKind;
|
fn convo_type(&self) -> ConversationKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GroupConvo<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>: Convo {
|
pub trait GroupConvo<DS: DeliveryService, RS: KeyPackageProvider>: Convo {
|
||||||
fn add_member(
|
fn add_member(&mut self, members: &[&AccountId]) -> Result<(), ChatError>;
|
||||||
&mut self,
|
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
members: &[&AccountId],
|
|
||||||
) -> Result<(), ChatError>;
|
|
||||||
|
|
||||||
// Default implementation which dispatches envelopes to the DeliveryService
|
// This is intended to replace `send_message`. The trait change is that it automatically
|
||||||
fn send_content(
|
// sends the payload directly.
|
||||||
&mut self,
|
fn send_content(&mut self, content: &[u8]) -> Result<(), ChatError>;
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
content: &[u8],
|
|
||||||
) -> Result<(), ChatError> {
|
|
||||||
let payloads = self.send_message(content)?;
|
|
||||||
for payload in payloads {
|
|
||||||
ctx.ds()
|
|
||||||
.publish(payload.into_envelope(self.id().into()))
|
|
||||||
.map_err(|e| ChatError::Delivery(e.to_string()))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Conversation<S: ConversationStore + RatchetStore> {
|
pub enum Conversation<S: ConversationStore + RatchetStore> {
|
||||||
|
|||||||
@ -8,13 +8,13 @@ use openmls::prelude::tls_codec::Deserialize;
|
|||||||
use openmls::prelude::*;
|
use openmls::prelude::*;
|
||||||
use openmls_libcrux_crypto::Provider as LibcruxProvider;
|
use openmls_libcrux_crypto::Provider as LibcruxProvider;
|
||||||
use openmls_traits::signatures::Signer as OpenMlsSigner;
|
use openmls_traits::signatures::Signer as OpenMlsSigner;
|
||||||
use storage::{ChatStore, ConversationKind};
|
use storage::ConversationKind;
|
||||||
|
|
||||||
use crate::types::AccountId;
|
use crate::types::AccountId;
|
||||||
use crate::{
|
use crate::{
|
||||||
DeliveryService, service_traits::KeyPackageProvider,
|
DeliveryService,
|
||||||
conversation::{ChatError, ConversationId, Convo, GroupConvo, Id},
|
conversation::{ChatError, ConversationId, Convo, GroupConvo, Id},
|
||||||
ctx::ClientCtx,
|
service_traits::KeyPackageProvider,
|
||||||
types::{AddressedEncryptedPayload, ContentData},
|
types::{AddressedEncryptedPayload, ContentData},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,21 +37,28 @@ pub trait MlsContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invite_user<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
fn invite_user<DS: DeliveryService>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ds: &mut DS,
|
||||||
account_id: &AccountId,
|
account_id: &AccountId,
|
||||||
welcome: &MlsMessageOut,
|
welcome: &MlsMessageOut,
|
||||||
) -> Result<(), ChatError>;
|
) -> Result<(), ChatError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GroupV1Convo<Ctx: MlsContext> {
|
pub struct GroupV1Convo<MlsCtx, DS, KP> {
|
||||||
ctx: Rc<RefCell<Ctx>>,
|
ctx: Rc<RefCell<MlsCtx>>,
|
||||||
|
ds: Rc<RefCell<DS>>,
|
||||||
|
keypkg_provider: Rc<RefCell<KP>>,
|
||||||
pub(crate) mls_group: MlsGroup, // TODO: (!) Fix Visibility
|
pub(crate) mls_group: MlsGroup, // TODO: (!) Fix Visibility
|
||||||
convo_id: String,
|
convo_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsContext> std::fmt::Debug for GroupV1Convo<Ctx> {
|
impl<MlsCtx, DS, KP> std::fmt::Debug for GroupV1Convo<MlsCtx, DS, KP>
|
||||||
|
where
|
||||||
|
MlsCtx: MlsContext,
|
||||||
|
DS: DeliveryService,
|
||||||
|
KP: KeyPackageProvider,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("GroupV1Convo")
|
f.debug_struct("GroupV1Convo")
|
||||||
.field("name", &self.ctx.borrow().ident().friendly_name())
|
.field("name", &self.ctx.borrow().ident().friendly_name())
|
||||||
@ -61,8 +68,17 @@ impl<Ctx: MlsContext> std::fmt::Debug for GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
impl<MlsCtx, DS, KP> GroupV1Convo<MlsCtx, DS, KP>
|
||||||
pub fn new<DS: DeliveryService>(ctx: Rc<RefCell<Ctx>>, ds: &mut DS) -> Self {
|
where
|
||||||
|
MlsCtx: MlsContext,
|
||||||
|
DS: DeliveryService,
|
||||||
|
KP: KeyPackageProvider,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
ctx: Rc<RefCell<MlsCtx>>,
|
||||||
|
ds: Rc<RefCell<DS>>,
|
||||||
|
keypkg_provider: Rc<RefCell<KP>>,
|
||||||
|
) -> Self {
|
||||||
let config = Self::mls_create_config();
|
let config = Self::mls_create_config();
|
||||||
let mls_group = {
|
let mls_group = {
|
||||||
let ctx_ref = ctx.borrow();
|
let ctx_ref = ctx.borrow();
|
||||||
@ -75,7 +91,7 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let convo_id = hex::encode(mls_group.group_id().as_slice());
|
let convo_id = hex::encode(mls_group.group_id().as_slice());
|
||||||
Self::subscribe(ds, &convo_id);
|
Self::subscribe(&mut ds.borrow_mut(), &convo_id);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"@ Create Convo: {}. {}. d:{} dc:{}",
|
"@ Create Convo: {}. {}. d:{} dc:{}",
|
||||||
@ -86,14 +102,17 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
|
ds,
|
||||||
|
keypkg_provider,
|
||||||
mls_group,
|
mls_group,
|
||||||
convo_id,
|
convo_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_welcome<DS: DeliveryService>(
|
pub fn new_from_welcome(
|
||||||
ctx: Rc<RefCell<Ctx>>,
|
ctx: Rc<RefCell<MlsCtx>>,
|
||||||
ds: &mut DS,
|
ds: Rc<RefCell<DS>>,
|
||||||
|
keypkg_provider: Rc<RefCell<KP>>,
|
||||||
welcome: Welcome,
|
welcome: Welcome,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mls_group = {
|
let mls_group = {
|
||||||
@ -109,7 +128,7 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let convo_id = hex::encode(mls_group.group_id().as_slice());
|
let convo_id = hex::encode(mls_group.group_id().as_slice());
|
||||||
Self::subscribe(ds, &convo_id);
|
Self::subscribe(&mut *ds.borrow_mut(), &convo_id);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"@ Welcome Convo: I:{}. {}. d:{} dc:{}",
|
"@ Welcome Convo: I:{}. {}. d:{} dc:{}",
|
||||||
@ -121,14 +140,17 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
|
|
||||||
GroupV1Convo {
|
GroupV1Convo {
|
||||||
ctx,
|
ctx,
|
||||||
|
ds,
|
||||||
|
keypkg_provider,
|
||||||
mls_group,
|
mls_group,
|
||||||
convo_id,
|
convo_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load<DS: DeliveryService>(
|
pub fn load(
|
||||||
ctx: Rc<RefCell<Ctx>>,
|
ctx: Rc<RefCell<MlsCtx>>,
|
||||||
ds: &mut DS,
|
ds: Rc<RefCell<DS>>,
|
||||||
|
keypkg_provider: Rc<RefCell<KP>>,
|
||||||
convo_id: String,
|
convo_id: String,
|
||||||
group_id: GroupId,
|
group_id: GroupId,
|
||||||
) -> Result<Self, ChatError> {
|
) -> Result<Self, ChatError> {
|
||||||
@ -138,16 +160,18 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
return Err(ChatError::NoConvo("mls group not found".into()));
|
return Err(ChatError::NoConvo("mls group not found".into()));
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::subscribe(ds, &convo_id)?;
|
Self::subscribe(&mut *ds.borrow_mut(), &convo_id)?;
|
||||||
|
|
||||||
Ok(GroupV1Convo {
|
Ok(GroupV1Convo {
|
||||||
ctx,
|
ctx,
|
||||||
|
ds,
|
||||||
|
keypkg_provider,
|
||||||
mls_group,
|
mls_group,
|
||||||
convo_id,
|
convo_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe<DS: DeliveryService>(ds: &mut DS, convo_id: &str) -> Result<(), ChatError> {
|
fn subscribe(ds: &mut DS, convo_id: &str) -> Result<(), ChatError> {
|
||||||
ds.subscribe(&Self::delivery_address_from_id(&convo_id))
|
ds.subscribe(&Self::delivery_address_from_id(&convo_id))
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
ds.subscribe(&Self::ctrl_delivery_address_from_id(&convo_id))
|
ds.subscribe(&Self::ctrl_delivery_address_from_id(&convo_id))
|
||||||
@ -191,15 +215,12 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
Self::ctrl_delivery_address_from_id(&self.convo_id)
|
Self::ctrl_delivery_address_from_id(&self.convo_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_package_for_account<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
fn key_package_for_account(&self, ident: &AccountId) -> Result<KeyPackage, ChatError> {
|
||||||
&self,
|
let retrieved_bytes = self
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
.keypkg_provider
|
||||||
ident: &AccountId,
|
.borrow()
|
||||||
) -> Result<KeyPackage, ChatError> {
|
|
||||||
let retrieved_bytes = ctx
|
|
||||||
.contact_registry()
|
|
||||||
.retrieve(ident)
|
.retrieve(ident)
|
||||||
.map_err(|e| ChatError::Generic(e.to_string()))?;
|
.map_err(|e: KP::Error| ChatError::Generic(e.to_string()))?;
|
||||||
|
|
||||||
// dbg!(ctx.contact_registry());
|
// dbg!(ctx.contact_registry());
|
||||||
let Some(keypkg_bytes) = retrieved_bytes else {
|
let Some(keypkg_bytes) = retrieved_bytes else {
|
||||||
@ -215,13 +236,23 @@ impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsContext> Id for GroupV1Convo<Ctx> {
|
impl<MlsCtx, DS, KP> Id for GroupV1Convo<MlsCtx, DS, KP>
|
||||||
|
where
|
||||||
|
MlsCtx: MlsContext,
|
||||||
|
DS: DeliveryService,
|
||||||
|
KP: KeyPackageProvider,
|
||||||
|
{
|
||||||
fn id(&self) -> ConversationId<'_> {
|
fn id(&self) -> ConversationId<'_> {
|
||||||
&self.convo_id
|
&self.convo_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsContext> Convo for GroupV1Convo<Ctx> {
|
impl<MlsCtx, DS, KP> Convo for GroupV1Convo<MlsCtx, DS, KP>
|
||||||
|
where
|
||||||
|
MlsCtx: MlsContext,
|
||||||
|
DS: DeliveryService,
|
||||||
|
KP: KeyPackageProvider,
|
||||||
|
{
|
||||||
fn send_message(
|
fn send_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
content: &[u8],
|
content: &[u8],
|
||||||
@ -314,14 +345,13 @@ impl<Ctx: MlsContext> Convo for GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsContext, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>
|
impl<MlsCtx, DS, KP> GroupConvo<DS, KP> for GroupV1Convo<MlsCtx, DS, KP>
|
||||||
GroupConvo<DS, RS, CS> for GroupV1Convo<Ctx>
|
where
|
||||||
|
MlsCtx: MlsContext,
|
||||||
|
DS: DeliveryService,
|
||||||
|
KP: KeyPackageProvider,
|
||||||
{
|
{
|
||||||
fn add_member(
|
fn add_member(&mut self, members: &[&AccountId]) -> Result<(), ChatError> {
|
||||||
&mut self,
|
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
members: &[&AccountId],
|
|
||||||
) -> Result<(), ChatError> {
|
|
||||||
// add_members returns:
|
// add_members returns:
|
||||||
// commit — the Commit message Alice broadcasts to all members
|
// commit — the Commit message Alice broadcasts to all members
|
||||||
// welcome — the Welcome message sent privately to each new joiner
|
// welcome — the Welcome message sent privately to each new joiner
|
||||||
@ -341,7 +371,7 @@ impl<Ctx: MlsContext, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore
|
|||||||
let keypkgs = members
|
let keypkgs = members
|
||||||
.iter()
|
.iter()
|
||||||
// .map(|ident| self.key_package_for_account(ctx, ident))
|
// .map(|ident| self.key_package_for_account(ctx, ident))
|
||||||
.map(|ident| self.key_package_for_account(ctx, ident))
|
.map(|ident| self.key_package_for_account(ident))
|
||||||
.collect::<Result<Vec<_>, ChatError>>()?;
|
.collect::<Result<Vec<_>, ChatError>>()?;
|
||||||
|
|
||||||
let (commit, welcome, _group_info) = self
|
let (commit, welcome, _group_info) = self
|
||||||
@ -353,7 +383,7 @@ impl<Ctx: MlsContext, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore
|
|||||||
|
|
||||||
// TODO: (P3) Evaluate privacy/performance implications of an aggregated Welcome for multiple users
|
// TODO: (P3) Evaluate privacy/performance implications of an aggregated Welcome for multiple users
|
||||||
for account_id in members {
|
for account_id in members {
|
||||||
ctx_ref.invite_user(ctx, account_id, &welcome)?;
|
ctx_ref.invite_user(&mut *self.ds.borrow_mut(), account_id, &welcome)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let encrypted_payload = EncryptedPayload {
|
let encrypted_payload = EncryptedPayload {
|
||||||
@ -370,8 +400,20 @@ impl<Ctx: MlsContext, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore
|
|||||||
// TODO: (P1) Make GroupConvos agnostic to framing so its less error prone and more
|
// TODO: (P1) Make GroupConvos agnostic to framing so its less error prone and more
|
||||||
let env = addr_enc_payload.into_envelope(self.convo_id.clone());
|
let env = addr_enc_payload.into_envelope(self.convo_id.clone());
|
||||||
|
|
||||||
ctx.ds()
|
self.ds
|
||||||
|
.borrow_mut()
|
||||||
.publish(env)
|
.publish(env)
|
||||||
.map_err(|e| ChatError::Generic(format!("Publish: {e}")))
|
.map_err(|e| ChatError::Generic(format!("Publish: {e}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_content(&mut self, content: &[u8]) -> Result<(), ChatError> {
|
||||||
|
let payloads = self.send_message(content)?;
|
||||||
|
for payload in payloads {
|
||||||
|
self.ds
|
||||||
|
.borrow_mut()
|
||||||
|
.publish(payload.into_envelope(self.id().into()))
|
||||||
|
.map_err(|e| ChatError::Delivery(e.to_string()))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cell::{RefCell, RefMut},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use storage::ChatStore;
|
|
||||||
|
|
||||||
use crate::{DeliveryService, RegistrationService};
|
|
||||||
use crate::service_traits::KeyPackageProvider;
|
|
||||||
|
|
||||||
pub struct ClientCtx<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore> {
|
|
||||||
ds: DS,
|
|
||||||
contact_registry: RS,
|
|
||||||
convo_store: Rc<RefCell<CS>>, // TODO: (P2) Remove Rc/Refcell
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore> ClientCtx<DS, RS, CS> {
|
|
||||||
pub fn new(ds: DS, contact_registry: RS, convo_store: Rc<RefCell<CS>>) -> Self {
|
|
||||||
Self {
|
|
||||||
ds,
|
|
||||||
contact_registry,
|
|
||||||
convo_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ds(&'a mut self) -> &'a mut DS {
|
|
||||||
&mut self.ds
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contact_registry(&'a self) -> &'a RS {
|
|
||||||
&self.contact_registry
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn store(&'a self) -> RefMut<'a, CS> {
|
|
||||||
self.convo_store.borrow_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, DS: DeliveryService, RS: RegistrationService, CS: ChatStore> ClientCtx<DS, RS, CS> {
|
|
||||||
pub fn contact_registry_mut(&'a mut self) -> &'a mut RS {
|
|
||||||
&mut self.contact_registry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,15 +12,13 @@ use storage::ConversationMeta;
|
|||||||
use crate::AddressedEnvelope;
|
use crate::AddressedEnvelope;
|
||||||
use crate::ChatError;
|
use crate::ChatError;
|
||||||
use crate::DeliveryService;
|
use crate::DeliveryService;
|
||||||
|
use crate::RegistrationService;
|
||||||
use crate::account::LogosAccount;
|
use crate::account::LogosAccount;
|
||||||
use crate::conversation::GroupConvo;
|
use crate::conversation::GroupConvo;
|
||||||
use crate::conversation::group_v1::MlsContext;
|
use crate::conversation::group_v1::MlsContext;
|
||||||
use crate::conversation::{GroupV1Convo, IdentityProvider};
|
use crate::conversation::{GroupV1Convo, IdentityProvider};
|
||||||
use crate::ctx::ClientCtx;
|
|
||||||
use crate::types::AccountId;
|
use crate::types::AccountId;
|
||||||
use crate::utils::{blake2b_hex, hash_size};
|
use crate::utils::{blake2b_hex, hash_size};
|
||||||
use crate::RegistrationService;
|
|
||||||
use crate::service_traits::KeyPackageProvider;
|
|
||||||
pub struct PqMlsContext {
|
pub struct PqMlsContext {
|
||||||
ident_provider: LogosAccount,
|
ident_provider: LogosAccount,
|
||||||
provider: LibcruxProvider,
|
provider: LibcruxProvider,
|
||||||
@ -37,9 +35,9 @@ impl MlsContext for PqMlsContext {
|
|||||||
&self.provider
|
&self.provider
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invite_user<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
fn invite_user<DS: DeliveryService>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ds: &mut DS,
|
||||||
account_id: &AccountId,
|
account_id: &AccountId,
|
||||||
welcome: &MlsMessageOut,
|
welcome: &MlsMessageOut,
|
||||||
) -> Result<(), ChatError> {
|
) -> Result<(), ChatError> {
|
||||||
@ -62,7 +60,7 @@ impl MlsContext for PqMlsContext {
|
|||||||
data: envelope.encode_to_vec(),
|
data: envelope.encode_to_vec(),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.ds().publish(outbound_msg).map_err(ChatError::generic)?;
|
ds.publish(outbound_msg).map_err(ChatError::generic)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,17 +79,33 @@ impl InboxProtocolParams {
|
|||||||
|
|
||||||
type ProtocolParams = InboxProtocolParams;
|
type ProtocolParams = InboxProtocolParams;
|
||||||
|
|
||||||
pub struct InboxV2 {
|
pub struct InboxV2<DS, RS, CS> {
|
||||||
account_id: AccountId,
|
account_id: AccountId,
|
||||||
|
ds: Rc<RefCell<DS>>,
|
||||||
|
reg_service: Rc<RefCell<RS>>,
|
||||||
|
store: Rc<RefCell<CS>>,
|
||||||
ctx: Rc<RefCell<PqMlsContext>>,
|
ctx: Rc<RefCell<PqMlsContext>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InboxV2 {
|
impl<'a, DS, CS, RS> InboxV2<DS, RS, CS>
|
||||||
pub fn new_with_account(account: LogosAccount) -> Self {
|
where
|
||||||
|
DS: DeliveryService,
|
||||||
|
RS: RegistrationService,
|
||||||
|
CS: ChatStore,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
account: LogosAccount,
|
||||||
|
ds: Rc<RefCell<DS>>,
|
||||||
|
reg_service: Rc<RefCell<RS>>,
|
||||||
|
store: Rc<RefCell<CS>>,
|
||||||
|
) -> Self {
|
||||||
let account_id = account.account_id().clone();
|
let account_id = account.account_id().clone();
|
||||||
let provider = LibcruxProvider::new().unwrap();
|
let provider = LibcruxProvider::new().unwrap();
|
||||||
Self {
|
Self {
|
||||||
account_id,
|
account_id,
|
||||||
|
ds,
|
||||||
|
reg_service,
|
||||||
|
store,
|
||||||
ctx: Rc::new(RefCell::new(PqMlsContext {
|
ctx: Rc::new(RefCell::new(PqMlsContext {
|
||||||
ident_provider: account,
|
ident_provider: account,
|
||||||
provider,
|
provider,
|
||||||
@ -103,18 +117,19 @@ impl<'a> InboxV2 {
|
|||||||
&self.account_id
|
&self.account_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
/// Submit MlsKeypackage to registration service
|
||||||
&mut self,
|
pub fn register(&mut self) -> Result<(), ChatError> {
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
let keypackage_bytes = self.create_keypackage()?.tls_serialize_detached()?;
|
||||||
) -> Result<(), ChatError> {
|
|
||||||
let keypackage = self.create_keypackage()?;
|
|
||||||
|
|
||||||
let bytes = keypackage.tls_serialize_detached()?;
|
// TODO: (P3) Each keypackage can only be used once either enable...
|
||||||
|
// "LastResort" package or publish multiple
|
||||||
ctx.contact_registry_mut()
|
self.reg_service
|
||||||
.register(&self.ctx.borrow().ident_provider.friendly_name(), bytes)
|
.borrow_mut()
|
||||||
.map_err(ChatError::generic)?; //TODO: (P1) create an address scheme instead of using names
|
.register(
|
||||||
Ok(())
|
&self.ctx.borrow().ident_provider.friendly_name(),
|
||||||
|
keypackage_bytes,
|
||||||
|
)
|
||||||
|
.map_err(ChatError::generic)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delivery_address(&self) -> String {
|
pub fn delivery_address(&self) -> String {
|
||||||
@ -125,19 +140,12 @@ impl<'a> InboxV2 {
|
|||||||
ProtocolParams::conversation_id_for_account_id(&self.account_id)
|
ProtocolParams::conversation_id_for_account_id(&self.account_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_group_v1<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
pub fn create_group_v1(&self) -> Result<GroupV1Convo<PqMlsContext, DS, RS>, ChatError> {
|
||||||
&self,
|
let convo = GroupV1Convo::new(self.ctx.clone(), self.ds.clone(), self.reg_service.clone());
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
) -> Result<GroupV1Convo<PqMlsContext>, ChatError> {
|
|
||||||
let convo = GroupV1Convo::new(self.assemble_ctx(), ctx.ds());
|
|
||||||
Ok(convo)
|
Ok(convo)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_frame<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
pub fn handle_frame(&self, payload_bytes: &[u8]) -> Result<(), ChatError> {
|
||||||
&self,
|
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
payload_bytes: &[u8],
|
|
||||||
) -> Result<(), ChatError> {
|
|
||||||
let inbox_frame = InboxV2Frame::decode(payload_bytes)?;
|
let inbox_frame = InboxV2Frame::decode(payload_bytes)?;
|
||||||
|
|
||||||
let Some(payload) = inbox_frame.payload else {
|
let Some(payload) = inbox_frame.payload else {
|
||||||
@ -146,20 +154,12 @@ impl<'a> InboxV2 {
|
|||||||
|
|
||||||
match payload {
|
match payload {
|
||||||
InviteType::GroupV1(group_v1_heavy_invite) => {
|
InviteType::GroupV1(group_v1_heavy_invite) => {
|
||||||
self.handle_heavy_invite(ctx, group_v1_heavy_invite)
|
self.handle_heavy_invite(group_v1_heavy_invite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_ctx(&self) -> Rc<RefCell<PqMlsContext>> {
|
fn persist_convo(&self, convo: impl GroupConvo<DS, RS>) -> Result<(), ChatError> {
|
||||||
self.ctx.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn persist_convo<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
|
||||||
&self,
|
|
||||||
ctx: &'a ClientCtx<DS, RS, CS>,
|
|
||||||
convo: impl GroupConvo<DS, RS, CS>,
|
|
||||||
) -> Result<(), ChatError> {
|
|
||||||
// TODO: (P2) Remove remote_convo_id this is an implementation detail specific to PrivateV1
|
// TODO: (P2) Remove remote_convo_id this is an implementation detail specific to PrivateV1
|
||||||
// TODO: (P3) Implement From<Convo> for ConversationMeta
|
// TODO: (P3) Implement From<Convo> for ConversationMeta
|
||||||
let meta = ConversationMeta {
|
let meta = ConversationMeta {
|
||||||
@ -167,16 +167,12 @@ impl<'a> InboxV2 {
|
|||||||
remote_convo_id: "0".into(),
|
remote_convo_id: "0".into(),
|
||||||
kind: storage::ConversationKind::GroupV1,
|
kind: storage::ConversationKind::GroupV1,
|
||||||
};
|
};
|
||||||
ctx.store().save_conversation(&meta)?;
|
self.store.borrow_mut().save_conversation(&meta)?;
|
||||||
// TODO: (P1) Persist state
|
// TODO: (P1) Persist state
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_heavy_invite<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
fn handle_heavy_invite(&self, invite: GroupV1HeavyInvite) -> Result<(), ChatError> {
|
||||||
&self,
|
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
invite: GroupV1HeavyInvite,
|
|
||||||
) -> Result<(), ChatError> {
|
|
||||||
let (msg_in, _rest) = MlsMessageIn::tls_deserialize_bytes(invite.welcome_bytes.as_slice())?;
|
let (msg_in, _rest) = MlsMessageIn::tls_deserialize_bytes(invite.welcome_bytes.as_slice())?;
|
||||||
|
|
||||||
let MlsMessageBodyIn::Welcome(welcome) = msg_in.extract() else {
|
let MlsMessageBodyIn::Welcome(welcome) = msg_in.extract() else {
|
||||||
@ -186,8 +182,13 @@ impl<'a> InboxV2 {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let convo = GroupV1Convo::new_from_welcome(self.assemble_ctx(), ctx.ds(), welcome);
|
let convo = GroupV1Convo::new_from_welcome(
|
||||||
self.persist_convo(ctx, convo)
|
self.ctx.clone(),
|
||||||
|
self.ds.clone(),
|
||||||
|
self.reg_service.clone(),
|
||||||
|
welcome,
|
||||||
|
);
|
||||||
|
self.persist_convo(convo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_keypackage(&self) -> Result<KeyPackage, ChatError> {
|
fn create_keypackage(&self) -> Result<KeyPackage, ChatError> {
|
||||||
@ -211,14 +212,19 @@ impl<'a> InboxV2 {
|
|||||||
Ok(a.key_package().clone())
|
Ok(a.key_package().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_mls_convo<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
pub fn load_mls_convo(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
convo_id: String,
|
convo_id: String,
|
||||||
) -> Result<GroupV1Convo<PqMlsContext>, ChatError> {
|
) -> Result<GroupV1Convo<PqMlsContext, DS, RS>, ChatError> {
|
||||||
let group_id_bytes = hex::decode(&convo_id).map_err(ChatError::generic)?;
|
let group_id_bytes = hex::decode(&convo_id).map_err(ChatError::generic)?;
|
||||||
let group_id = GroupId::from_slice(&group_id_bytes);
|
let group_id = GroupId::from_slice(&group_id_bytes);
|
||||||
let convo = GroupV1Convo::load(self.assemble_ctx(), ctx.ds(), convo_id, group_id)?;
|
let convo = GroupV1Convo::load(
|
||||||
|
self.ctx.clone(),
|
||||||
|
self.ds.clone(),
|
||||||
|
self.reg_service.clone(),
|
||||||
|
convo_id,
|
||||||
|
group_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(convo)
|
Ok(convo)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,11 @@ mod account;
|
|||||||
mod context;
|
mod context;
|
||||||
mod conversation;
|
mod conversation;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
mod ctx;
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod service_traits;
|
|
||||||
mod inbox;
|
mod inbox;
|
||||||
mod inbox_v2;
|
mod inbox_v2;
|
||||||
mod proto;
|
mod proto;
|
||||||
|
mod service_traits;
|
||||||
mod types;
|
mod types;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use storage::{ConversationMeta, ConversationStore, IdentityStore};
|
|||||||
use storage::{EphemeralKeyStore, RatchetStore};
|
use storage::{EphemeralKeyStore, RatchetStore};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AccountId, AddressedEnvelope, DeliveryService, RegistrationService, KeyPackageProvider,
|
AccountId, AddressedEnvelope, DeliveryService, RegistrationService,
|
||||||
utils::{blake2b_hex, hash_size::Testing},
|
utils::{blake2b_hex, hash_size::Testing},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,18 +159,23 @@ impl Debug for EphemeralRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyPackageProvider for EphemeralRegistry {
|
impl RegistrationService for EphemeralRegistry {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
fn register(&mut self, identity: &str, key_bundle: Vec<u8>) -> Result<(), Self::Error> {
|
||||||
|
self.registry
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(identity.to_string(), key_bundle);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error> {
|
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||||
Ok(self.registry.lock().unwrap().get(identity.as_str()).cloned())
|
Ok(self
|
||||||
}
|
.registry
|
||||||
}
|
.lock()
|
||||||
|
.unwrap()
|
||||||
impl RegistrationService for EphemeralRegistry {
|
.get(identity.as_str())
|
||||||
fn register(&mut self, identity: &str, key_bundle: Vec<u8>) -> Result<(), Self::Error> {
|
.cloned())
|
||||||
self.registry.lock().unwrap().insert(identity.to_string(), key_bundle);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,13 +297,3 @@ impl RatchetStore for MemStore {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl GroupMlsStorageV1 for MemStore {
|
|
||||||
// fn save_state(&self, convo_id: &str, state: &[u8]) {
|
|
||||||
// self.state.insert(convo_id, state)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn load_state(&self, convo_id: &str) -> Vec<u8> {
|
|
||||||
// self.state.get(convo_id).unwrap().clone()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@ -33,13 +33,12 @@ pub mod hash_size {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
use blake2::digest::consts::{U4, U8, U18};
|
use blake2::digest::consts::{U4, U8};
|
||||||
hash_sizes! {
|
hash_sizes! {
|
||||||
/// Generic hash size for tests and debug
|
/// Generic hash size for tests and debug
|
||||||
Testing => U4,
|
Testing => U4,
|
||||||
/// Account ID hash length
|
/// Account ID hash length
|
||||||
AccountId => U8,
|
AccountId => U8,
|
||||||
ConversationId => U18,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,7 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
||||||
use libchat::{ChatStorage, ContentData, Context, ConversationId, GroupConvo, hex_trunc};
|
use libchat::{ContentData, Context, GroupConvo, hex_trunc};
|
||||||
|
|
||||||
type TestContext = Context<LocalBroadcaster, EphemeralRegistry, ChatStorage>;
|
|
||||||
|
|
||||||
fn send_and_verify(
|
|
||||||
sender: &mut TestContext,
|
|
||||||
receiver: &mut TestContext,
|
|
||||||
convo_id: ConversationId,
|
|
||||||
content: &[u8],
|
|
||||||
) {
|
|
||||||
let payloads = sender.send_content(convo_id, content).unwrap();
|
|
||||||
let payload = payloads.first().unwrap();
|
|
||||||
let received = receiver
|
|
||||||
.handle_payload(&payload.data)
|
|
||||||
.unwrap()
|
|
||||||
.expect("expected content");
|
|
||||||
assert_eq!(content, received.data.as_slice());
|
|
||||||
assert!(!received.is_new_convo); // Check that `is_new_convo` is FALSE
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple client Functionality for testing
|
// Simple client Functionality for testing
|
||||||
struct Client {
|
struct Client {
|
||||||
@ -39,12 +21,16 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_messages(&mut self) {
|
fn process_messages(&mut self) {
|
||||||
while let Some(data) = self.client_ctx().ds().poll() {
|
let messages: Vec<_> = {
|
||||||
|
let mut ds = self.ds();
|
||||||
|
std::iter::from_fn(|| ds.poll()).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
for data in messages {
|
||||||
let res = self.handle_payload(&data).unwrap();
|
let res = self.handle_payload(&data).unwrap();
|
||||||
if let Some(cb) = &self.on_content {
|
if let Some(cb) = &self.on_content {
|
||||||
match res {
|
if let Some(content_data) = res {
|
||||||
Some(content_data) => cb(content_data),
|
cb(content_data);
|
||||||
None => continue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +39,7 @@ impl Client {
|
|||||||
fn convo(
|
fn convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
convo_id: &str,
|
convo_id: &str,
|
||||||
) -> Box<dyn GroupConvo<LocalBroadcaster, EphemeralRegistry, MemStore>> {
|
) -> Box<dyn GroupConvo<LocalBroadcaster, EphemeralRegistry>> {
|
||||||
// TODO: (P1) Convos are being copied somewhere, which means hanging on to a reference causes state desync
|
// TODO: (P1) Convos are being copied somewhere, which means hanging on to a reference causes state desync
|
||||||
self.get_convo(convo_id).unwrap()
|
self.get_convo(convo_id).unwrap()
|
||||||
}
|
}
|
||||||
@ -117,24 +103,16 @@ fn create_group() {
|
|||||||
|
|
||||||
clients[SARO]
|
clients[SARO]
|
||||||
.convo(convo_id)
|
.convo(convo_id)
|
||||||
.send_content(
|
.send_content(b"ok who broke the group chat again")
|
||||||
&mut clients[SARO].client_ctx(),
|
|
||||||
b"ok who broke the group chat again",
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// clients[SARO].process_messages();
|
|
||||||
process(&mut clients);
|
process(&mut clients);
|
||||||
|
|
||||||
clients[RAYA]
|
clients[RAYA]
|
||||||
.convo(convo_id)
|
.convo(convo_id)
|
||||||
.send_content(
|
.send_content(b"it was literally working five minutes ago")
|
||||||
&mut clients[RAYA].client_ctx(),
|
|
||||||
b"it was literally working five minutes ago",
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// clients[SARO].process_messages();
|
|
||||||
process(&mut clients);
|
process(&mut clients);
|
||||||
|
|
||||||
let pax_ctx = Context::new_with_name("pax", ds, rs, MemStore::new()).unwrap();
|
let pax_ctx = Context::new_with_name("pax", ds, rs, MemStore::new()).unwrap();
|
||||||
@ -144,32 +122,22 @@ fn create_group() {
|
|||||||
let pax_id = clients[PAX].account_id().clone();
|
let pax_id = clients[PAX].account_id().clone();
|
||||||
clients[SARO]
|
clients[SARO]
|
||||||
.convo(convo_id)
|
.convo(convo_id)
|
||||||
.add_member(&mut clients[SARO].client_ctx(), &[&pax_id])
|
.add_member(&[&pax_id])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// clients[SARO].process_messages();
|
|
||||||
process(&mut clients);
|
process(&mut clients);
|
||||||
|
|
||||||
clients[PAX]
|
clients[PAX]
|
||||||
.convo(convo_id)
|
.convo(convo_id)
|
||||||
.send_content(
|
.send_content(b"ngl the key rotation is cooked")
|
||||||
&mut clients[PAX].client_ctx(),
|
|
||||||
b"ngl the key rotation is cooked",
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// clients[SARO].process_messages();
|
|
||||||
|
|
||||||
process(&mut clients);
|
process(&mut clients);
|
||||||
|
|
||||||
clients[SARO]
|
clients[SARO]
|
||||||
.convo(convo_id)
|
.convo(convo_id)
|
||||||
.send_content(
|
.send_content(b"bro we literally just added you to the group ")
|
||||||
&mut clients[SARO].client_ctx(),
|
|
||||||
b"bro we literally just added you to the group ",
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
process(&mut clients);
|
process(&mut clients);
|
||||||
// process(&mut clients);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use libchat::{Context, Introduction};
|
||||||
|
|
||||||
use libchat::{AddressedEnvelope, Context, Introduction};
|
|
||||||
use sqlite::{ChatStorage, StorageConfig};
|
use sqlite::{ChatStorage, StorageConfig};
|
||||||
use storage::{ConversationStore, IdentityStore};
|
use storage::{ConversationStore, IdentityStore};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
use components::{EphemeralRegistry, LocalBroadcaster};
|
||||||
|
|
||||||
fn send_and_verify(
|
fn send_and_verify(
|
||||||
sender: &mut Context<LocalBroadcaster, EphemeralRegistry, ChatStorage>,
|
sender: &mut Context<LocalBroadcaster, EphemeralRegistry, ChatStorage>,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user