mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-08-27 08:01:09 +00:00
fix: signer-scoped DirectV1 routing (#162)
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_delegate_signer does the lamport upsert and signs internally. - Every client acts for an account (ChatClientBuilder::new(account)). DelegateSigner is a pure keypair; the client composes the wire credential from the signer and the account, so the association is client state. addr() is the account address. - resolve_device_ids fails fast (NotAnAccountKey / NoDeviceBundle / Directory) instead of falling back to treating an unresolved address as a signer id. LogosChatClient::open and chat-cli mint and publish a dev account each launch. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
This commit is contained in:
@@ -10,8 +10,8 @@ edition = "2024"
|
||||
# Workspace dependencies (sorted)
|
||||
chat-sqlite = { workspace = true }
|
||||
components = { workspace = true }
|
||||
crypto = { workspace = true }
|
||||
libchat = { workspace = true }
|
||||
logos-account = { workspace = true, features = ["dev"]}
|
||||
shared-traits = { workspace = true }
|
||||
|
||||
# External dependencies (sorted)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod test_client;
|
||||
mod test_ident;
|
||||
mod wakeup;
|
||||
|
||||
pub use test_client::TestHarness;
|
||||
pub use test_ident::TestIdent;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::test_ident::TestIdent;
|
||||
use libchat::{ConversationId, Core, IdentityProvider, PayloadOutcome};
|
||||
use logos_account::TestLogosAccount;
|
||||
use shared_traits::IdentId;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
@@ -21,14 +21,8 @@ const RAYA: usize = 1;
|
||||
const PAX: usize = 2;
|
||||
const MIRA: usize = 3;
|
||||
|
||||
// type ClientType = CoreClient<TestLogosAccount, LocalBroadcaster, EphemeralRegistry, WP, MemStore>;
|
||||
type ClientType = Core<(
|
||||
TestLogosAccount,
|
||||
LocalBroadcaster,
|
||||
EphemeralRegistry,
|
||||
WP,
|
||||
MemStore,
|
||||
)>;
|
||||
// type ClientType = CoreClient<TestIdent, LocalBroadcaster, EphemeralRegistry, WP, MemStore>;
|
||||
type ClientType = Core<(TestIdent, LocalBroadcaster, EphemeralRegistry, WP, MemStore)>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReceivedMessage<T> {
|
||||
@@ -151,7 +145,7 @@ impl<const N: usize> TestHarness<N> {
|
||||
|
||||
for i in 0..N {
|
||||
let wp = ws.new_provider(i);
|
||||
let ident = TestLogosAccount::new(Self::names(i));
|
||||
let ident = TestIdent::new(Self::names(i));
|
||||
|
||||
addresses.insert(i, ident.id().clone());
|
||||
let core_client =
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
|
||||
use libchat::IdentityProvider;
|
||||
use shared_traits::{IdentId, IdentIdRef};
|
||||
|
||||
/// Test identity with a fixed, human-readable id ("saro"). Stands in for a
|
||||
/// device signer so core tests can address peers by name.
|
||||
pub struct TestIdent {
|
||||
id: IdentId,
|
||||
signing_key: Ed25519SigningKey,
|
||||
verifying_key: Ed25519VerifyingKey,
|
||||
}
|
||||
|
||||
impl TestIdent {
|
||||
pub fn new(explicit_id: impl Into<String>) -> Self {
|
||||
let signing_key = Ed25519SigningKey::generate();
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
Self {
|
||||
id: IdentId::new(explicit_id.into()),
|
||||
signing_key,
|
||||
verifying_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentityProvider for TestIdent {
|
||||
fn id(&self) -> IdentIdRef<'_> {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn display_name(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
|
||||
fn public_key(&self) -> &Ed25519VerifyingKey {
|
||||
&self.verifying_key
|
||||
}
|
||||
|
||||
fn sign(&self, payload: &[u8]) -> crypto::Ed25519Signature {
|
||||
self.signing_key.sign(payload)
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
||||
use integration_tests_core::TestIdent;
|
||||
use libchat::{Core, MissingMessage, WakeupService};
|
||||
use logos_account::TestLogosAccount;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NoopWakeupService {}
|
||||
@@ -18,7 +18,7 @@ impl WakeupService for NoopWakeupService {
|
||||
|
||||
struct Client {
|
||||
inner: Core<(
|
||||
TestLogosAccount,
|
||||
TestIdent,
|
||||
LocalBroadcaster,
|
||||
EphemeralRegistry,
|
||||
NoopWakeupService,
|
||||
@@ -29,7 +29,7 @@ struct Client {
|
||||
impl Client {
|
||||
fn init(
|
||||
core: Core<(
|
||||
TestLogosAccount,
|
||||
TestIdent,
|
||||
LocalBroadcaster,
|
||||
EphemeralRegistry,
|
||||
NoopWakeupService,
|
||||
@@ -60,7 +60,7 @@ impl Client {
|
||||
|
||||
impl Deref for Client {
|
||||
type Target = Core<(
|
||||
TestLogosAccount,
|
||||
TestIdent,
|
||||
LocalBroadcaster,
|
||||
EphemeralRegistry,
|
||||
NoopWakeupService,
|
||||
@@ -82,9 +82,9 @@ fn missing_group_message_is_detected() {
|
||||
let ds = LocalBroadcaster::new();
|
||||
let rs = EphemeralRegistry::new();
|
||||
|
||||
let saro_account = TestLogosAccount::new("saro");
|
||||
let saro_ident = TestIdent::new("saro");
|
||||
let saro_ctx = Core::new_with_name(
|
||||
saro_account,
|
||||
saro_ident,
|
||||
ds.new_consumer(),
|
||||
rs.clone(),
|
||||
NoopWakeupService {},
|
||||
@@ -92,9 +92,9 @@ fn missing_group_message_is_detected() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let raya_account = TestLogosAccount::new("raya");
|
||||
let raya_ident = TestIdent::new("raya");
|
||||
let raya_ctx = Core::new_with_name(
|
||||
raya_account,
|
||||
raya_ident,
|
||||
ds.clone(),
|
||||
rs.clone(),
|
||||
NoopWakeupService {},
|
||||
@@ -135,9 +135,11 @@ fn missing_group_message_is_detected() {
|
||||
!missing[0].frontier.message_id().is_empty(),
|
||||
"the missing message must be identified"
|
||||
);
|
||||
// The causal sender hint carries the sender's identity id ("saro"), not
|
||||
// the signer id the inbox and registry key on.
|
||||
assert_eq!(
|
||||
missing[0].frontier.sender_id(),
|
||||
saro.ident_id().as_str(),
|
||||
"saro",
|
||||
"missing-message sender hint should attribute to Saro"
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use chat_sqlite::{ChatStorage, StorageConfig};
|
||||
use integration_tests_core::TestIdent;
|
||||
use libchat::{ConversationClass, Core, Introduction, PayloadOutcome, WakeupService};
|
||||
use logos_account::TestLogosAccount;
|
||||
use storage::{ConversationStore, IdentityStore};
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -13,7 +13,7 @@ impl WakeupService for NoopWakeupService {
|
||||
}
|
||||
|
||||
type PrivateCore = Core<(
|
||||
TestLogosAccount,
|
||||
TestIdent,
|
||||
LocalBroadcaster,
|
||||
EphemeralRegistry,
|
||||
NoopWakeupService,
|
||||
@@ -62,18 +62,18 @@ fn ctx_integration() {
|
||||
let ds = LocalBroadcaster::new();
|
||||
let rs = EphemeralRegistry::new();
|
||||
|
||||
let saro_account = TestLogosAccount::new("saro");
|
||||
let saro_ident = TestIdent::new("saro");
|
||||
let mut saro = Core::new_with_name(
|
||||
saro_account,
|
||||
saro_ident,
|
||||
ds.clone(),
|
||||
rs.clone(),
|
||||
NoopWakeupService {},
|
||||
ChatStorage::in_memory(),
|
||||
)
|
||||
.unwrap();
|
||||
let raya_account = TestLogosAccount::new("raya");
|
||||
let raya_ident = TestIdent::new("raya");
|
||||
let mut raya = Core::new_with_name(
|
||||
raya_account,
|
||||
raya_ident,
|
||||
ds,
|
||||
rs,
|
||||
NoopWakeupService {},
|
||||
@@ -121,8 +121,8 @@ fn identity_persistence() {
|
||||
let ds = LocalBroadcaster::new();
|
||||
let rs = EphemeralRegistry::new();
|
||||
let store1 = ChatStorage::new(StorageConfig::InMemory).unwrap();
|
||||
let alice_account = TestLogosAccount::new("alice");
|
||||
let ctx1 = Core::new_with_name(alice_account, ds, rs, NoopWakeupService {}, store1).unwrap();
|
||||
let alice_ident = TestIdent::new("alice");
|
||||
let ctx1 = Core::new_with_name(alice_ident, ds, rs, NoopWakeupService {}, store1).unwrap();
|
||||
let pubkey1 = ctx1.identity().public_key();
|
||||
let name1 = ctx1.installation_name().to_string();
|
||||
|
||||
@@ -141,8 +141,8 @@ fn open_persists_new_identity() {
|
||||
let ds = LocalBroadcaster::new();
|
||||
let rs = EphemeralRegistry::new();
|
||||
let store = ChatStorage::new(StorageConfig::File(db_path.clone())).unwrap();
|
||||
let alice_account = TestLogosAccount::new("alice");
|
||||
let core = Core::new_from_store(alice_account, ds, rs, NoopWakeupService {}, store).unwrap();
|
||||
let alice_ident = TestIdent::new("alice");
|
||||
let core = Core::new_from_store(alice_ident, ds, rs, NoopWakeupService {}, store).unwrap();
|
||||
let pubkey = core.identity().public_key();
|
||||
drop(core);
|
||||
|
||||
@@ -157,18 +157,18 @@ fn open_persists_new_identity() {
|
||||
fn conversation_metadata_persistence() {
|
||||
let ds = LocalBroadcaster::new();
|
||||
let rs = EphemeralRegistry::new();
|
||||
let alice_account = TestLogosAccount::new("alice");
|
||||
let alice_ident = TestIdent::new("alice");
|
||||
let mut alice = Core::new_with_name(
|
||||
alice_account,
|
||||
alice_ident,
|
||||
ds.clone(),
|
||||
rs.clone(),
|
||||
NoopWakeupService {},
|
||||
ChatStorage::in_memory(),
|
||||
)
|
||||
.unwrap();
|
||||
let bob_account = TestLogosAccount::new("bob");
|
||||
let bob_ident = TestIdent::new("bob");
|
||||
let mut bob = Core::new_with_name(
|
||||
bob_account,
|
||||
bob_ident,
|
||||
ds,
|
||||
rs,
|
||||
NoopWakeupService {},
|
||||
@@ -198,18 +198,18 @@ fn conversation_metadata_persistence() {
|
||||
fn conversation_full_flow() {
|
||||
let ds = LocalBroadcaster::new();
|
||||
let rs = EphemeralRegistry::new();
|
||||
let alice_account = TestLogosAccount::new("alice");
|
||||
let alice_ident = TestIdent::new("alice");
|
||||
let mut alice = Core::new_with_name(
|
||||
alice_account,
|
||||
alice_ident,
|
||||
ds.clone(),
|
||||
rs.clone(),
|
||||
NoopWakeupService {},
|
||||
ChatStorage::in_memory(),
|
||||
)
|
||||
.unwrap();
|
||||
let bob_account = TestLogosAccount::new("bob");
|
||||
let bob_ident = TestIdent::new("bob");
|
||||
let mut bob = Core::new_with_name(
|
||||
bob_account,
|
||||
bob_ident,
|
||||
ds,
|
||||
rs,
|
||||
NoopWakeupService {},
|
||||
|
||||
Reference in New Issue
Block a user