mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-06-28 20:19:26 +00:00
Update Context to accept External Identity Provider. (#127)
* rename .account_id() to .id() * Create logos-traits crate * Remove AccountId references * external IdentityProvider for Context * Fix compile errors from merge * Update logos-traits to shared-traits * format fixes * warnings cleanup * clippy fix * Remove rebase artifact
This commit is contained in:
parent
0e72fdf483
commit
a610117e81
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1660,6 +1660,7 @@ dependencies = [
|
|||||||
"chat-sqlite",
|
"chat-sqlite",
|
||||||
"components",
|
"components",
|
||||||
"libchat",
|
"libchat",
|
||||||
|
"logos-account",
|
||||||
"storage",
|
"storage",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
@ -1787,6 +1788,7 @@ dependencies = [
|
|||||||
"prost",
|
"prost",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"safer-ffi",
|
"safer-ffi",
|
||||||
|
"shared-traits",
|
||||||
"storage",
|
"storage",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -2113,6 +2115,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto",
|
"crypto",
|
||||||
"libchat",
|
"libchat",
|
||||||
|
"shared-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2122,6 +2125,7 @@ dependencies = [
|
|||||||
"chat-sqlite",
|
"chat-sqlite",
|
||||||
"components",
|
"components",
|
||||||
"libchat",
|
"libchat",
|
||||||
|
"logos-account",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
@ -3381,6 +3385,13 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared-traits"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|||||||
@ -10,6 +10,7 @@ members = [
|
|||||||
"core/crypto",
|
"core/crypto",
|
||||||
"core/double-ratchets",
|
"core/double-ratchets",
|
||||||
"core/integration_tests_core",
|
"core/integration_tests_core",
|
||||||
|
"core/shared-traits",
|
||||||
"core/sqlite",
|
"core/sqlite",
|
||||||
"core/storage",
|
"core/storage",
|
||||||
"crates/client-ffi",
|
"crates/client-ffi",
|
||||||
@ -23,6 +24,7 @@ default-members = [
|
|||||||
"core/crypto",
|
"core/crypto",
|
||||||
"core/double-ratchets",
|
"core/double-ratchets",
|
||||||
"core/integration_tests_core",
|
"core/integration_tests_core",
|
||||||
|
"core/shared-traits",
|
||||||
"core/sqlite",
|
"core/sqlite",
|
||||||
"core/storage",
|
"core/storage",
|
||||||
"crates/client-ffi",
|
"crates/client-ffi",
|
||||||
@ -31,11 +33,13 @@ default-members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
# Internal Workspace dependency declarations (sorted)
|
# Internal Workspace dependency declarations (sorted)
|
||||||
|
logos-account = { path = "core/account" }
|
||||||
chat-sqlite = { path = "core/sqlite" }
|
chat-sqlite = { path = "core/sqlite" }
|
||||||
components = { path = "extensions/components" }
|
components = { path = "extensions/components" }
|
||||||
crypto = { path = "core/crypto" }
|
crypto = { path = "core/crypto" }
|
||||||
libchat = { path = "core/conversations" }
|
libchat = { path = "core/conversations" }
|
||||||
logos-chat = { path = "crates/client" }
|
logos-chat = { path = "crates/client" }
|
||||||
|
shared-traits = { path = "core/shared-traits" }
|
||||||
storage = { path = "core/storage" }
|
storage = { path = "core/storage" }
|
||||||
|
|
||||||
# External Workspace dependency declarations (sorted)
|
# External Workspace dependency declarations (sorted)
|
||||||
|
|||||||
@ -10,5 +10,6 @@ dev = []
|
|||||||
# Workspace dependencies (sorted)
|
# Workspace dependencies (sorted)
|
||||||
crypto = { workspace = true }
|
crypto = { workspace = true }
|
||||||
libchat = { workspace = true }
|
libchat = { workspace = true }
|
||||||
|
shared-traits = { workspace = true }
|
||||||
|
|
||||||
# External dependencies (sorted)
|
# External dependencies (sorted)
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
|
use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
|
||||||
|
use shared_traits::{IdentId, IdentIdRef};
|
||||||
|
|
||||||
use libchat::{AccountId, IdentityProvider};
|
use libchat::IdentityProvider;
|
||||||
|
|
||||||
/// A Test Focused LogosAccount using a pre-defined identifier.
|
/// A Test Focused LogosAccount using a pre-defined identifier.
|
||||||
/// The test account is not persisted, and uses a single user provided id.
|
/// The test account is not persisted, and uses a single user provided id.
|
||||||
/// This account type should not be used in a production system.
|
/// This account type should not be used in a production system.
|
||||||
pub struct TestLogosAccount {
|
pub struct TestLogosAccount {
|
||||||
id: AccountId,
|
id: IdentId,
|
||||||
signing_key: Ed25519SigningKey,
|
signing_key: Ed25519SigningKey,
|
||||||
verifying_key: Ed25519VerifyingKey,
|
verifying_key: Ed25519VerifyingKey,
|
||||||
}
|
}
|
||||||
@ -16,7 +17,7 @@ impl TestLogosAccount {
|
|||||||
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: IdentId::new(explicit_id.into()),
|
||||||
signing_key,
|
signing_key,
|
||||||
verifying_key,
|
verifying_key,
|
||||||
}
|
}
|
||||||
@ -24,7 +25,7 @@ impl TestLogosAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IdentityProvider for TestLogosAccount {
|
impl IdentityProvider for TestLogosAccount {
|
||||||
fn account_id(&self) -> &AccountId {
|
fn id(&self) -> IdentIdRef<'_> {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ crate-type = ["rlib","staticlib"]
|
|||||||
blake2 = { workspace = true }
|
blake2 = { workspace = true }
|
||||||
chat-sqlite = { workspace = true }
|
chat-sqlite = { workspace = true }
|
||||||
crypto = { workspace = true }
|
crypto = { workspace = true }
|
||||||
|
shared-traits = { workspace = true }
|
||||||
storage = { workspace = true }
|
storage = { workspace = true }
|
||||||
|
|
||||||
# External dependencies (sorted)
|
# External dependencies (sorted)
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
use crypto::{Ed25519Signature, Ed25519SigningKey, Ed25519VerifyingKey};
|
|
||||||
use openmls::prelude::SignatureScheme;
|
|
||||||
use openmls_traits::signatures::Signer;
|
|
||||||
|
|
||||||
use crate::{AccountId, IdentityProvider};
|
|
||||||
|
|
||||||
/// Logos Account represents a single account across
|
|
||||||
/// multiple installations and services.
|
|
||||||
///
|
|
||||||
/// Deprecated!
|
|
||||||
pub struct LogosAccount {
|
|
||||||
id: AccountId,
|
|
||||||
signing_key: Ed25519SigningKey,
|
|
||||||
verifying_key: Ed25519VerifyingKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogosAccount {
|
|
||||||
/// Create a test LogosAccount. The `AccountId` is derived from the
|
|
||||||
/// generated Ed25519 verifying key (hex-encoded) so signatures over the
|
|
||||||
/// id can be verified by anyone holding the id alone.
|
|
||||||
/// The supplied `_display_name` is currently ignored — id is the key.
|
|
||||||
/// This should only be used during MLS integration. Not suitable for production use.
|
|
||||||
/// TODO: (P1) Remove once implementation is ready.
|
|
||||||
pub fn new_test(_display_name: impl Into<String>) -> Self {
|
|
||||||
let signing_key = Ed25519SigningKey::generate();
|
|
||||||
let verifying_key = signing_key.verifying_key();
|
|
||||||
let id = AccountId::new(hex::encode(verifying_key.as_ref()));
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
signing_key,
|
|
||||||
verifying_key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signer for LogosAccount {
|
|
||||||
// TODO: (P2) Remove OpenMLS dependency to make accounts more portable
|
|
||||||
fn sign(&self, payload: &[u8]) -> Result<Vec<u8>, openmls_traits::signatures::SignerError> {
|
|
||||||
Ok(self.signing_key.sign(payload).as_ref().to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature_scheme(&self) -> SignatureScheme {
|
|
||||||
SignatureScheme::ED25519
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdentityProvider for LogosAccount {
|
|
||||||
fn account_id(&self) -> &AccountId {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_name(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign(&self, payload: &[u8]) -> Ed25519Signature {
|
|
||||||
self.signing_key.sign(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn public_key(&self) -> &Ed25519VerifyingKey {
|
|
||||||
&self.verifying_key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +1,13 @@
|
|||||||
pub mod group_v1;
|
pub mod group_v1;
|
||||||
mod privatev1;
|
mod privatev1;
|
||||||
|
|
||||||
|
pub use crate::errors::ChatError;
|
||||||
use crate::outcomes::ConvoOutcome;
|
use crate::outcomes::ConvoOutcome;
|
||||||
use crate::proto::EncryptedPayload;
|
use crate::proto::EncryptedPayload;
|
||||||
use crate::service_context::{ExternalServices, ServiceContext};
|
use crate::service_context::{ExternalServices, ServiceContext};
|
||||||
use crate::types::AccountId;
|
|
||||||
|
|
||||||
pub use crate::errors::ChatError;
|
|
||||||
pub use group_v1::GroupV1Convo;
|
pub use group_v1::GroupV1Convo;
|
||||||
pub use privatev1::PrivateV1Convo;
|
pub use privatev1::PrivateV1Convo;
|
||||||
|
use shared_traits::IdentIdRef;
|
||||||
|
|
||||||
pub type ConversationId = String;
|
pub type ConversationId = String;
|
||||||
|
|
||||||
@ -34,6 +33,6 @@ pub(crate) trait GroupConvo<S: ExternalServices>: Convo<S> {
|
|||||||
fn add_member(
|
fn add_member(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ServiceContext<S>,
|
cx: &mut ServiceContext<S>,
|
||||||
members: &[&AccountId],
|
members: &[IdentIdRef],
|
||||||
) -> Result<(), ChatError>;
|
) -> Result<(), ChatError>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,11 @@ use chat_proto::logoschat::reliability::ReliablePayload;
|
|||||||
use openmls::prelude::tls_codec::Deserialize;
|
use openmls::prelude::tls_codec::Deserialize;
|
||||||
use openmls::prelude::*;
|
use openmls::prelude::*;
|
||||||
use prost::Message as _;
|
use prost::Message as _;
|
||||||
|
use shared_traits::IdentIdRef;
|
||||||
|
|
||||||
use crate::inbox_v2::MlsProvider;
|
use crate::inbox_v2::MlsProvider;
|
||||||
use crate::service_context::{ExternalServices, ServiceContext};
|
use crate::service_context::{ExternalServices, ServiceContext};
|
||||||
use crate::types::AccountId;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DeliveryService, IdentityProvider,
|
DeliveryService, IdentityProvider,
|
||||||
conversation::{ChatError, Convo, GroupConvo},
|
conversation::{ChatError, Convo, GroupConvo},
|
||||||
@ -140,7 +141,7 @@ impl GroupV1Convo {
|
|||||||
|
|
||||||
fn key_package_for_account(
|
fn key_package_for_account(
|
||||||
&self,
|
&self,
|
||||||
ident: &AccountId,
|
ident: IdentIdRef,
|
||||||
provider: &impl MlsProvider,
|
provider: &impl MlsProvider,
|
||||||
keypkg_provider: &impl KeyPackageProvider,
|
keypkg_provider: &impl KeyPackageProvider,
|
||||||
) -> Result<KeyPackage, ChatError> {
|
) -> Result<KeyPackage, ChatError> {
|
||||||
@ -171,7 +172,7 @@ impl GroupV1Convo {
|
|||||||
content: &[u8],
|
content: &[u8],
|
||||||
cx: &ServiceContext<S>,
|
cx: &ServiceContext<S>,
|
||||||
) -> Result<Vec<AddressedEncryptedPayload>, ChatError> {
|
) -> Result<Vec<AddressedEncryptedPayload>, ChatError> {
|
||||||
let sender_id = cx.mls_identity.account_id().as_str();
|
let sender_id = cx.mls_identity.id().as_str();
|
||||||
let reliable = cx.causal.on_send(&self.convo_id, sender_id, content);
|
let reliable = cx.causal.on_send(&self.convo_id, sender_id, content);
|
||||||
let wire = reliable.encode_to_vec();
|
let wire = reliable.encode_to_vec();
|
||||||
|
|
||||||
@ -274,7 +275,7 @@ impl<S: ExternalServices> GroupConvo<S> for GroupV1Convo {
|
|||||||
fn add_member(
|
fn add_member(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ServiceContext<S>,
|
cx: &mut ServiceContext<S>,
|
||||||
members: &[&AccountId],
|
members: &[IdentIdRef],
|
||||||
) -> Result<(), ChatError> {
|
) -> Result<(), ChatError> {
|
||||||
if members.len() > 50 {
|
if members.len() > 50 {
|
||||||
// This is a temporary limit that originates from the the De-MLS epoch time.
|
// This is a temporary limit that originates from the the De-MLS epoch time.
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use crate::account::LogosAccount;
|
|
||||||
use crate::causal_history::{CausalHistoryStore, MissingMessage};
|
use crate::causal_history::{CausalHistoryStore, MissingMessage};
|
||||||
use crate::service_context::{ExternalServices, ServiceContext};
|
use crate::service_context::{ExternalServices, ServiceContext};
|
||||||
use crate::{DeliveryService, IdentityProvider, RegistrationService};
|
use crate::{DeliveryService, IdentityProvider, RegistrationService};
|
||||||
@ -9,10 +8,10 @@ use crate::{
|
|||||||
inbox_v2::{InboxV2, MlsEphemeralPqProvider, MlsIdentityProvider},
|
inbox_v2::{InboxV2, MlsEphemeralPqProvider, MlsIdentityProvider},
|
||||||
outcomes::{ConvoOutcome, InboxOutcome, PayloadOutcome},
|
outcomes::{ConvoOutcome, InboxOutcome, PayloadOutcome},
|
||||||
proto::{EncryptedPayload, EnvelopeV1, Message},
|
proto::{EncryptedPayload, EnvelopeV1, Message},
|
||||||
types::AccountId,
|
|
||||||
};
|
};
|
||||||
use crypto::{Identity, PublicKey};
|
use crypto::{Identity, PublicKey};
|
||||||
use openmls::prelude::GroupId;
|
use openmls::prelude::GroupId;
|
||||||
|
use shared_traits::IdentIdRef;
|
||||||
use storage::{ChatStore, ConversationKind, ConversationStore};
|
use storage::{ChatStore, ConversationKind, ConversationStore};
|
||||||
|
|
||||||
pub use crate::conversation::ConversationId;
|
pub use crate::conversation::ConversationId;
|
||||||
@ -32,8 +31,9 @@ pub struct Core<S: ExternalServices> {
|
|||||||
|
|
||||||
// Constructors live on the `(DS, RS, CS)` form: `S` can't be inferred backwards
|
// Constructors live on the `(DS, RS, CS)` form: `S` can't be inferred backwards
|
||||||
// through `S::DS`, so the bundle is built from the three args here.
|
// through `S::DS`, so the bundle is built from the three args here.
|
||||||
impl<DS, RS, CS> Core<(DS, RS, CS)>
|
impl<IP, DS, RS, CS> Core<(IP, DS, RS, CS)>
|
||||||
where
|
where
|
||||||
|
IP: IdentityProvider + 'static,
|
||||||
DS: DeliveryService + 'static,
|
DS: DeliveryService + 'static,
|
||||||
RS: RegistrationService + 'static,
|
RS: RegistrationService + 'static,
|
||||||
CS: ChatStore + 'static,
|
CS: ChatStore + 'static,
|
||||||
@ -43,42 +43,34 @@ where
|
|||||||
/// If an identity exists in storage, it will be restored.
|
/// If an identity exists in storage, it will be restored.
|
||||||
/// Otherwise, a new identity will be created with the given name and saved.
|
/// Otherwise, a new identity will be created with the given name and saved.
|
||||||
pub fn new_from_store(
|
pub fn new_from_store(
|
||||||
name: impl Into<String>,
|
ident: IP,
|
||||||
delivery: DS,
|
delivery: DS,
|
||||||
registration: RS,
|
registration: RS,
|
||||||
mut store: CS,
|
mut store: CS,
|
||||||
) -> Result<Self, ChatError> {
|
) -> Result<Self, ChatError> {
|
||||||
let name = name.into();
|
|
||||||
|
|
||||||
// Load or create identity
|
|
||||||
let identity = if let Some(identity) = store.load_identity()? {
|
let identity = if let Some(identity) = store.load_identity()? {
|
||||||
identity
|
identity
|
||||||
} else {
|
} else {
|
||||||
let identity = Identity::new(&name);
|
let identity = Identity::new(ident.id().as_str().to_string());
|
||||||
store.save_identity(&identity)?;
|
store.save_identity(&identity)?;
|
||||||
identity
|
identity
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::assemble(name, identity, delivery, registration, store)
|
Self::assemble(ident, identity, delivery, registration, store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new in-memory `Core` (for testing).
|
/// Creates a new in-memory `Core` (for testing).
|
||||||
///
|
///
|
||||||
/// Uses in-memory SQLite database. Each call creates a new isolated database.
|
/// Uses in-memory SQLite database. Each call creates a new isolated database.
|
||||||
pub fn new_with_name(
|
pub fn new_with_name(
|
||||||
name: impl Into<String>,
|
ident: IP,
|
||||||
delivery: DS,
|
delivery: DS,
|
||||||
registration: RS,
|
registration: RS,
|
||||||
mut store: CS,
|
store: CS,
|
||||||
) -> Result<Self, ChatError> {
|
) -> Result<Self, ChatError> {
|
||||||
let name = name.into();
|
let identity = Identity::new(ident.id().as_str().to_string());
|
||||||
let identity = Identity::new(&name);
|
let mut core = Self::assemble(ident, identity, delivery, registration, store)?;
|
||||||
store
|
|
||||||
.save_identity(&identity)
|
|
||||||
.expect("in-memory storage should not fail");
|
|
||||||
|
|
||||||
let mut core = Self::assemble(name, identity, delivery, registration, store)?;
|
|
||||||
// TODO: (P2) Initialize Account in Core or upper client.
|
|
||||||
core.register_keypackage()?;
|
core.register_keypackage()?;
|
||||||
Ok(core)
|
Ok(core)
|
||||||
}
|
}
|
||||||
@ -86,19 +78,18 @@ where
|
|||||||
/// Builds the inbox/account/MLS/causal state, subscribes both inbound
|
/// Builds the inbox/account/MLS/causal state, subscribes both inbound
|
||||||
/// addresses, and assembles the service bundle — shared by both constructors.
|
/// addresses, and assembles the service bundle — shared by both constructors.
|
||||||
fn assemble(
|
fn assemble(
|
||||||
name: String,
|
ident: IP,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
mut delivery: DS,
|
mut delivery: DS,
|
||||||
registration: RS,
|
registration: RS,
|
||||||
store: CS,
|
store: CS,
|
||||||
) -> Result<Self, ChatError> {
|
) -> Result<Self, ChatError> {
|
||||||
let inbox = Inbox::new(&identity);
|
let inbox = Inbox::new(&identity);
|
||||||
let account = LogosAccount::new_test(name);
|
let ident_id = ident.id().clone();
|
||||||
let account_id = account.account_id().clone();
|
let mls_identity = MlsIdentityProvider::new(ident);
|
||||||
let mls_identity = MlsIdentityProvider::new(account);
|
|
||||||
let mls_provider = MlsEphemeralPqProvider::new().map_err(ChatError::generic)?;
|
let mls_provider = MlsEphemeralPqProvider::new().map_err(ChatError::generic)?;
|
||||||
let causal = CausalHistoryStore::new();
|
let causal = CausalHistoryStore::new();
|
||||||
let pq_inbox = InboxV2::new(account_id);
|
let pq_inbox = InboxV2::new(ident_id);
|
||||||
|
|
||||||
// Subscribe to inbound addresses for both conversation stacks.
|
// Subscribe to inbound addresses for both conversation stacks.
|
||||||
delivery
|
delivery
|
||||||
@ -124,7 +115,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ExternalServices + 'static> Core<S> {
|
impl<'a, S: ExternalServices + 'static> Core<S> {
|
||||||
pub fn ds(&mut self) -> &mut S::DS {
|
pub fn ds(&mut self) -> &mut S::DS {
|
||||||
&mut self.services.ds
|
&mut self.services.ds
|
||||||
}
|
}
|
||||||
@ -138,8 +129,8 @@ impl<S: ExternalServices + 'static> Core<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the unique identifier associated with the account
|
/// Returns the unique identifier associated with the account
|
||||||
pub fn account_id(&self) -> &AccountId {
|
pub fn ident_id(&'a self) -> IdentIdRef<'a> {
|
||||||
self.pq_inbox.account_id()
|
self.pq_inbox.ident_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Submit the local account's MLS KeyPackage to the registration service.
|
/// Submit the local account's MLS KeyPackage to the registration service.
|
||||||
@ -179,7 +170,7 @@ impl<S: ExternalServices + 'static> Core<S> {
|
|||||||
|
|
||||||
pub fn create_group_convo(
|
pub fn create_group_convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
participants: &[&AccountId],
|
participants: &[IdentIdRef],
|
||||||
) -> Result<ConversationId, ChatError> {
|
) -> Result<ConversationId, ChatError> {
|
||||||
// TODO: (P1) Ensure errors are handled properly. This is a high chance for
|
// TODO: (P1) Ensure errors are handled properly. This is a high chance for
|
||||||
// desynchronized state: MlsGroup persistence, conversation persistence, and
|
// desynchronized state: MlsGroup persistence, conversation persistence, and
|
||||||
@ -200,7 +191,7 @@ impl<S: ExternalServices + 'static> Core<S> {
|
|||||||
pub fn group_add_member(
|
pub fn group_add_member(
|
||||||
&mut self,
|
&mut self,
|
||||||
convo_id: &str,
|
convo_id: &str,
|
||||||
members: &[&AccountId],
|
members: &[IdentIdRef],
|
||||||
) -> Result<(), ChatError> {
|
) -> Result<(), ChatError> {
|
||||||
let mut convo = self.load_group_convo(convo_id)?;
|
let mut convo = self.load_group_convo(convo_id)?;
|
||||||
convo.add_member(&mut self.services, members)
|
convo.add_member(&mut self.services, members)
|
||||||
|
|||||||
@ -270,18 +270,60 @@ impl Inbox {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use chat_sqlite::{ChatStorage, StorageConfig};
|
use chat_sqlite::{ChatStorage, StorageConfig};
|
||||||
|
use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
|
||||||
|
use shared_traits::{IdentId, IdentityProvider};
|
||||||
|
|
||||||
|
struct Identity {
|
||||||
|
name: IdentId,
|
||||||
|
key: Ed25519SigningKey,
|
||||||
|
verify: Ed25519VerifyingKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identity {
|
||||||
|
pub fn new(name: impl Into<String>) -> Self {
|
||||||
|
let key = Ed25519SigningKey::generate();
|
||||||
|
let verify = key.verifying_key();
|
||||||
|
Identity {
|
||||||
|
name: IdentId::new(name.into()),
|
||||||
|
key,
|
||||||
|
verify,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentityProvider for Identity {
|
||||||
|
fn id(&self) -> shared_traits::IdentIdRef<'_> {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_name(&self) -> String {
|
||||||
|
self.name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign(&self, payload: &[u8]) -> crypto::Ed25519Signature {
|
||||||
|
self.key.sign(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(&self) -> &crypto::Ed25519VerifyingKey {
|
||||||
|
&self.verify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invite_privatev1_roundtrip() {
|
fn test_invite_privatev1_roundtrip() {
|
||||||
let saro_storage = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
let saro_storage = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
||||||
let raya_storage = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
let raya_storage = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
||||||
|
|
||||||
let mut saro_cx = ServiceContext::for_test("saro", saro_storage).unwrap();
|
let saro_account = Identity::new("saro");
|
||||||
|
let raya_account = Identity::new("raya");
|
||||||
|
|
||||||
|
let mut saro_cx = ServiceContext::for_test(saro_account, saro_storage).unwrap();
|
||||||
let saro_inbox = Inbox::new(&saro_cx.identity);
|
let saro_inbox = Inbox::new(&saro_cx.identity);
|
||||||
|
|
||||||
let mut raya_cx = ServiceContext::for_test("raya", raya_storage).unwrap();
|
let mut raya_cx = ServiceContext::for_test(raya_account, raya_storage).unwrap();
|
||||||
let raya_inbox = Inbox::new(&raya_cx.identity);
|
let raya_inbox = Inbox::new(&raya_cx.identity);
|
||||||
|
|
||||||
let bundle = raya_inbox.create_intro_bundle(&mut raya_cx).unwrap();
|
let bundle = raya_inbox.create_intro_bundle(&mut raya_cx).unwrap();
|
||||||
|
|||||||
@ -3,6 +3,8 @@ mod mls_provider;
|
|||||||
|
|
||||||
pub use identity::MlsIdentityProvider;
|
pub use identity::MlsIdentityProvider;
|
||||||
pub(crate) use mls_provider::MlsEphemeralPqProvider;
|
pub(crate) use mls_provider::MlsEphemeralPqProvider;
|
||||||
|
use shared_traits::IdentId;
|
||||||
|
use shared_traits::IdentIdRef;
|
||||||
|
|
||||||
use chat_proto::logoschat::envelope::EnvelopeV1;
|
use chat_proto::logoschat::envelope::EnvelopeV1;
|
||||||
use openmls::prelude::tls_codec::Serialize;
|
use openmls::prelude::tls_codec::Serialize;
|
||||||
@ -18,16 +20,15 @@ use crate::conversation::ConversationId;
|
|||||||
use crate::conversation::GroupV1Convo;
|
use crate::conversation::GroupV1Convo;
|
||||||
use crate::outcomes::{ConversationClass, InboxOutcome, NewConversation};
|
use crate::outcomes::{ConversationClass, InboxOutcome, NewConversation};
|
||||||
use crate::service_context::{ExternalServices, ServiceContext};
|
use crate::service_context::{ExternalServices, ServiceContext};
|
||||||
use crate::types::AccountId;
|
|
||||||
use crate::utils::{blake2b_hex, hash_size};
|
use crate::utils::{blake2b_hex, hash_size};
|
||||||
|
|
||||||
// Define unique Identifiers derivations used in InboxV2
|
// Define unique Identifiers derivations used in InboxV2
|
||||||
fn delivery_address_for(account_id: &AccountId) -> String {
|
fn delivery_address_for(ident_id: IdentIdRef) -> String {
|
||||||
blake2b_hex::<hash_size::AccountId>(&["InboxV2|", "delivery_address|", account_id.as_str()])
|
blake2b_hex::<hash_size::DeliveryAddr>(&["InboxV2|", "delivery_address|", ident_id.as_str()])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conversation_id_for(account_id: &AccountId) -> String {
|
fn conversation_id_for(ident_id: IdentIdRef) -> String {
|
||||||
blake2b_hex::<hash_size::ConvoId>(&["InboxV2|", "conversation_id|", account_id.as_str()])
|
blake2b_hex::<hash_size::ConvoId>(&["InboxV2|", "conversation_id|", ident_id.as_str()])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An Extension trait which extends OpenMlsProvider to add required functionality
|
/// An Extension trait which extends OpenMlsProvider to add required functionality
|
||||||
@ -36,7 +37,7 @@ pub trait MlsProvider: OpenMlsProvider {
|
|||||||
fn invite_user<DS: DeliveryService>(
|
fn invite_user<DS: DeliveryService>(
|
||||||
&self,
|
&self,
|
||||||
ds: &mut DS,
|
ds: &mut DS,
|
||||||
account_id: &AccountId,
|
ident_id: IdentIdRef,
|
||||||
welcome: &MlsMessageOut,
|
welcome: &MlsMessageOut,
|
||||||
) -> Result<(), ChatError>;
|
) -> Result<(), ChatError>;
|
||||||
}
|
}
|
||||||
@ -46,16 +47,16 @@ pub trait MlsProvider: OpenMlsProvider {
|
|||||||
/// such as MLS.
|
/// such as MLS.
|
||||||
pub struct InboxV2 {
|
pub struct InboxV2 {
|
||||||
// Account_id field is an owned value, so it can be returned via reference.
|
// Account_id field is an owned value, so it can be returned via reference.
|
||||||
account_id: AccountId,
|
ident_id: IdentId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InboxV2 {
|
impl InboxV2 {
|
||||||
pub fn new(account_id: AccountId) -> Self {
|
pub fn new(ident_id: IdentId) -> Self {
|
||||||
Self { account_id }
|
Self { ident_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account_id(&self) -> &AccountId {
|
pub fn ident_id(&self) -> IdentIdRef<'_> {
|
||||||
&self.account_id
|
&self.ident_id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Submit MlsKeypackage to registration service
|
/// Submit MlsKeypackage to registration service
|
||||||
@ -73,11 +74,11 @@ impl InboxV2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn delivery_address(&self) -> String {
|
pub fn delivery_address(&self) -> String {
|
||||||
delivery_address_for(&self.account_id)
|
delivery_address_for(&self.ident_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> String {
|
pub fn id(&self) -> String {
|
||||||
conversation_id_for(&self.account_id)
|
conversation_id_for(&self.ident_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_frame<S: ExternalServices>(
|
pub fn handle_frame<S: ExternalServices>(
|
||||||
|
|||||||
@ -5,8 +5,9 @@ use openmls_traits::{
|
|||||||
signatures::{Signer, SignerError},
|
signatures::{Signer, SignerError},
|
||||||
types::SignatureScheme,
|
types::SignatureScheme,
|
||||||
};
|
};
|
||||||
|
use shared_traits::IdentIdRef;
|
||||||
|
|
||||||
use crate::{AccountId, IdentityProvider};
|
use crate::IdentityProvider;
|
||||||
|
|
||||||
/// A Wrapper for an IdentityProvider which provides MLS specific functionality
|
/// A Wrapper for an IdentityProvider which provides MLS specific functionality
|
||||||
///
|
///
|
||||||
@ -22,7 +23,7 @@ impl<T: IdentityProvider> MlsIdentityProvider<T> {
|
|||||||
|
|
||||||
pub fn get_credential(&self) -> CredentialWithKey {
|
pub fn get_credential(&self) -> CredentialWithKey {
|
||||||
CredentialWithKey {
|
CredentialWithKey {
|
||||||
credential: BasicCredential::new(self.account_id().as_str().as_bytes().to_vec()).into(),
|
credential: BasicCredential::new(self.id().as_str().as_bytes().to_vec()).into(),
|
||||||
signature_key: self.public_key().as_ref().into(),
|
signature_key: self.public_key().as_ref().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,8 +38,8 @@ impl<T: IdentityProvider> Deref for MlsIdentityProvider<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IdentityProvider> IdentityProvider for MlsIdentityProvider<T> {
|
impl<T: IdentityProvider> IdentityProvider for MlsIdentityProvider<T> {
|
||||||
fn account_id(&self) -> &AccountId {
|
fn id(&self) -> IdentIdRef<'_> {
|
||||||
self.0.account_id()
|
self.0.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_name(&self) -> String {
|
fn display_name(&self) -> String {
|
||||||
|
|||||||
@ -4,8 +4,9 @@ use openmls_memory_storage::MemoryStorage;
|
|||||||
use openmls_traits::OpenMlsProvider;
|
use openmls_traits::OpenMlsProvider;
|
||||||
use openmls_traits::types::CryptoError;
|
use openmls_traits::types::CryptoError;
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
use shared_traits::IdentIdRef;
|
||||||
|
|
||||||
use crate::{AccountId, ChatError, DeliveryService};
|
use crate::{ChatError, DeliveryService};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AddressedEnvelope, EnvelopeV1, GroupV1HeavyInvite, InboxV2Frame, InviteType, MlsProvider,
|
AddressedEnvelope, EnvelopeV1, GroupV1HeavyInvite, InboxV2Frame, InviteType, MlsProvider,
|
||||||
@ -31,7 +32,7 @@ impl MlsProvider for MlsEphemeralPqProvider {
|
|||||||
fn invite_user<DS: DeliveryService>(
|
fn invite_user<DS: DeliveryService>(
|
||||||
&self,
|
&self,
|
||||||
ds: &mut DS,
|
ds: &mut DS,
|
||||||
account_id: &AccountId,
|
ident_id: IdentIdRef,
|
||||||
welcome: &MlsMessageOut,
|
welcome: &MlsMessageOut,
|
||||||
) -> Result<(), ChatError> {
|
) -> Result<(), ChatError> {
|
||||||
let invite = GroupV1HeavyInvite {
|
let invite = GroupV1HeavyInvite {
|
||||||
@ -43,13 +44,13 @@ impl MlsProvider for MlsEphemeralPqProvider {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let envelope = EnvelopeV1 {
|
let envelope = EnvelopeV1 {
|
||||||
conversation_hint: conversation_id_for(account_id),
|
conversation_hint: conversation_id_for(ident_id),
|
||||||
salt: 0,
|
salt: 0,
|
||||||
payload: frame.encode_to_vec().into(),
|
payload: frame.encode_to_vec().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let outbound_msg = AddressedEnvelope {
|
let outbound_msg = AddressedEnvelope {
|
||||||
delivery_address: delivery_address_for(account_id),
|
delivery_address: delivery_address_for(ident_id),
|
||||||
data: envelope.encode_to_vec(),
|
data: envelope.encode_to_vec(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
mod account;
|
|
||||||
mod causal_history;
|
mod causal_history;
|
||||||
mod conversation;
|
mod conversation;
|
||||||
mod core;
|
mod core;
|
||||||
@ -13,7 +12,6 @@ mod service_traits;
|
|||||||
mod types;
|
mod types;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use account::LogosAccount;
|
|
||||||
pub use causal_history::{Frontier, MissingMessage};
|
pub use causal_history::{Frontier, MissingMessage};
|
||||||
pub use chat_sqlite::ChatStorage;
|
pub use chat_sqlite::ChatStorage;
|
||||||
pub use chat_sqlite::StorageConfig;
|
pub use chat_sqlite::StorageConfig;
|
||||||
@ -23,7 +21,8 @@ pub use outcomes::{
|
|||||||
Content, ConversationClass, ConvoOutcome, InboxOutcome, NewConversation, PayloadOutcome,
|
Content, ConversationClass, ConvoOutcome, InboxOutcome, NewConversation, PayloadOutcome,
|
||||||
};
|
};
|
||||||
pub use service_context::ExternalServices;
|
pub use service_context::ExternalServices;
|
||||||
pub use service_traits::{DeliveryService, IdentityProvider, RegistrationService};
|
pub use service_traits::{DeliveryService, RegistrationService};
|
||||||
|
pub use shared_traits::IdentityProvider;
|
||||||
pub use storage::ConversationKind;
|
pub use storage::ConversationKind;
|
||||||
pub use types::{AccountId, AddressedEnvelope};
|
pub use types::AddressedEnvelope;
|
||||||
pub use utils::hex_trunc;
|
pub use utils::hex_trunc;
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
use crypto::Identity;
|
use crypto::Identity;
|
||||||
use storage::ChatStore;
|
use storage::ChatStore;
|
||||||
|
|
||||||
use crate::account::LogosAccount;
|
use crate::IdentityProvider;
|
||||||
use crate::causal_history::CausalHistoryStore;
|
use crate::causal_history::CausalHistoryStore;
|
||||||
use crate::inbox_v2::{MlsEphemeralPqProvider, MlsIdentityProvider};
|
use crate::inbox_v2::{MlsEphemeralPqProvider, MlsIdentityProvider};
|
||||||
use crate::{DeliveryService, RegistrationService};
|
use crate::{DeliveryService, RegistrationService};
|
||||||
@ -11,17 +11,20 @@ use crate::{DeliveryService, RegistrationService};
|
|||||||
/// Bundles the external service types (`DS`, `RS`, `CS`) behind one `S`. The
|
/// Bundles the external service types (`DS`, `RS`, `CS`) behind one `S`. The
|
||||||
/// `(DS, RS, CS)` tuple impl lets them still be supplied separately.
|
/// `(DS, RS, CS)` tuple impl lets them still be supplied separately.
|
||||||
pub trait ExternalServices {
|
pub trait ExternalServices {
|
||||||
|
type IP: IdentityProvider;
|
||||||
type DS: DeliveryService;
|
type DS: DeliveryService;
|
||||||
type RS: RegistrationService;
|
type RS: RegistrationService;
|
||||||
type CS: ChatStore;
|
type CS: ChatStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<DS, RS, CS> ExternalServices for (DS, RS, CS)
|
impl<IP, DS, RS, CS> ExternalServices for (IP, DS, RS, CS)
|
||||||
where
|
where
|
||||||
|
IP: IdentityProvider,
|
||||||
DS: DeliveryService,
|
DS: DeliveryService,
|
||||||
RS: RegistrationService,
|
RS: RegistrationService,
|
||||||
CS: ChatStore,
|
CS: ChatStore,
|
||||||
{
|
{
|
||||||
|
type IP = IP;
|
||||||
type DS = DS;
|
type DS = DS;
|
||||||
type RS = RS;
|
type RS = RS;
|
||||||
type CS = CS;
|
type CS = CS;
|
||||||
@ -32,7 +35,7 @@ pub(crate) struct ServiceContext<S: ExternalServices> {
|
|||||||
pub(crate) ds: S::DS,
|
pub(crate) ds: S::DS,
|
||||||
pub(crate) registry: S::RS,
|
pub(crate) registry: S::RS,
|
||||||
pub(crate) store: S::CS,
|
pub(crate) store: S::CS,
|
||||||
pub(crate) mls_identity: MlsIdentityProvider<LogosAccount>,
|
pub(crate) mls_identity: MlsIdentityProvider<S::IP>,
|
||||||
pub(crate) mls_provider: MlsEphemeralPqProvider,
|
pub(crate) mls_provider: MlsEphemeralPqProvider,
|
||||||
pub(crate) causal: CausalHistoryStore,
|
pub(crate) causal: CausalHistoryStore,
|
||||||
pub(crate) identity: Identity,
|
pub(crate) identity: Identity,
|
||||||
@ -80,15 +83,15 @@ mod test_support {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: ChatStore> ServiceContext<(NoopDelivery, NoopRegistration, CS)> {
|
impl<IP: IdentityProvider, CS: ChatStore> ServiceContext<(IP, NoopDelivery, NoopRegistration, CS)> {
|
||||||
/// Builds a context around a real store, stubbing other services.
|
/// Builds a context around a real store, stubbing other services.
|
||||||
pub(crate) fn for_test(name: &str, store: CS) -> Result<Self, ChatError> {
|
pub(crate) fn for_test(ident: IP, store: CS) -> Result<Self, ChatError> {
|
||||||
let account = LogosAccount::new_test(name);
|
let name = ident.id().as_str().to_string();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ds: NoopDelivery,
|
ds: NoopDelivery,
|
||||||
registry: NoopRegistration,
|
registry: NoopRegistration,
|
||||||
store,
|
store,
|
||||||
mls_identity: MlsIdentityProvider::new(account),
|
mls_identity: MlsIdentityProvider::new(ident),
|
||||||
mls_provider: MlsEphemeralPqProvider::new().map_err(ChatError::generic)?,
|
mls_provider: MlsEphemeralPqProvider::new().map_err(ChatError::generic)?,
|
||||||
causal: CausalHistoryStore::new(),
|
causal: CausalHistoryStore::new(),
|
||||||
identity: Identity::new(name),
|
identity: Identity::new(name),
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
/// Service traits define the functionality which must be externally supplied by
|
/// Service traits define the functionality which must be externally supplied by
|
||||||
/// platform clients. Platforms can alter the behaviour of the chat core by supplying
|
/// platform clients. Platforms can alter the behaviour of the chat core by supplying
|
||||||
/// different implementations.
|
/// different implementations.
|
||||||
|
use shared_traits::IdentityProvider;
|
||||||
use std::{fmt::Debug, fmt::Display};
|
use std::{fmt::Debug, fmt::Display};
|
||||||
|
|
||||||
use crypto::{Ed25519Signature, Ed25519VerifyingKey};
|
use crate::types::AddressedEnvelope;
|
||||||
|
|
||||||
use crate::types::{AccountId, AddressedEnvelope};
|
|
||||||
|
|
||||||
/// A Delivery service is responsible for payload transport.
|
/// A Delivery service is responsible for payload transport.
|
||||||
/// This interface allows Conversations to send payloads on the wire as well as
|
/// This interface allows Conversations to send payloads on the wire as well as
|
||||||
@ -50,14 +49,3 @@ impl<T: RegistrationService> KeyPackageProvider for T {
|
|||||||
RegistrationService::retrieve(self, device_id)
|
RegistrationService::retrieve(self, device_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an external Identity
|
|
||||||
/// Implement this to provide an Authentication model for users/installations
|
|
||||||
pub trait IdentityProvider {
|
|
||||||
fn account_id(&self) -> &AccountId;
|
|
||||||
// Display name is not garenteed to be consistent. It should only be used to
|
|
||||||
// provded a more readable identifier for the account.
|
|
||||||
fn display_name(&self) -> String;
|
|
||||||
fn sign(&self, payload: &[u8]) -> Ed25519Signature;
|
|
||||||
fn public_key(&self) -> &Ed25519VerifyingKey;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::fmt::{self, Debug};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::proto::{self, Message};
|
use crate::proto::{self, Message};
|
||||||
|
|
||||||
@ -66,31 +66,3 @@ impl AddressedEncryptedPayload {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This represents an Identifier for an account.
|
|
||||||
/// Its a thin wrapper around a string, but providers extra functionality,
|
|
||||||
/// and ensures type consistency
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct AccountId(String);
|
|
||||||
|
|
||||||
impl AccountId {
|
|
||||||
pub fn new(id: impl Into<String>) -> Self {
|
|
||||||
Self(id.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for AccountId {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
std::fmt::Display::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for AccountId {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ pub fn timestamp_millis() -> i64 {
|
|||||||
/// Track hash sizes in use across the crate.
|
/// Track hash sizes in use across the crate.
|
||||||
pub mod hash_size {
|
pub mod hash_size {
|
||||||
use blake2::digest::{
|
use blake2::digest::{
|
||||||
consts::{U32, U64},
|
consts::{U4, U6, U32, U64},
|
||||||
generic_array::ArrayLength,
|
generic_array::ArrayLength,
|
||||||
typenum::{IsLessOrEqual, NonZero},
|
typenum::{IsLessOrEqual, NonZero},
|
||||||
};
|
};
|
||||||
@ -34,12 +34,11 @@ pub mod hash_size {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
use blake2::digest::consts::{U6, U8};
|
|
||||||
hash_sizes! {
|
hash_sizes! {
|
||||||
/// Account ID hash length
|
|
||||||
AccountId => U8,
|
|
||||||
/// Conversation ID hash length
|
/// Conversation ID hash length
|
||||||
ConvoId => U6,
|
ConvoId => U6,
|
||||||
|
/// Delivery Address length
|
||||||
|
DeliveryAddr => U4,
|
||||||
/// Causal history message ID hash length (256-bit, collision-resistant)
|
/// Causal history message ID hash length (256-bit, collision-resistant)
|
||||||
MessageId => U32,
|
MessageId => U32,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,10 @@ edition = "2024"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# Workspace dependencies (sorted)
|
# Workspace dependencies (sorted)
|
||||||
|
chat-sqlite = { workspace = true }
|
||||||
components = { workspace = true }
|
components = { workspace = true }
|
||||||
libchat = { workspace = true }
|
libchat = { workspace = true }
|
||||||
chat-sqlite = { workspace = true }
|
logos-account = { workspace = true , features = ["dev"]}
|
||||||
storage = { workspace = true }
|
storage = { workspace = true }
|
||||||
|
|
||||||
# External dependencies (sorted)
|
# External dependencies (sorted)
|
||||||
|
|||||||
@ -8,13 +8,25 @@ use std::ops::{Deref, DerefMut};
|
|||||||
|
|
||||||
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
||||||
use libchat::{Core, MissingMessage};
|
use libchat::{Core, MissingMessage};
|
||||||
|
use logos_account::TestLogosAccount;
|
||||||
struct Client {
|
struct Client {
|
||||||
inner: Core<(LocalBroadcaster, EphemeralRegistry, MemStore)>,
|
inner: Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
MemStore,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
fn init(core: Core<(LocalBroadcaster, EphemeralRegistry, MemStore)>) -> Self {
|
fn init(
|
||||||
|
core: Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
MemStore,
|
||||||
|
)>,
|
||||||
|
) -> Self {
|
||||||
Client { inner: core }
|
Client { inner: core }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +50,12 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Client {
|
impl Deref for Client {
|
||||||
type Target = Core<(LocalBroadcaster, EphemeralRegistry, MemStore)>;
|
type Target = Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
MemStore,
|
||||||
|
)>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
@ -55,16 +72,19 @@ fn missing_group_message_is_detected() {
|
|||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
|
|
||||||
|
let saro_account = TestLogosAccount::new("saro");
|
||||||
let saro_ctx =
|
let saro_ctx =
|
||||||
Core::new_with_name("saro", ds.new_consumer(), rs.clone(), MemStore::new()).unwrap();
|
Core::new_with_name(saro_account, ds.new_consumer(), rs.clone(), MemStore::new()).unwrap();
|
||||||
|
|
||||||
let raya_ctx = Core::new_with_name("raya", ds.clone(), rs.clone(), MemStore::new()).unwrap();
|
let raya_account = TestLogosAccount::new("raya");
|
||||||
|
let raya_ctx =
|
||||||
|
Core::new_with_name(raya_account, ds.clone(), rs.clone(), MemStore::new()).unwrap();
|
||||||
|
|
||||||
let mut saro = Client::init(saro_ctx);
|
let mut saro = Client::init(saro_ctx);
|
||||||
let mut raya = Client::init(raya_ctx);
|
let mut raya = Client::init(raya_ctx);
|
||||||
|
|
||||||
// Saro creates a group with Raya.
|
// Saro creates a group with Raya.
|
||||||
let raya_id = raya.account_id().clone();
|
let raya_id = raya.ident_id().clone();
|
||||||
let convo_id = saro.create_group_convo(&[&raya_id]).unwrap().to_string();
|
let convo_id = saro.create_group_convo(&[&raya_id]).unwrap().to_string();
|
||||||
|
|
||||||
// Raya joins (processes the Welcome + commit).
|
// Raya joins (processes the Welcome + commit).
|
||||||
@ -95,7 +115,7 @@ fn missing_group_message_is_detected() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
missing[0].frontier.sender_id(),
|
missing[0].frontier.sender_id(),
|
||||||
saro.account_id().as_str(),
|
saro.ident_id().as_str(),
|
||||||
"missing-message sender hint should attribute to Saro"
|
"missing-message sender hint should attribute to Saro"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,18 @@ use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
|||||||
use libchat::{
|
use libchat::{
|
||||||
Content, ConversationClass, ConvoOutcome, Core, NewConversation, PayloadOutcome, hex_trunc,
|
Content, ConversationClass, ConvoOutcome, Core, NewConversation, PayloadOutcome, hex_trunc,
|
||||||
};
|
};
|
||||||
|
use logos_account::TestLogosAccount;
|
||||||
|
|
||||||
type ResultCallback = Box<dyn Fn(&PayloadOutcome)>;
|
type ResultCallback = Box<dyn Fn(&PayloadOutcome)>;
|
||||||
|
|
||||||
// Simple client Functionality for testing
|
// Simple client Functionality for testing
|
||||||
struct Client {
|
struct Client {
|
||||||
inner: Core<(LocalBroadcaster, EphemeralRegistry, MemStore)>,
|
inner: Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
MemStore,
|
||||||
|
)>,
|
||||||
on_result: Option<ResultCallback>,
|
on_result: Option<ResultCallback>,
|
||||||
new_conversations: Vec<NewConversation>,
|
new_conversations: Vec<NewConversation>,
|
||||||
received_messages: Vec<(libchat::ConversationId, Content)>,
|
received_messages: Vec<(libchat::ConversationId, Content)>,
|
||||||
@ -17,7 +23,12 @@ struct Client {
|
|||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
fn init(
|
fn init(
|
||||||
core: Core<(LocalBroadcaster, EphemeralRegistry, MemStore)>,
|
core: Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
MemStore,
|
||||||
|
)>,
|
||||||
cb: Option<impl Fn(&PayloadOutcome) + 'static>,
|
cb: Option<impl Fn(&PayloadOutcome) + 'static>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Client {
|
Client {
|
||||||
@ -60,7 +71,12 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Client {
|
impl Deref for Client {
|
||||||
type Target = Core<(LocalBroadcaster, EphemeralRegistry, MemStore)>;
|
type Target = Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
MemStore,
|
||||||
|
)>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
@ -111,19 +127,22 @@ fn create_group() {
|
|||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
|
|
||||||
let saro_ctx =
|
let saro_ident = TestLogosAccount::new("saro");
|
||||||
Core::new_with_name("saro", ds.new_consumer(), rs.clone(), MemStore::new()).unwrap();
|
let saro =
|
||||||
let raya_ctx = Core::new_with_name("raya", ds.clone(), rs.clone(), MemStore::new()).unwrap();
|
Core::new_with_name(saro_ident, ds.new_consumer(), rs.clone(), MemStore::new()).unwrap();
|
||||||
|
|
||||||
|
let raya_ident = TestLogosAccount::new("raya");
|
||||||
|
let raya = Core::new_with_name(raya_ident, ds.clone(), rs.clone(), MemStore::new()).unwrap();
|
||||||
|
|
||||||
let mut clients = vec![
|
let mut clients = vec![
|
||||||
Client::init(saro_ctx, Some(pretty_print(" Saro "))),
|
Client::init(saro, Some(pretty_print(" Saro "))),
|
||||||
Client::init(raya_ctx, Some(pretty_print(" Raya "))),
|
Client::init(raya, Some(pretty_print(" Raya "))),
|
||||||
];
|
];
|
||||||
|
|
||||||
const SARO: usize = 0;
|
const SARO: usize = 0;
|
||||||
const RAYA: usize = 1;
|
const RAYA: usize = 1;
|
||||||
|
|
||||||
let raya_id = clients[RAYA].account_id().clone();
|
let raya_id = clients[RAYA].ident_id().clone();
|
||||||
let convo_id = clients[SARO]
|
let convo_id = clients[SARO]
|
||||||
.create_group_convo(&[&raya_id])
|
.create_group_convo(&[&raya_id])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -157,11 +176,12 @@ fn create_group() {
|
|||||||
|
|
||||||
process(&mut clients);
|
process(&mut clients);
|
||||||
|
|
||||||
let pax_ctx = Core::new_with_name("pax", ds, rs, MemStore::new()).unwrap();
|
let pax_ident = TestLogosAccount::new("pax");
|
||||||
clients.push(Client::init(pax_ctx, Some(pretty_print(" Pax"))));
|
let pax = Core::new_with_name(pax_ident, ds, rs, MemStore::new()).unwrap();
|
||||||
|
clients.push(Client::init(pax, Some(pretty_print(" Pax"))));
|
||||||
const PAX: usize = 2;
|
const PAX: usize = 2;
|
||||||
|
|
||||||
let pax_id = clients[PAX].account_id().clone();
|
let pax_id = clients[PAX].ident_id().clone();
|
||||||
clients[SARO]
|
clients[SARO]
|
||||||
.group_add_member(&convo_id, &[&pax_id])
|
.group_add_member(&convo_id, &[&pax_id])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
use chat_sqlite::{ChatStorage, StorageConfig};
|
use chat_sqlite::{ChatStorage, StorageConfig};
|
||||||
use libchat::{ConversationClass, Core, Introduction, PayloadOutcome};
|
use libchat::{ConversationClass, Core, Introduction, PayloadOutcome};
|
||||||
|
use logos_account::TestLogosAccount;
|
||||||
use storage::{ConversationStore, IdentityStore};
|
use storage::{ConversationStore, IdentityStore};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use components::{EphemeralRegistry, LocalBroadcaster};
|
use components::{EphemeralRegistry, LocalBroadcaster};
|
||||||
|
|
||||||
type PrivateCore = Core<(LocalBroadcaster, EphemeralRegistry, ChatStorage)>;
|
type PrivateCore = Core<(
|
||||||
|
TestLogosAccount,
|
||||||
|
LocalBroadcaster,
|
||||||
|
EphemeralRegistry,
|
||||||
|
ChatStorage,
|
||||||
|
)>;
|
||||||
|
|
||||||
/// Drains everything published to `receiver`'s delivery service and feeds each
|
/// Drains everything published to `receiver`'s delivery service and feeds each
|
||||||
/// payload back through `handle_payload`, returning the observed outcomes.
|
/// payload back through `handle_payload`, returning the observed outcomes.
|
||||||
@ -49,9 +55,16 @@ fn ctx_integration() {
|
|||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
|
|
||||||
let mut saro =
|
let saro_account = TestLogosAccount::new("saro");
|
||||||
Core::new_with_name("saro", ds.clone(), rs.clone(), ChatStorage::in_memory()).unwrap();
|
let mut saro = Core::new_with_name(
|
||||||
let mut raya = Core::new_with_name("raya", ds, rs, ChatStorage::in_memory()).unwrap();
|
saro_account,
|
||||||
|
ds.clone(),
|
||||||
|
rs.clone(),
|
||||||
|
ChatStorage::in_memory(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let raya_account = TestLogosAccount::new("raya");
|
||||||
|
let mut raya = Core::new_with_name(raya_account, ds, rs, ChatStorage::in_memory()).unwrap();
|
||||||
|
|
||||||
// Raya creates intro bundle and sends to Saro
|
// Raya creates intro bundle and sends to Saro
|
||||||
let bundle = raya.create_intro_bundle().unwrap();
|
let bundle = raya.create_intro_bundle().unwrap();
|
||||||
@ -93,7 +106,8 @@ fn identity_persistence() {
|
|||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
let store1 = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
let store1 = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
||||||
let ctx1 = Core::new_with_name("alice", ds, rs, store1).unwrap();
|
let alice_account = TestLogosAccount::new("alice");
|
||||||
|
let ctx1 = Core::new_with_name(alice_account, ds, rs, store1).unwrap();
|
||||||
let pubkey1 = ctx1.identity().public_key();
|
let pubkey1 = ctx1.identity().public_key();
|
||||||
let name1 = ctx1.installation_name().to_string();
|
let name1 = ctx1.installation_name().to_string();
|
||||||
|
|
||||||
@ -112,7 +126,8 @@ fn open_persists_new_identity() {
|
|||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
let store = ChatStorage::new(StorageConfig::File(db_path.clone())).unwrap();
|
let store = ChatStorage::new(StorageConfig::File(db_path.clone())).unwrap();
|
||||||
let core = Core::new_from_store("alice", ds, rs, store).unwrap();
|
let alice_account = TestLogosAccount::new("alice");
|
||||||
|
let core = Core::new_from_store(alice_account, ds, rs, store).unwrap();
|
||||||
let pubkey = core.identity().public_key();
|
let pubkey = core.identity().public_key();
|
||||||
drop(core);
|
drop(core);
|
||||||
|
|
||||||
@ -127,9 +142,16 @@ fn open_persists_new_identity() {
|
|||||||
fn conversation_metadata_persistence() {
|
fn conversation_metadata_persistence() {
|
||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
let mut alice =
|
let alice_account = TestLogosAccount::new("alice");
|
||||||
Core::new_with_name("alice", ds.clone(), rs.clone(), ChatStorage::in_memory()).unwrap();
|
let mut alice = Core::new_with_name(
|
||||||
let mut bob = Core::new_with_name("bob", ds, rs, ChatStorage::in_memory()).unwrap();
|
alice_account,
|
||||||
|
ds.clone(),
|
||||||
|
rs.clone(),
|
||||||
|
ChatStorage::in_memory(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let bob_account = TestLogosAccount::new("bob");
|
||||||
|
let mut bob = Core::new_with_name(bob_account, ds, rs, ChatStorage::in_memory()).unwrap();
|
||||||
|
|
||||||
let bundle = alice.create_intro_bundle().unwrap();
|
let bundle = alice.create_intro_bundle().unwrap();
|
||||||
let intro = Introduction::try_from(bundle.as_slice()).unwrap();
|
let intro = Introduction::try_from(bundle.as_slice()).unwrap();
|
||||||
@ -153,9 +175,16 @@ fn conversation_metadata_persistence() {
|
|||||||
fn conversation_full_flow() {
|
fn conversation_full_flow() {
|
||||||
let ds = LocalBroadcaster::new();
|
let ds = LocalBroadcaster::new();
|
||||||
let rs = EphemeralRegistry::new();
|
let rs = EphemeralRegistry::new();
|
||||||
let mut alice =
|
let alice_account = TestLogosAccount::new("alice");
|
||||||
Core::new_with_name("alice", ds.clone(), rs.clone(), ChatStorage::in_memory()).unwrap();
|
let mut alice = Core::new_with_name(
|
||||||
let mut bob = Core::new_with_name("bob", ds, rs, ChatStorage::in_memory()).unwrap();
|
alice_account,
|
||||||
|
ds.clone(),
|
||||||
|
rs.clone(),
|
||||||
|
ChatStorage::in_memory(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let bob_account = TestLogosAccount::new("bob");
|
||||||
|
let mut bob = Core::new_with_name(bob_account, ds, rs, ChatStorage::in_memory()).unwrap();
|
||||||
|
|
||||||
let bundle = alice.create_intro_bundle().unwrap();
|
let bundle = alice.create_intro_bundle().unwrap();
|
||||||
let intro = Introduction::try_from(bundle.as_slice()).unwrap();
|
let intro = Introduction::try_from(bundle.as_slice()).unwrap();
|
||||||
|
|||||||
8
core/shared-traits/Cargo.toml
Normal file
8
core/shared-traits/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "shared-traits"
|
||||||
|
description = "Shared traits for the Logos Ecosystem"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
crypto = { workspace = true }
|
||||||
39
core/shared-traits/src/lib.rs
Normal file
39
core/shared-traits/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use crypto::{Ed25519Signature, Ed25519VerifyingKey};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct IdentId(String);
|
||||||
|
pub type IdentIdRef<'a> = &'a IdentId;
|
||||||
|
|
||||||
|
impl IdentId {
|
||||||
|
pub fn new(id: impl Into<String>) -> Self {
|
||||||
|
Self(id.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for IdentId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for IdentId {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an external Identity
|
||||||
|
/// Implement this to provide an Authentication model for users/installations
|
||||||
|
pub trait IdentityProvider {
|
||||||
|
fn id(&self) -> IdentIdRef<'_>;
|
||||||
|
// Display name is not garenteed to be consistent. It should only be used to
|
||||||
|
// provded a more readable identifier for the account.
|
||||||
|
fn display_name(&self) -> String;
|
||||||
|
fn sign(&self, payload: &[u8]) -> Ed25519Signature;
|
||||||
|
fn public_key(&self) -> &Ed25519VerifyingKey;
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ crate-type = ["rlib"]
|
|||||||
chat-sqlite = { workspace = true }
|
chat-sqlite = { workspace = true }
|
||||||
components = { workspace = true}
|
components = { workspace = true}
|
||||||
libchat = { workspace = true }
|
libchat = { workspace = true }
|
||||||
|
logos-account = { workspace = true, features = ["dev"]}
|
||||||
|
|
||||||
# External dependencies (sorted)
|
# External dependencies (sorted)
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
|
|||||||
@ -6,12 +6,13 @@ use libchat::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use components::EphemeralRegistry;
|
use components::EphemeralRegistry;
|
||||||
|
use logos_account::TestLogosAccount;
|
||||||
|
|
||||||
use crate::errors::ClientError;
|
use crate::errors::ClientError;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
|
||||||
pub struct ChatClient<D: DeliveryService, R: RegistrationService = EphemeralRegistry> {
|
pub struct ChatClient<D: DeliveryService, R: RegistrationService = EphemeralRegistry> {
|
||||||
core: Core<(D, R, ChatStorage)>,
|
core: Core<(TestLogosAccount, D, R, ChatStorage)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Default-registry constructors ────────────────────────────────────────────
|
// ── Default-registry constructors ────────────────────────────────────────────
|
||||||
@ -21,8 +22,9 @@ impl<D: DeliveryService + 'static> ChatClient<D, EphemeralRegistry> {
|
|||||||
pub fn new(name: impl Into<String>, delivery: D) -> Self {
|
pub fn new(name: impl Into<String>, delivery: D) -> Self {
|
||||||
let registry = EphemeralRegistry::new();
|
let registry = EphemeralRegistry::new();
|
||||||
let store = ChatStorage::in_memory();
|
let store = ChatStorage::in_memory();
|
||||||
|
let ident = TestLogosAccount::new(name);
|
||||||
Self {
|
Self {
|
||||||
core: Core::new_with_name(name, delivery, registry, store).unwrap(),
|
core: Core::new_with_name(ident, delivery, registry, store).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +39,8 @@ impl<D: DeliveryService + 'static> ChatClient<D, EphemeralRegistry> {
|
|||||||
) -> Result<Self, ClientError> {
|
) -> Result<Self, ClientError> {
|
||||||
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
||||||
let registry = EphemeralRegistry::new();
|
let registry = EphemeralRegistry::new();
|
||||||
let core = Core::new_from_store(name, delivery, registry, store)?;
|
let ident = TestLogosAccount::new(name);
|
||||||
|
let core = Core::new_from_store(ident, delivery, registry, store)?;
|
||||||
Ok(Self { core })
|
Ok(Self { core })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,7 +67,9 @@ where
|
|||||||
registry: R,
|
registry: R,
|
||||||
) -> Result<Self, ClientError> {
|
) -> Result<Self, ClientError> {
|
||||||
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
||||||
let mut core = Core::new_from_store(name, delivery, registry, store)?;
|
|
||||||
|
let ident = TestLogosAccount::new(name);
|
||||||
|
let mut core = Core::new_from_store(ident, delivery, registry, store)?;
|
||||||
core.register_keypackage()?;
|
core.register_keypackage()?;
|
||||||
Ok(Self { core })
|
Ok(Self { core })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ impl RegistrationService for EphemeralRegistry {
|
|||||||
self.registry
|
self.registry
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(identity.account_id().to_string(), key_bundle);
|
.insert(identity.id().to_string(), key_bundle);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user