pr feedback and test fix

This commit is contained in:
pablo 2026-03-27 10:43:29 +01:00
parent ecd1be3b9e
commit 14a403399e
No known key found for this signature in database
GPG Key ID: 78F35FCC60FDC63A
6 changed files with 57 additions and 22 deletions

View File

@ -47,11 +47,13 @@ pub struct ContextHandle(pub(crate) Context);
/// Creates a new libchat Ctx
///
/// # Returns
/// Opaque handle to the store. Must be freed with destroy_context()
/// Opaque handle to the store. Must be freed with destroy_context().
/// Uses lossy UTF-8 conversion: invalid bytes are replaced with U+FFFD
/// so the caller always gets a deterministic name reflecting their input.
#[ffi_export]
pub fn create_context(name: c_slice::Ref<'_, u8>) -> repr_c::Box<ContextHandle> {
let name_str = std::str::from_utf8(name.as_slice()).unwrap_or("default");
Box::new(ContextHandle(Context::new_with_name(name_str))).into()
let name_str = std::string::String::from_utf8_lossy(name.as_slice());
Box::new(ContextHandle(Context::new_with_name(&name_str))).into()
}
/// Returns the friendly name of the contexts installation.
@ -193,7 +195,16 @@ pub fn send_content(
content: c_slice::Ref<'_, u8>,
out: &mut SendContentResult,
) {
let convo_id_str = std::str::from_utf8(convo_id.as_slice()).unwrap_or("");
let convo_id_str = match std::str::from_utf8(convo_id.as_slice()) {
Ok(s) => s,
Err(_) => {
*out = SendContentResult {
error_code: ErrorCode::BadConvoId as i32,
payloads: safer_ffi::Vec::EMPTY,
};
return;
}
};
let payloads = match ctx.0.send_content(convo_id_str, &content) {
Ok(p) => p,
Err(_) => {

View File

@ -60,7 +60,11 @@ pub struct PrivateV1Convo {
}
impl PrivateV1Convo {
pub fn new_initiator(seed_key: SymmetricKey32, remote: PublicKey, remote_delivery_address: String) -> Self {
pub fn new_initiator(
seed_key: SymmetricKey32,
remote: PublicKey,
remote_delivery_address: String,
) -> Self {
let base_convo_id = BaseConvoId::new(&seed_key);
let local_convo_id = base_convo_id.id_for_participant(Role::Initiator);
let remote_convo_id = base_convo_id.id_for_participant(Role::Responder);
@ -79,7 +83,11 @@ impl PrivateV1Convo {
}
}
pub fn new_responder(seed_key: SymmetricKey32, dh_self: &PrivateKey, remote_delivery_address: String) -> Self {
pub fn new_responder(
seed_key: SymmetricKey32,
dh_self: &PrivateKey,
remote_delivery_address: String,
) -> Self {
let base_convo_id = BaseConvoId::new(&seed_key);
let local_convo_id = base_convo_id.id_for_participant(Role::Responder);
let remote_convo_id = base_convo_id.id_for_participant(Role::Initiator);
@ -239,7 +247,8 @@ mod tests {
let seed_key_saro = SymmetricKey32::from(seed_key);
let seed_key_raya = SymmetricKey32::from(seed_key);
let send_content_bytes = vec![0, 2, 4, 6, 8];
let mut sr_convo = PrivateV1Convo::new_initiator(seed_key_saro, pub_raya, "test_addr".into());
let mut sr_convo =
PrivateV1Convo::new_initiator(seed_key_saro, pub_raya, "test_addr".into());
let mut rs_convo = PrivateV1Convo::new_responder(seed_key_raya, &raya, "test_addr".into());
let send_frame = PrivateV1Frame {

View File

@ -72,8 +72,13 @@ impl Inbox {
let (seed_key, ephemeral_pub) =
InboxHandshake::perform_as_initiator(self.ident.secret(), &pkb, &mut rng);
let remote_delivery_addr = Inbox::inbox_identifier_for_key(*remote_bundle.installation_key());
let mut convo = PrivateV1Convo::new_initiator(seed_key, *remote_bundle.ephemeral_key(), remote_delivery_addr.clone());
let remote_delivery_addr =
Inbox::inbox_identifier_for_key(*remote_bundle.installation_key());
let mut convo = PrivateV1Convo::new_initiator(
seed_key,
*remote_bundle.ephemeral_key(),
remote_delivery_addr.clone(),
);
let mut payloads = convo.send_message(initial_message)?;
@ -119,17 +124,21 @@ impl Inbox {
let ephemeral_key = self.lookup_ephemeral_key(&key_index)?;
// Extract initiator's identity key for delivery address before header is consumed
let initiator_static_bytes: [u8; 32] = header.initiator_static.as_ref()
let initiator_static_bytes: [u8; 32] = header
.initiator_static
.as_ref()
.try_into()
.map_err(|_| ChatError::BadBundleValue("wrong size - initiator static".into()))?;
let remote_delivery_addr = Inbox::inbox_identifier_for_key(PublicKey::from(initiator_static_bytes));
let remote_delivery_addr =
Inbox::inbox_identifier_for_key(PublicKey::from(initiator_static_bytes));
// Perform handshake and decrypt frame
let (seed_key, frame) = self.perform_handshake(ephemeral_key, header, handshake.payload)?;
match frame.frame_type.unwrap() {
proto::inbox_v1_frame::FrameType::InvitePrivateV1(_invite_private_v1) => {
let mut convo = PrivateV1Convo::new_responder(seed_key, ephemeral_key, remote_delivery_addr);
let mut convo =
PrivateV1Convo::new_responder(seed_key, ephemeral_key, remote_delivery_addr);
let Some(enc_payload) = _invite_private_v1.initial_message else {
return Err(ChatError::Protocol("missing initial encpayload".into()));

View File

@ -51,18 +51,24 @@
doCheck = false; # tests require network access unavailable in nix sandbox
postBuild = ''
cargo run --release --bin generate-libchat-headers --features headers
cargo run --frozen --release --bin generate-libchat-headers --features headers
'';
installPhase = ''
runHook preInstall
mkdir -p $out/lib $out/include
# Copy shared library
# Copy shared library (platform-dependent extension)
cp target/release/liblibchat.so $out/lib/ 2>/dev/null || true
cp target/release/liblibchat.dylib $out/lib/ 2>/dev/null || true
cp target/release/liblibchat.a $out/lib/ 2>/dev/null || true
# Fail if no library was produced
if [ -z "$(ls $out/lib/liblibchat.* 2>/dev/null)" ]; then
echo "ERROR: No library artifact found in target/release/"
exit 1
fi
# Copy generated header
cp libchat.h $out/include/

View File

@ -85,7 +85,7 @@ type
## Creates a new libchat Context
## Returns: Opaque handle to the context. Must be freed with destroy_context()
proc create_context*(name: ReprCString): ContextHandle {.importc.}
proc create_context*(name: SliceUint8): ContextHandle {.importc.}
## Returns the friendly name of the context's identity
## The result must be freed by the caller (repr_c::String ownership transfers)
@ -129,7 +129,7 @@ proc list_conversations*(
## The result must be freed with destroy_send_content_result()
proc send_content*(
ctx: ContextHandle,
convo_id: ReprCString,
convo_id: SliceUint8,
content: SliceUint8,
): SendContentResult {.importc.}

View File

@ -72,7 +72,7 @@ proc testHelperProcs() =
proc testContextLifecycle() =
echo "\n--- testContextLifecycle ---"
let ctx = create_context(toReprCString("lifecycle-test"))
let ctx = create_context(toSlice("lifecycle-test"))
check(ctx != nil, "create_context: returns non-nil handle")
let iname = installation_name(ctx)
@ -94,10 +94,10 @@ proc testContextLifecycle() =
proc testFullConversationFlow() =
echo "\n--- testFullConversationFlow ---"
let aliceCtx = create_context(toReprCString("alice"))
let aliceCtx = create_context(toSlice("alice"))
check(aliceCtx != nil, "Alice: create_context non-nil")
let bobCtx = create_context(toReprCString("bob"))
let bobCtx = create_context(toSlice("bob"))
check(bobCtx != nil, "Bob: create_context non-nil")
# --- create_intro_bundle ---
@ -177,7 +177,7 @@ proc testFullConversationFlow() =
# --- send_content ---
var sendRes = send_content(
aliceCtx,
toReprCString(aliceConvoId),
toSlice(aliceConvoId),
toSlice("How are you, Bob?")
)
check(sendRes.error_code == ErrNone,
@ -213,13 +213,13 @@ proc testFullConversationFlow() =
proc testErrorCases() =
echo "\n--- testErrorCases ---"
let ctx = create_context(toReprCString("error-tester"))
let ctx = create_context(toSlice("error-tester"))
check(ctx != nil, "error-tester: create_context non-nil")
# send_content with a nonexistent convo_id must fail
var badSend = send_content(
ctx,
toReprCString("00000000-0000-0000-0000-nonexistent"),
toSlice("00000000-0000-0000-0000-nonexistent"),
toSlice("payload")
)
check(badSend.error_code != ErrNone,