lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit b455d786a6cc9c5c7895f8b05f50f2dcb438863c
parent ab2a6fbc62609100e88042a7d7c305b753872b0e
Author: triesap <tyson@radroots.org>
Date:   Tue, 23 Jun 2026 06:30:54 +0000

simplex: create short link invitations

Diffstat:
Mcrates/simplex_agent_runtime/src/runtime.rs | 255++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mcrates/simplex_agent_runtime/src/types.rs | 4++--
Mcrates/simplex_agent_store/src/store.rs | 5+++++
Mcrates/simplex_chat_proto/src/codec.rs | 1+
Mcrates/simplex_chat_proto/src/model.rs | 1+
Mcrates/simplex_interop_tests/src/lib.rs | 70+++++++++++++++++++++++++++++-----------------------------------------
Mcrates/simplex_interop_tests/src/policy.rs | 4+++-
7 files changed, 276 insertions(+), 64 deletions(-)

diff --git a/crates/simplex_agent_runtime/src/runtime.rs b/crates/simplex_agent_runtime/src/runtime.rs @@ -2,7 +2,8 @@ use crate::error::RadrootsSimplexAgentRuntimeError; use crate::types::{RadrootsSimplexAgentCommandOutcome, RadrootsSimplexAgentRuntimeEvent}; use alloc::collections::VecDeque; use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; +use alloc::vec; use alloc::vec::Vec; use base64::Engine as _; use base64::engine::general_purpose::{URL_SAFE, URL_SAFE_NO_PAD}; @@ -13,32 +14,38 @@ use radroots_simplex_agent_proto::prelude::{ RadrootsSimplexAgentMessage, RadrootsSimplexAgentMessageFrame, RadrootsSimplexAgentMessageHeader, RadrootsSimplexAgentMessageReceipt, RadrootsSimplexAgentQueueAddress, RadrootsSimplexAgentQueueDescriptor, - decode_decrypted_message, decode_envelope, encode_decrypted_message, encode_envelope, + RadrootsSimplexAgentShortLinkScheme, decode_decrypted_message, decode_envelope, + encode_connection_link, encode_decrypted_message, encode_envelope, }; use radroots_simplex_agent_store::prelude::{ RadrootsSimplexAgentOutboundMessage, RadrootsSimplexAgentPendingCommand, RadrootsSimplexAgentPendingCommandKind, RadrootsSimplexAgentPqKeypair, - RadrootsSimplexAgentQueueRole, RadrootsSimplexAgentStore, RadrootsSimplexAgentX3dhKeypair, + RadrootsSimplexAgentQueueRole, RadrootsSimplexAgentShortLinkCredentials, + RadrootsSimplexAgentStore, RadrootsSimplexAgentX3dhKeypair, }; use radroots_simplex_smp_crypto::prelude::{ RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION, RADROOTS_SIMPLEX_SMP_NONCE_LENGTH, RadrootsSimplexOfficialSntrup761Keypair, RadrootsSimplexOfficialX3dhParams, RadrootsSimplexOfficialX448Keypair, RadrootsSimplexSmpCommandAuthorization, RadrootsSimplexSmpCryptoError, - RadrootsSimplexSmpRatchetState, RadrootsSimplexSmpX25519Keypair, decode_x25519_public_key_x509, - decrypt_padded, derive_shared_secret, encode_ed25519_public_key_x509, - encode_x25519_public_key_x509, encrypt_padded, official_sntrup761_keypair_from_seed, - official_x3dh_receiver_init, official_x3dh_receiver_init_accepting_pq, - official_x3dh_sender_init, official_x3dh_sender_init_accepting_pq, - official_x448_keypair_from_seed, random_nonce, + RadrootsSimplexSmpEd25519Keypair, RadrootsSimplexSmpRatchetState, + RadrootsSimplexSmpX25519Keypair, decode_x25519_public_key_x509, decrypt_padded, + derive_invitation_short_link_data_key, derive_shared_secret, encode_ed25519_public_key_x509, + encode_x25519_public_key_x509, encrypt_padded, encrypt_short_link_data, + official_sntrup761_keypair_from_seed, official_x3dh_receiver_init, + official_x3dh_receiver_init_accepting_pq, official_x3dh_sender_init, + official_x3dh_sender_init_accepting_pq, official_x448_keypair_from_seed, random_nonce, + sign_short_link_data, }; use radroots_simplex_smp_proto::prelude::{ RADROOTS_SIMPLEX_SMP_CURRENT_CLIENT_VERSION, RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, RadrootsSimplexSmpBrokerMessage, RadrootsSimplexSmpCommand, RadrootsSimplexSmpCorrelationId, - RadrootsSimplexSmpMessageFlags, RadrootsSimplexSmpNewQueueRequest, - RadrootsSimplexSmpQueueIdsResponse, RadrootsSimplexSmpQueueMode, + RadrootsSimplexSmpMessageFlags, RadrootsSimplexSmpMessagingQueueRequest, + RadrootsSimplexSmpNewQueueRequest, RadrootsSimplexSmpQueueIdsResponse, + RadrootsSimplexSmpQueueLinkData, RadrootsSimplexSmpQueueMode, RadrootsSimplexSmpQueueRequestData, RadrootsSimplexSmpQueueUri, RadrootsSimplexSmpSendCommand, - RadrootsSimplexSmpSubscriptionMode, RadrootsSimplexSmpVersionRange, + RadrootsSimplexSmpServerAddress, RadrootsSimplexSmpSubscriptionMode, + RadrootsSimplexSmpVersionRange, }; use radroots_simplex_smp_transport::prelude::{ RadrootsSimplexSmpCommandTransport, RadrootsSimplexSmpSubscriptionReceiveRequest, @@ -76,6 +83,13 @@ struct SimplexReceivedBody { sent_body: Vec<u8>, } +struct SimplexPreparedShortInvitationLinkData { + link_key: Vec<u8>, + link_public_signature_key: Vec<u8>, + link_private_signature_key: Vec<u8>, + encrypted_link_data: RadrootsSimplexSmpQueueLinkData, +} + pub struct RadrootsSimplexAgentRuntimeBuilder { store: Option<RadrootsSimplexAgentStore>, queue_capacity: usize, @@ -234,6 +248,11 @@ impl RadrootsSimplexAgentRuntime { e2e_ratchet_params, contact_address, }; + let prepared_short_link = if contact_address { + None + } else { + Some(prepare_short_invitation_link_data(&invitation)?) + }; self.store.connection_mut(&connection.id)?.invitation = Some(invitation); let receive_auth_state = self.store.generate_queue_auth_state()?; let delivery_keypair = RadrootsSimplexSmpX25519Keypair::generate() @@ -258,6 +277,19 @@ impl RadrootsSimplexAgentRuntime { connection.local_x3dh_key_1 = Some(agent_x3dh_keypair(x3dh_key_1)); connection.local_x3dh_key_2 = Some(agent_x3dh_keypair(x3dh_key_2)); connection.local_pq_keypair = Some(agent_pq_keypair(pq_keypair)); + connection.short_link = + prepared_short_link.map(|prepared| RadrootsSimplexAgentShortLinkCredentials { + scheme: RadrootsSimplexAgentShortLinkScheme::Simplex, + hosts: descriptor.queue_uri.server.hosts.clone(), + port: descriptor.queue_uri.server.port, + server_key_hash: None, + link_id: Vec::new(), + link_key: prepared.link_key, + link_public_signature_key: prepared.link_public_signature_key, + link_private_signature_key: prepared.link_private_signature_key, + encrypted_fixed_data: Some(prepared.encrypted_link_data.fixed_data), + encrypted_user_data: Some(prepared.encrypted_link_data.user_data), + }); let queue = connection .queues .iter_mut() @@ -1011,6 +1043,29 @@ impl RadrootsSimplexAgentRuntime { &self, command: &RadrootsSimplexAgentPendingCommand, ) -> Result<RadrootsSimplexSmpTransportRequest, RadrootsSimplexAgentRuntimeError> { + match &command.kind { + RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData { + server, + link_id, + link_key, + } => { + return Ok(self.server_transport_request( + command.id, + server, + link_id.clone(), + RadrootsSimplexSmpCommand::LKey(link_key.clone()), + )); + } + RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { server, link_id } => { + return Ok(self.server_transport_request( + command.id, + server, + link_id.clone(), + RadrootsSimplexSmpCommand::LGet, + )); + } + _ => {} + } let (queue_address, entity_id, smp_command) = self.command_transport_parts(command)?; let queue = self .store @@ -1050,6 +1105,23 @@ impl RadrootsSimplexAgentRuntime { }) } + fn server_transport_request( + &self, + command_id: u64, + server: &RadrootsSimplexSmpServerAddress, + entity_id: Vec<u8>, + command: RadrootsSimplexSmpCommand, + ) -> RadrootsSimplexSmpTransportRequest { + RadrootsSimplexSmpTransportRequest { + server: server.clone(), + transport_version: RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, + correlation_id: Some(correlation_id_for_command(command_id)), + entity_id, + command, + authorization: RadrootsSimplexSmpCommandAuthorization::None, + } + } + fn command_transport_parts( &self, command: &RadrootsSimplexAgentPendingCommand, @@ -1105,7 +1177,12 @@ impl RadrootsSimplexAgentRuntime { .unwrap_or(RadrootsSimplexSmpQueueMode::Messaging) { RadrootsSimplexSmpQueueMode::Messaging => { - RadrootsSimplexSmpQueueRequestData::Messaging(None) + RadrootsSimplexSmpQueueRequestData::Messaging( + self.short_link_messaging_queue_request( + &command.connection_id, + descriptor, + )?, + ) } RadrootsSimplexSmpQueueMode::Contact => { RadrootsSimplexSmpQueueRequestData::Contact(None) @@ -1182,9 +1259,62 @@ impl RadrootsSimplexAgentRuntime { let entity_id = address.sender_id.clone(); Ok((address, entity_id, RadrootsSimplexSmpCommand::Ping)) } + RadrootsSimplexAgentPendingCommandKind::SetQueueLinkData { + queue, + link_id, + link_data, + } => Ok(( + queue.clone(), + self.store + .queue_record(&command.connection_id, queue)? + .entity_id, + RadrootsSimplexSmpCommand::LSet { + link_id: link_id.clone(), + link_data: link_data.clone(), + }, + )), + RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData { .. } + | RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { .. } => { + Err(RadrootsSimplexAgentRuntimeError::Runtime( + "SimpleX short-link retrieval commands require server transport dispatch" + .into(), + )) + } } } + fn short_link_messaging_queue_request( + &self, + connection_id: &str, + descriptor: &RadrootsSimplexAgentQueueDescriptor, + ) -> Result<Option<RadrootsSimplexSmpMessagingQueueRequest>, RadrootsSimplexAgentRuntimeError> + { + let connection = self.store.connection(connection_id)?; + if connection.status != RadrootsSimplexAgentConnectionStatus::CreatePending { + return Ok(None); + } + let Some(short_link) = connection.short_link.as_ref() else { + return Ok(None); + }; + let fixed_data = short_link.encrypted_fixed_data.clone().ok_or_else(|| { + RadrootsSimplexAgentRuntimeError::Runtime(format!( + "SimpleX connection `{connection_id}` is missing encrypted short-link fixed data" + )) + })?; + let user_data = short_link.encrypted_user_data.clone().ok_or_else(|| { + RadrootsSimplexAgentRuntimeError::Runtime(format!( + "SimpleX connection `{connection_id}` is missing encrypted short-link user data" + )) + })?; + Ok(Some(RadrootsSimplexSmpMessagingQueueRequest { + sender_id: descriptor.queue_address().sender_id, + link_data: RadrootsSimplexSmpQueueLinkData { + fixed_data, + user_data, + }, + })) + } + fn apply_transport_response( &mut self, command: &RadrootsSimplexAgentPendingCommand, @@ -1471,7 +1601,17 @@ impl RadrootsSimplexAgentRuntime { connection.status = RadrootsSimplexAgentConnectionStatus::InvitationReady; if let Some(invitation) = connection.invitation.as_mut() { invitation.invitation_queue = queue.descriptor.queue_uri.clone(); - invitation_event = Some(invitation.clone()); + } + if let Some(short_link) = connection.short_link.as_mut() { + short_link.link_id = ids.link_id.clone().ok_or_else(|| { + RadrootsSimplexAgentRuntimeError::Runtime(format!( + "SimpleX broker IDS response for `{}` did not include a short-link id", + command.connection_id + )) + })?; + short_link.hosts = queue.descriptor.queue_uri.server.hosts.clone(); + short_link.port = queue.descriptor.queue_uri.server.port; + invitation_event = Some(short_link.invitation_link()); } } else if connection.status == RadrootsSimplexAgentConnectionStatus::JoinPending { let local_x3dh_key_1 = connection.local_x3dh_key_1.as_ref().ok_or_else(|| { @@ -1934,6 +2074,27 @@ fn official_x3dh_params_from_parts( }) } +fn prepare_short_invitation_link_data( + invitation: &RadrootsSimplexAgentConnectionLink, +) -> Result<SimplexPreparedShortInvitationLinkData, RadrootsSimplexAgentRuntimeError> { + let root_keypair = RadrootsSimplexSmpEd25519Keypair::generate() + .map_err(|error| RadrootsSimplexAgentRuntimeError::Runtime(error.to_string()))?; + let fixed_data = encode_connection_link(invitation)?; + let user_data = invitation.connection_id.clone(); + let (link_key, signed_link_data) = sign_short_link_data(&root_keypair, &fixed_data, &user_data) + .map_err(|error| RadrootsSimplexAgentRuntimeError::Runtime(error.to_string()))?; + let link_data_key = derive_invitation_short_link_data_key(&link_key) + .map_err(|error| RadrootsSimplexAgentRuntimeError::Runtime(error.to_string()))?; + let encrypted_link_data = encrypt_short_link_data(&link_data_key, &signed_link_data) + .map_err(|error| RadrootsSimplexAgentRuntimeError::Runtime(error.to_string()))?; + Ok(SimplexPreparedShortInvitationLinkData { + link_key, + link_public_signature_key: root_keypair.public_key, + link_private_signature_key: root_keypair.private_key, + encrypted_link_data, + }) +} + fn correlation_id_for_command(command_id: u64) -> RadrootsSimplexSmpCorrelationId { let digest = derive_material(b"simplex-command-correlation", &[&command_id.to_be_bytes()]); let mut correlation = [0_u8; RadrootsSimplexSmpCorrelationId::LENGTH]; @@ -1974,13 +2135,16 @@ fn queue_for_command( | RadrootsSimplexAgentPendingCommandKind::SendEnvelope { queue, .. } | RadrootsSimplexAgentPendingCommandKind::SubscribeQueue { queue } | RadrootsSimplexAgentPendingCommandKind::GetQueueMessage { queue } - | RadrootsSimplexAgentPendingCommandKind::AckInboxMessage { queue, .. } => { + | RadrootsSimplexAgentPendingCommandKind::AckInboxMessage { queue, .. } + | RadrootsSimplexAgentPendingCommandKind::SetQueueLinkData { queue, .. } => { Some(queue.clone()) } RadrootsSimplexAgentPendingCommandKind::RotateQueues { descriptors } => descriptors .first() .map(RadrootsSimplexAgentQueueDescriptor::queue_address), RadrootsSimplexAgentPendingCommandKind::TestQueues { queues } => queues.first().cloned(), + RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData { .. } + | RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { .. } => None, } } @@ -2244,12 +2408,20 @@ mod tests { sender_id: sender_id.to_vec(), server_dh_public_key: RadrootsSimplexSmpX25519Keypair::from_seed(seed).public_key, queue_mode: Some(RadrootsSimplexSmpQueueMode::Messaging), - link_id: None, + link_id: Some(synthetic_link_id(seed)), service_id: None, server_notification_credentials: None, }) } + fn synthetic_link_id(seed: &[u8]) -> Vec<u8> { + let mut hasher = Sha256::new(); + hasher.update(b"rr-synth-runtime-link-id"); + hasher.update(seed); + let digest = hasher.finalize(); + digest[..24].to_vec() + } + #[derive(Default)] struct ScriptedTransport { responses: VecDeque<RadrootsSimplexSmpBrokerMessage>, @@ -2405,6 +2577,17 @@ mod tests { let created_queue = runtime.store.receive_queues(&created).unwrap(); assert!(created_queue[0].subscribed); assert_eq!(transport.requests.len(), 6); + let RadrootsSimplexSmpCommand::New(create_request) = &transport.requests[0].command else { + panic!("first request should create the invitation queue"); + }; + let Some(RadrootsSimplexSmpQueueRequestData::Messaging(Some(link_request))) = + create_request.queue_request_data.as_ref() + else { + panic!("invitation NEW should carry short-link messaging data"); + }; + assert!(!link_request.sender_id.is_empty()); + assert!(!link_request.link_data.fixed_data.is_empty()); + assert!(!link_request.link_data.user_data.is_empty()); assert!(matches!( transport.requests[3].command, RadrootsSimplexSmpCommand::Sub @@ -2421,10 +2604,42 @@ mod tests { .iter() .any(|request| matches!(request.command, RadrootsSimplexSmpCommand::Get)) ); - assert!(matches!( - runtime.drain_events(16).first(), - Some(RadrootsSimplexAgentRuntimeEvent::InvitationReady { .. }) - )); + let events = runtime.drain_events(16); + let Some(RadrootsSimplexAgentRuntimeEvent::InvitationReady { invitation, .. }) = + events.first() + else { + panic!("runtime should emit a short invitation event"); + }; + let rendered = invitation.render().unwrap(); + assert!(rendered.starts_with("simplex:/i#")); + assert_eq!( + radroots_simplex_agent_proto::prelude::parse_short_invitation_link(&rendered).unwrap(), + invitation.clone() + ); + let short_link = runtime + .store + .connection(&created) + .unwrap() + .short_link + .as_ref() + .unwrap(); + assert_eq!(short_link.link_id, synthetic_link_id(b"server-dh")); + let link_data_key = derive_invitation_short_link_data_key(&short_link.link_key).unwrap(); + let verified = radroots_simplex_smp_crypto::prelude::decrypt_verify_short_link_data( + &short_link.link_key, + &link_data_key, + &short_link.link_public_signature_key, + &RadrootsSimplexSmpQueueLinkData { + fixed_data: short_link.encrypted_fixed_data.clone().unwrap(), + user_data: short_link.encrypted_user_data.clone().unwrap(), + }, + ) + .unwrap(); + let decoded = + radroots_simplex_agent_proto::prelude::decode_connection_link(&verified.fixed_data) + .unwrap(); + assert_eq!(decoded.connection_id, created.as_bytes().to_vec()); + assert_eq!(verified.user_data, created.as_bytes().to_vec()); assert_eq!( runtime.store.connection(&joined).unwrap().status, RadrootsSimplexAgentConnectionStatus::JoinPending diff --git a/crates/simplex_agent_runtime/src/types.rs b/crates/simplex_agent_runtime/src/types.rs @@ -1,12 +1,12 @@ use alloc::string::String; use alloc::vec::Vec; -use radroots_simplex_agent_proto::prelude::RadrootsSimplexAgentConnectionLink; +use radroots_simplex_agent_proto::prelude::RadrootsSimplexAgentShortInvitationLink; #[derive(Debug, Clone, PartialEq, Eq)] pub enum RadrootsSimplexAgentRuntimeEvent { InvitationReady { connection_id: String, - invitation: RadrootsSimplexAgentConnectionLink, + invitation: RadrootsSimplexAgentShortInvitationLink, }, ConfirmationRequired { connection_id: String, diff --git a/crates/simplex_agent_store/src/store.rs b/crates/simplex_agent_store/src/store.rs @@ -153,6 +153,7 @@ pub struct RadrootsSimplexAgentShortLinkCredentials { pub link_public_signature_key: Vec<u8>, pub link_private_signature_key: Vec<u8>, pub encrypted_fixed_data: Option<Vec<u8>>, + pub encrypted_user_data: Option<Vec<u8>>, } impl RadrootsSimplexAgentShortLinkCredentials { @@ -348,6 +349,7 @@ struct RadrootsSimplexAgentShortLinkCredentialsSnapshot { link_public_signature_key: Vec<u8>, link_private_signature_key: Vec<u8>, encrypted_fixed_data: Option<Vec<u8>>, + encrypted_user_data: Option<Vec<u8>>, } #[cfg(feature = "std")] @@ -2296,6 +2298,7 @@ fn short_link_to_snapshot( link_public_signature_key: credentials.link_public_signature_key, link_private_signature_key: credentials.link_private_signature_key, encrypted_fixed_data: credentials.encrypted_fixed_data, + encrypted_user_data: credentials.encrypted_user_data, } } @@ -2313,6 +2316,7 @@ fn short_link_from_snapshot( link_public_signature_key: snapshot.link_public_signature_key, link_private_signature_key: snapshot.link_private_signature_key, encrypted_fixed_data: snapshot.encrypted_fixed_data, + encrypted_user_data: snapshot.encrypted_user_data, }) } @@ -2816,6 +2820,7 @@ mod tests { link_public_signature_key: vec![7_u8; 32], link_private_signature_key: b"short-link-private-signature-key".to_vec(), encrypted_fixed_data: Some(b"encrypted-fixed-link-data".to_vec()), + encrypted_user_data: Some(b"encrypted-user-link-data".to_vec()), } } diff --git a/crates/simplex_chat_proto/src/codec.rs b/crates/simplex_chat_proto/src/codec.rs @@ -14,6 +14,7 @@ use crate::model::{ RadrootsSimplexChatQuotedMessage, RadrootsSimplexChatScope, }; use crate::version::RadrootsSimplexChatVersionRange; +use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use alloc::vec; diff --git a/crates/simplex_chat_proto/src/model.rs b/crates/simplex_chat_proto/src/model.rs @@ -1,5 +1,6 @@ use crate::error::RadrootsSimplexChatProtoError; use crate::version::RadrootsSimplexChatVersionRange; +use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use base64::Engine as _; diff --git a/crates/simplex_interop_tests/src/lib.rs b/crates/simplex_interop_tests/src/lib.rs @@ -71,12 +71,21 @@ mod tests { sender_id: sender_id.to_vec(), server_dh_public_key: RadrootsSimplexSmpX25519Keypair::from_seed(seed).public_key, queue_mode: Some(RadrootsSimplexSmpQueueMode::Messaging), - link_id: None, + link_id: Some(synthetic_link_id(seed)), service_id: None, server_notification_credentials: None, }) } + fn synthetic_link_id(seed: &[u8]) -> Vec<u8> { + let mut link_id = vec![0_u8; 24]; + for (index, byte) in seed.iter().enumerate() { + link_id[index % 24] ^= *byte; + link_id[(index * 7 + 3) % 24] = link_id[(index * 7 + 3) % 24].wrapping_add(*byte); + } + link_id + } + fn correlation_id(byte: u8) -> RadrootsSimplexSmpCorrelationId { RadrootsSimplexSmpCorrelationId::new([byte; RadrootsSimplexSmpCorrelationId::LENGTH]) } @@ -106,12 +115,14 @@ mod tests { #[derive(Default)] struct ScriptedTransport { responses: VecDeque<RadrootsSimplexSmpBrokerMessage>, + requests: Vec<RadrootsSimplexSmpTransportRequest>, } impl ScriptedTransport { fn with_responses(responses: Vec<RadrootsSimplexSmpBrokerMessage>) -> Self { Self { responses: responses.into(), + requests: Vec::new(), } } } @@ -173,6 +184,7 @@ mod tests { ) .map_err(|error| error.to_string())?; let response_encoded = response_block.encode().map_err(|error| error.to_string())?; + self.requests.push(request.clone()); Ok(RadrootsSimplexSmpTransportResponse { server: request.server, transport_version: request.transport_version, @@ -290,7 +302,7 @@ mod tests { .execute_ready_commands(&mut invitation_transport, 20, 16) .unwrap(); let events = runtime.drain_events(8); - let invitation = events + let short_invitation = events .into_iter() .find_map(|event| match event { RadrootsSimplexAgentRuntimeEvent::InvitationReady { invitation, .. } => { @@ -299,45 +311,21 @@ mod tests { _ => None, }) .expect("invitation event"); - - let joined = runtime - .join_connection(invitation, synthetic_reply_queue(), 30) - .unwrap(); - let mut join_transport = ScriptedTransport::with_responses(vec![ - RadrootsSimplexSmpBrokerMessage::Ok, - ids_response(b"recipient-2", b"sender-2", b"server-dh-2"), - RadrootsSimplexSmpBrokerMessage::Ok, - RadrootsSimplexSmpBrokerMessage::Ok, - RadrootsSimplexSmpBrokerMessage::Ok, - ]); - runtime - .execute_ready_commands(&mut join_transport, 40, 16) - .unwrap(); - runtime - .handle_inbound_decrypted_message( - &joined, - RadrootsSimplexAgentDecryptedMessage::Message(RadrootsSimplexAgentMessageFrame { - header: RadrootsSimplexAgentMessageHeader { - message_id: 1, - previous_message_hash: Vec::new(), - }, - message: RadrootsSimplexAgentMessage::Hello, - padding: Vec::new(), - }), - b"rr-synth-hello".to_vec(), - ) - .unwrap(); - let mut hello_transport = - ScriptedTransport::with_responses(vec![RadrootsSimplexSmpBrokerMessage::Ok]); - runtime - .execute_ready_commands(&mut hello_transport, 50, 16) - .unwrap(); - let message_id = runtime - .send_message(&joined, b"rr-synth-chat".to_vec(), 60) - .unwrap(); - assert_eq!(message_id, 2); - runtime.reconnect_connection(&joined, 70).unwrap(); - assert!(!runtime.retry_pending(70 + 5_000, 64).is_empty()); + assert!( + short_invitation + .render() + .unwrap() + .starts_with("simplex:/i#") + ); + let RadrootsSimplexSmpCommand::New(create_request) = + &invitation_transport.requests[0].command + else { + panic!("first synthetic runtime command should create the invite queue"); + }; + assert!(matches!( + create_request.queue_request_data.as_ref(), + Some(RadrootsSimplexSmpQueueRequestData::Messaging(Some(_))) + )); assert!(created.starts_with("conn-")); } diff --git a/crates/simplex_interop_tests/src/policy.rs b/crates/simplex_interop_tests/src/policy.rs @@ -1,4 +1,6 @@ -use alloc::string::{String, ToString}; +use alloc::string::String; +#[cfg(feature = "std")] +use alloc::string::ToString; #[cfg(feature = "std")] use alloc::vec; use core::fmt;