commit e4517b061898a6d2651e0d584bbd633ccef6c43f
parent bf533e60cc795cc2779e061f5ec4e9c52a526019
Author: triesap <tyson@radroots.org>
Date: Tue, 23 Jun 2026 06:51:09 +0000
simplex: join short invitations through link data
Diffstat:
2 files changed, 340 insertions(+), 116 deletions(-)
diff --git a/crates/simplex_agent_runtime/src/runtime.rs b/crates/simplex_agent_runtime/src/runtime.rs
@@ -346,9 +346,53 @@ impl RadrootsSimplexAgentRuntime {
pub fn join_connection(
&mut self,
invitation: RadrootsSimplexAgentConnectionLink,
- mut reply_queue: RadrootsSimplexSmpQueueUri,
+ reply_queue: RadrootsSimplexSmpQueueUri,
now: u64,
) -> Result<String, RadrootsSimplexAgentRuntimeError> {
+ let connection = self.store.create_connection(
+ RadrootsSimplexAgentConnectionMode::Direct,
+ RadrootsSimplexAgentConnectionStatus::JoinPending,
+ None,
+ None,
+ );
+ let connection_id = connection.id.clone();
+ self.prepare_join_connection(&connection_id, invitation, reply_queue, now)?;
+ self.flush_store()?;
+ Ok(connection_id)
+ }
+
+ pub fn join_short_invitation(
+ &mut self,
+ invitation: RadrootsSimplexAgentShortInvitationLink,
+ reply_queue: RadrootsSimplexSmpQueueUri,
+ now: u64,
+ ) -> Result<String, RadrootsSimplexAgentRuntimeError> {
+ let _ = short_invitation_server(&invitation)?;
+ let connection = self.store.create_connection(
+ RadrootsSimplexAgentConnectionMode::Direct,
+ RadrootsSimplexAgentConnectionStatus::JoinPending,
+ None,
+ None,
+ );
+ self.store.enqueue_command(
+ &connection.id,
+ RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
+ invitation,
+ reply_queue,
+ },
+ now,
+ )?;
+ self.flush_store()?;
+ Ok(connection.id)
+ }
+
+ fn prepare_join_connection(
+ &mut self,
+ connection_id: &str,
+ invitation: RadrootsSimplexAgentConnectionLink,
+ mut reply_queue: RadrootsSimplexSmpQueueUri,
+ now: u64,
+ ) -> Result<(), RadrootsSimplexAgentRuntimeError> {
let local_e2e_keypair = RadrootsSimplexSmpX25519Keypair::generate()
.map_err(|error| RadrootsSimplexAgentRuntimeError::Runtime(error.to_string()))?;
let invitation_e2e_public_key =
@@ -436,12 +480,6 @@ impl RadrootsSimplexAgentRuntime {
.map_err(|error| RadrootsSimplexAgentRuntimeError::Runtime(error.to_string()))?;
None
};
- let connection = self.store.create_connection(
- RadrootsSimplexAgentConnectionMode::Direct,
- RadrootsSimplexAgentConnectionStatus::JoinPending,
- Some(invitation.clone()),
- Some(ratchet_state),
- );
let send_auth_state = self.store.generate_queue_auth_state()?;
let send_descriptor = RadrootsSimplexAgentQueueDescriptor {
queue_uri: invitation.invitation_queue.clone(),
@@ -458,22 +496,29 @@ impl RadrootsSimplexAgentRuntime {
primary: true,
sender_key: None,
};
+ {
+ let connection = self.store.connection_mut(connection_id)?;
+ connection.mode = RadrootsSimplexAgentConnectionMode::Direct;
+ connection.status = RadrootsSimplexAgentConnectionStatus::JoinPending;
+ connection.invitation = Some(invitation);
+ connection.ratchet_state = Some(ratchet_state);
+ }
self.store.add_queue(
- &connection.id,
+ connection_id,
send_descriptor.clone(),
RadrootsSimplexAgentQueueRole::Send,
true,
send_auth_state,
)?;
self.store.add_queue(
- &connection.id,
+ connection_id,
receive_descriptor.clone(),
RadrootsSimplexAgentQueueRole::Receive,
true,
receive_auth_state,
)?;
{
- let connection = self.store.connection_mut(&connection.id)?;
+ let connection = self.store.connection_mut(connection_id)?;
connection.local_e2e_public_key = Some(local_e2e_keypair.public_key.clone());
connection.local_e2e_private_key = Some(local_e2e_keypair.private_key);
connection.local_x3dh_key_1 = Some(agent_x3dh_keypair(local_x3dh_key_1));
@@ -494,7 +539,7 @@ impl RadrootsSimplexAgentRuntime {
queue.delivery_private_key = Some(delivery_keypair.private_key);
}
self.store.enqueue_command(
- &connection.id,
+ connection_id,
RadrootsSimplexAgentPendingCommandKind::SecureQueue {
queue: send_descriptor.queue_address(),
sender_key: send_descriptor.sender_key.clone(),
@@ -502,14 +547,13 @@ impl RadrootsSimplexAgentRuntime {
now,
)?;
self.store.enqueue_command(
- &connection.id,
+ connection_id,
RadrootsSimplexAgentPendingCommandKind::CreateQueue {
descriptor: receive_descriptor.clone(),
},
now,
)?;
- self.flush_store()?;
- Ok(connection.id)
+ Ok(())
}
pub fn allow_connection(
@@ -1078,22 +1122,22 @@ impl RadrootsSimplexAgentRuntime {
) -> Result<RadrootsSimplexSmpTransportRequest, RadrootsSimplexAgentRuntimeError> {
match &command.kind {
RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
- server,
- link_id,
- link_key,
+ invitation, ..
} => {
+ let server = short_invitation_server(invitation)?;
return Ok(self.server_transport_request(
command.id,
- server,
- link_id.clone(),
- RadrootsSimplexSmpCommand::LKey(link_key.clone()),
+ &server,
+ invitation.link_id.clone(),
+ RadrootsSimplexSmpCommand::LKey(invitation.link_key.clone()),
));
}
- RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { server, link_id } => {
+ RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { invitation, .. } => {
+ let server = short_invitation_server(invitation)?;
return Ok(self.server_transport_request(
command.id,
- server,
- link_id.clone(),
+ &server,
+ invitation.link_id.clone(),
RadrootsSimplexSmpCommand::LGet,
));
}
@@ -1348,6 +1392,31 @@ impl RadrootsSimplexAgentRuntime {
}))
}
+ fn process_short_link_response(
+ &mut self,
+ command: &RadrootsSimplexAgentPendingCommand,
+ sender_id: Vec<u8>,
+ link_data: RadrootsSimplexSmpQueueLinkData,
+ ) -> Result<(), RadrootsSimplexAgentRuntimeError> {
+ let RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
+ invitation,
+ reply_queue,
+ } = &command.kind
+ else {
+ return Err(RadrootsSimplexAgentRuntimeError::Runtime(
+ "SimpleX LNK response received for non-retrieval command".into(),
+ ));
+ };
+ let mut connection_link = decrypt_short_invitation_link_data(invitation, &link_data)?;
+ connection_link.invitation_queue.sender_id = URL_SAFE_NO_PAD.encode(sender_id);
+ self.prepare_join_connection(
+ &command.connection_id,
+ connection_link,
+ reply_queue.clone(),
+ command.ready_at,
+ )
+ }
+
fn apply_transport_response(
&mut self,
command: &RadrootsSimplexAgentPendingCommand,
@@ -1388,6 +1457,16 @@ impl RadrootsSimplexAgentRuntime {
RadrootsSimplexAgentCommandOutcome::Delivered,
)
}
+ RadrootsSimplexSmpBrokerMessage::Lnk {
+ sender_id,
+ link_data,
+ } => {
+ self.process_short_link_response(command, sender_id, link_data)?;
+ self.record_command_outcome(
+ command.id,
+ RadrootsSimplexAgentCommandOutcome::Delivered,
+ )
+ }
_ => self
.record_command_outcome(command.id, RadrootsSimplexAgentCommandOutcome::Delivered),
}
@@ -1455,6 +1534,19 @@ impl RadrootsSimplexAgentRuntime {
.mark_queue_tested(&command.connection_id, queue)?;
}
}
+ RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
+ invitation,
+ reply_queue,
+ } => {
+ self.store.enqueue_command(
+ &command.connection_id,
+ RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
+ invitation: invitation.clone(),
+ reply_queue: reply_queue.clone(),
+ },
+ command.ready_at,
+ )?;
+ }
_ => {}
}
Ok(())
@@ -2128,6 +2220,21 @@ fn prepare_short_invitation_link_data(
})
}
+fn short_invitation_server(
+ invitation: &RadrootsSimplexAgentShortInvitationLink,
+) -> Result<RadrootsSimplexSmpServerAddress, RadrootsSimplexAgentRuntimeError> {
+ let server_identity = invitation.hosts.first().cloned().ok_or_else(|| {
+ RadrootsSimplexAgentRuntimeError::Runtime(
+ "SimpleX short invitation link does not include a relay host".into(),
+ )
+ })?;
+ Ok(RadrootsSimplexSmpServerAddress {
+ server_identity,
+ hosts: invitation.hosts.clone(),
+ port: invitation.port,
+ })
+}
+
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];
@@ -2695,6 +2802,101 @@ mod tests {
}
#[test]
+ fn join_short_invitation_retrieves_link_data_and_continues_join() {
+ let mut runtime = RadrootsSimplexAgentRuntimeBuilder::new().build().unwrap();
+ let created = runtime
+ .create_connection(invitation_queue(), b"e2e".to_vec(), false, 10)
+ .unwrap();
+ let mut setup_transport = ScriptedTransport::with_responses(vec![
+ ids_response(b"recipient", b"sender", b"server-dh"),
+ RadrootsSimplexSmpBrokerMessage::Ok,
+ ]);
+ runtime
+ .execute_ready_commands(&mut setup_transport, 30, 16)
+ .unwrap();
+ let events = runtime.drain_events(16);
+ let Some(RadrootsSimplexAgentRuntimeEvent::InvitationReady {
+ invitation: short_invitation,
+ ..
+ }) = events.first()
+ else {
+ panic!("runtime should emit a short invitation event");
+ };
+ let short_link = runtime
+ .store
+ .connection(&created)
+ .unwrap()
+ .short_link
+ .as_ref()
+ .unwrap();
+ let stored_link_data = RadrootsSimplexSmpQueueLinkData {
+ fixed_data: short_link.encrypted_fixed_data.clone().unwrap(),
+ user_data: short_link.encrypted_user_data.clone().unwrap(),
+ };
+ let joined = runtime
+ .join_short_invitation(short_invitation.clone(), reply_queue(), 40)
+ .unwrap();
+ let mut join_transport = ScriptedTransport::with_responses(vec![
+ RadrootsSimplexSmpBrokerMessage::Ok,
+ RadrootsSimplexSmpBrokerMessage::Lnk {
+ sender_id: b"sender".to_vec(),
+ link_data: stored_link_data,
+ },
+ RadrootsSimplexSmpBrokerMessage::Ok,
+ ids_response(b"recipient-2", b"sender-2", b"server-dh-2"),
+ RadrootsSimplexSmpBrokerMessage::Ok,
+ RadrootsSimplexSmpBrokerMessage::Ok,
+ ]);
+ runtime
+ .execute_ready_commands(&mut join_transport, 50, 16)
+ .unwrap();
+
+ assert_eq!(join_transport.requests.len(), 6);
+ let RadrootsSimplexSmpCommand::LKey(link_key) = &join_transport.requests[0].command else {
+ panic!("short invitation join should authorize link retrieval first");
+ };
+ assert_eq!(link_key, &short_invitation.link_key);
+ assert_eq!(
+ join_transport.requests[0].entity_id,
+ short_invitation.link_id.clone()
+ );
+ assert!(matches!(
+ join_transport.requests[1].command,
+ RadrootsSimplexSmpCommand::LGet
+ ));
+ let RadrootsSimplexSmpCommand::SKey(_) = &join_transport.requests[2].command else {
+ panic!("short invitation join should secure the invitation send queue");
+ };
+ let RadrootsSimplexSmpCommand::New(_) = &join_transport.requests[3].command else {
+ panic!("short invitation join should create the reply queue");
+ };
+ let joined_connection = runtime.store.connection(&joined).unwrap();
+ assert_eq!(
+ joined_connection.status,
+ RadrootsSimplexAgentConnectionStatus::JoinPending
+ );
+ assert_eq!(
+ joined_connection.invitation.as_ref().unwrap().connection_id,
+ created.as_bytes().to_vec()
+ );
+ assert_eq!(
+ runtime
+ .store
+ .primary_send_queue(&joined)
+ .unwrap()
+ .descriptor
+ .queue_uri
+ .sender_id,
+ URL_SAFE_NO_PAD.encode(b"sender")
+ );
+ assert!(runtime.drain_events(16).iter().any(|event| matches!(
+ event,
+ RadrootsSimplexAgentRuntimeEvent::ConfirmationRequired { connection_id }
+ if connection_id == &joined
+ )));
+ }
+
+ #[test]
fn join_confirmation_carries_sender_x3dh_params() {
let mut runtime = RadrootsSimplexAgentRuntimeBuilder::new().build().unwrap();
let created = runtime
diff --git a/crates/simplex_agent_store/src/store.rs b/crates/simplex_agent_store/src/store.rs
@@ -30,10 +30,9 @@ use radroots_simplex_smp_crypto::prelude::RadrootsSimplexSmpEd25519Keypair;
use radroots_simplex_smp_crypto::prelude::{
RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH, RadrootsSimplexSmpSkippedMessageKey,
};
-use radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpQueueLinkData;
-#[cfg(feature = "std")]
-use radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpQueueUri;
-use radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpServerAddress;
+use radroots_simplex_smp_proto::prelude::{
+ RadrootsSimplexSmpQueueLinkData, RadrootsSimplexSmpQueueUri, RadrootsSimplexSmpServerAddress,
+};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
@@ -206,13 +205,12 @@ pub enum RadrootsSimplexAgentPendingCommandKind {
link_data: RadrootsSimplexSmpQueueLinkData,
},
SecureGetQueueLinkData {
- server: RadrootsSimplexSmpServerAddress,
- link_id: Vec<u8>,
- link_key: Vec<u8>,
+ invitation: RadrootsSimplexAgentShortInvitationLink,
+ reply_queue: RadrootsSimplexSmpQueueUri,
},
GetQueueLinkData {
- server: RadrootsSimplexSmpServerAddress,
- link_id: Vec<u8>,
+ invitation: RadrootsSimplexAgentShortInvitationLink,
+ reply_queue: RadrootsSimplexSmpQueueUri,
},
}
@@ -330,14 +328,6 @@ struct RadrootsSimplexAgentQueueAddressSnapshot {
}
#[cfg(feature = "std")]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-struct RadrootsSimplexAgentServerAddressSnapshot {
- server_identity: String,
- hosts: Vec<String>,
- port: Option<u16>,
-}
-
-#[cfg(feature = "std")]
#[derive(Debug, Clone, Serialize, Deserialize)]
struct RadrootsSimplexAgentShortLinkCredentialsSnapshot {
scheme: String,
@@ -354,6 +344,17 @@ struct RadrootsSimplexAgentShortLinkCredentialsSnapshot {
#[cfg(feature = "std")]
#[derive(Debug, Clone, Serialize, Deserialize)]
+struct RadrootsSimplexAgentShortInvitationLinkSnapshot {
+ scheme: String,
+ hosts: Vec<String>,
+ port: Option<u16>,
+ server_key_hash: Option<Vec<u8>>,
+ link_id: Vec<u8>,
+ link_key: Vec<u8>,
+}
+
+#[cfg(feature = "std")]
+#[derive(Debug, Clone, Serialize, Deserialize)]
struct RadrootsSimplexAgentQueueLinkDataSnapshot {
fixed_data: Vec<u8>,
user_data: Vec<u8>,
@@ -446,13 +447,12 @@ enum RadrootsSimplexAgentPendingCommandKindSnapshot {
link_data: RadrootsSimplexAgentQueueLinkDataSnapshot,
},
SecureGetQueueLinkData {
- server: RadrootsSimplexAgentServerAddressSnapshot,
- link_id: Vec<u8>,
- link_key: Vec<u8>,
+ invitation: RadrootsSimplexAgentShortInvitationLinkSnapshot,
+ reply_queue: String,
},
GetQueueLinkData {
- server: RadrootsSimplexAgentServerAddressSnapshot,
- link_id: Vec<u8>,
+ invitation: RadrootsSimplexAgentShortInvitationLinkSnapshot,
+ reply_queue: String,
},
}
@@ -2209,14 +2209,8 @@ fn queue_descriptor_to_snapshot(
fn queue_descriptor_from_snapshot(
snapshot: RadrootsSimplexAgentQueueDescriptorSnapshot,
) -> Result<RadrootsSimplexAgentQueueDescriptor, RadrootsSimplexAgentStoreError> {
- let queue_uri = RadrootsSimplexSmpQueueUri::parse(&snapshot.queue_uri).map_err(|error| {
- RadrootsSimplexAgentStoreError::Persistence(format!(
- "failed to parse SimpleX queue uri `{}`: {error}",
- snapshot.queue_uri
- ))
- })?;
Ok(RadrootsSimplexAgentQueueDescriptor {
- queue_uri,
+ queue_uri: queue_uri_from_string(&snapshot.queue_uri)?,
replaced_queue: snapshot
.replaced_queue
.map(queue_address_from_snapshot)
@@ -2227,6 +2221,17 @@ fn queue_descriptor_from_snapshot(
}
#[cfg(feature = "std")]
+fn queue_uri_from_string(
+ value: &str,
+) -> Result<RadrootsSimplexSmpQueueUri, RadrootsSimplexAgentStoreError> {
+ RadrootsSimplexSmpQueueUri::parse(value).map_err(|error| {
+ RadrootsSimplexAgentStoreError::Persistence(format!(
+ "failed to parse SimpleX queue uri `{value}`: {error}"
+ ))
+ })
+}
+
+#[cfg(feature = "std")]
fn queue_address_to_snapshot(
address: RadrootsSimplexAgentQueueAddress,
) -> RadrootsSimplexAgentQueueAddressSnapshot {
@@ -2258,33 +2263,6 @@ fn queue_address_from_snapshot(
}
#[cfg(feature = "std")]
-fn server_address_to_snapshot(
- server: RadrootsSimplexSmpServerAddress,
-) -> RadrootsSimplexAgentServerAddressSnapshot {
- RadrootsSimplexAgentServerAddressSnapshot {
- server_identity: server.server_identity,
- hosts: server.hosts,
- port: server.port,
- }
-}
-
-#[cfg(feature = "std")]
-fn server_address_from_snapshot(
- snapshot: RadrootsSimplexAgentServerAddressSnapshot,
-) -> Result<RadrootsSimplexSmpServerAddress, RadrootsSimplexAgentStoreError> {
- if snapshot.server_identity.is_empty() || snapshot.hosts.is_empty() {
- return Err(RadrootsSimplexAgentStoreError::Persistence(
- "invalid SimpleX server address snapshot".into(),
- ));
- }
- Ok(RadrootsSimplexSmpServerAddress {
- server_identity: snapshot.server_identity,
- hosts: snapshot.hosts,
- port: snapshot.port,
- })
-}
-
-#[cfg(feature = "std")]
fn short_link_to_snapshot(
credentials: RadrootsSimplexAgentShortLinkCredentials,
) -> RadrootsSimplexAgentShortLinkCredentialsSnapshot {
@@ -2321,6 +2299,34 @@ fn short_link_from_snapshot(
}
#[cfg(feature = "std")]
+fn short_invitation_to_snapshot(
+ invitation: RadrootsSimplexAgentShortInvitationLink,
+) -> RadrootsSimplexAgentShortInvitationLinkSnapshot {
+ RadrootsSimplexAgentShortInvitationLinkSnapshot {
+ scheme: encode_short_link_scheme(invitation.scheme).into(),
+ hosts: invitation.hosts,
+ port: invitation.port,
+ server_key_hash: invitation.server_key_hash,
+ link_id: invitation.link_id,
+ link_key: invitation.link_key,
+ }
+}
+
+#[cfg(feature = "std")]
+fn short_invitation_from_snapshot(
+ snapshot: RadrootsSimplexAgentShortInvitationLinkSnapshot,
+) -> Result<RadrootsSimplexAgentShortInvitationLink, RadrootsSimplexAgentStoreError> {
+ Ok(RadrootsSimplexAgentShortInvitationLink {
+ scheme: decode_short_link_scheme(&snapshot.scheme)?,
+ hosts: snapshot.hosts,
+ port: snapshot.port,
+ server_key_hash: snapshot.server_key_hash,
+ link_id: snapshot.link_id,
+ link_key: snapshot.link_key,
+ })
+}
+
+#[cfg(feature = "std")]
fn queue_link_data_to_snapshot(
link_data: RadrootsSimplexSmpQueueLinkData,
) -> RadrootsSimplexAgentQueueLinkDataSnapshot {
@@ -2569,20 +2575,19 @@ fn command_kind_to_snapshot(
link_data: queue_link_data_to_snapshot(link_data),
},
RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
- server,
- link_id,
- link_key,
+ invitation,
+ reply_queue,
} => RadrootsSimplexAgentPendingCommandKindSnapshot::SecureGetQueueLinkData {
- server: server_address_to_snapshot(server),
- link_id,
- link_key,
+ invitation: short_invitation_to_snapshot(invitation),
+ reply_queue: reply_queue.to_string(),
+ },
+ RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
+ invitation,
+ reply_queue,
+ } => RadrootsSimplexAgentPendingCommandKindSnapshot::GetQueueLinkData {
+ invitation: short_invitation_to_snapshot(invitation),
+ reply_queue: reply_queue.to_string(),
},
- RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { server, link_id } => {
- RadrootsSimplexAgentPendingCommandKindSnapshot::GetQueueLinkData {
- server: server_address_to_snapshot(server),
- link_id,
- }
- }
})
}
@@ -2664,20 +2669,19 @@ fn command_kind_from_snapshot(
link_data: queue_link_data_from_snapshot(link_data),
},
RadrootsSimplexAgentPendingCommandKindSnapshot::SecureGetQueueLinkData {
- server,
- link_id,
- link_key,
+ invitation,
+ reply_queue,
} => RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
- server: server_address_from_snapshot(server)?,
- link_id,
- link_key,
+ invitation: short_invitation_from_snapshot(invitation)?,
+ reply_queue: queue_uri_from_string(&reply_queue)?,
+ },
+ RadrootsSimplexAgentPendingCommandKindSnapshot::GetQueueLinkData {
+ invitation,
+ reply_queue,
+ } => RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
+ invitation: short_invitation_from_snapshot(invitation)?,
+ reply_queue: queue_uri_from_string(&reply_queue)?,
},
- RadrootsSimplexAgentPendingCommandKindSnapshot::GetQueueLinkData { server, link_id } => {
- RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
- server: server_address_from_snapshot(server)?,
- link_id,
- }
- }
})
}
@@ -2824,6 +2828,17 @@ mod tests {
}
}
+ fn sample_short_invitation_link(link_id: Vec<u8>) -> RadrootsSimplexAgentShortInvitationLink {
+ RadrootsSimplexAgentShortInvitationLink {
+ scheme: RadrootsSimplexAgentShortLinkScheme::Simplex,
+ hosts: vec!["relay-a.example".to_owned(), "relay-b.example".to_owned()],
+ port: Some(5223),
+ server_key_hash: Some(vec![5_u8; 32]),
+ link_id,
+ link_key: b"short-link-key-must-be-secret!!".to_vec(),
+ }
+ }
+
#[cfg(feature = "std")]
fn persisted_store_with_secret_material(path: &Path) -> String {
let mut store = RadrootsSimplexAgentStore::open(path).unwrap();
@@ -2972,6 +2987,13 @@ mod tests {
.prepare_outbound_message(&connection.id, b"persisted".to_vec())
.unwrap();
let queue = sample_descriptor(true).queue_address();
+ let short_reply_queue = sample_descriptor_with_uri(
+ "smp://aGVsbG8@relay.example/cmVwbHk#/?v=4&dh=cmVwbHkta2V5&q=m",
+ true,
+ )
+ .queue_uri;
+ let secure_short_invitation = sample_short_invitation_link(vec![2_u8; 24]);
+ let get_short_invitation = sample_short_invitation_link(vec![3_u8; 24]);
store
.enqueue_command(
&connection.id,
@@ -3016,9 +3038,8 @@ mod tests {
.enqueue_command(
&connection.id,
RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
- server: queue.server.clone(),
- link_id: vec![2_u8; 24],
- link_key: b"retrieval-short-link-key-secret".to_vec(),
+ invitation: secure_short_invitation.clone(),
+ reply_queue: short_reply_queue.clone(),
},
13,
)
@@ -3027,8 +3048,8 @@ mod tests {
.enqueue_command(
&connection.id,
RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
- server: queue.server.clone(),
- link_id: vec![3_u8; 24],
+ invitation: get_short_invitation.clone(),
+ reply_queue: short_reply_queue.clone(),
},
14,
)
@@ -3297,17 +3318,18 @@ mod tests {
assert!(loaded.pending_commands.values().any(|command| matches!(
&command.kind,
RadrootsSimplexAgentPendingCommandKind::SecureGetQueueLinkData {
- server,
- link_id,
- link_key
- } if server == &queue.server
- && link_id.as_slice() == &[2_u8; 24]
- && link_key.as_slice() == b"retrieval-short-link-key-secret"
+ invitation,
+ reply_queue
+ } if invitation == &secure_short_invitation
+ && reply_queue == &short_reply_queue
)));
assert!(loaded.pending_commands.values().any(|command| matches!(
&command.kind,
- RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData { server, link_id }
- if server == &queue.server && link_id.as_slice() == &[3_u8; 24]
+ RadrootsSimplexAgentPendingCommandKind::GetQueueLinkData {
+ invitation,
+ reply_queue
+ } if invitation == &get_short_invitation
+ && reply_queue == &short_reply_queue
)));
assert!(
loaded