1#![warn(missing_docs)]
2
3use std::str::FromStr;
6use std::sync::Arc;
7use std::time::Duration;
8
9use rings_core::chunk::ReassemblyLimits;
10use rings_core::dht::Did;
11use rings_core::dht::EntryStorage;
12use rings_core::dht::DEFAULT_FINGER_TABLE_SIZE;
13use rings_core::ecc::PublicKey;
14use rings_core::ecc::SecretKey;
15use rings_core::measure::MeasureImpl;
16use rings_core::message::e2e;
17use rings_core::message::e2e::E2eHandshakeRequest;
18use rings_core::message::e2e::E2eHandshakeResponse;
19use rings_core::message::e2e::E2eStreamDecryptor;
20use rings_core::message::e2e::E2eStreamFrame;
21use rings_core::message::Encoded;
22use rings_core::message::Encoder;
23use rings_core::message::Message;
24use rings_core::prelude::uuid;
25use rings_core::storage::MemStorage;
26use rings_core::swarm::Swarm;
27use rings_core::swarm::SwarmBuilder;
28use rings_rpc::protos::rings_node::*;
29use serde::Deserialize;
30use serde::Serialize;
31
32use crate::consts::DATA_REDUNDANT;
33use crate::error::Error;
34use crate::error::Result;
35use crate::measure::PeriodicMeasure;
36use crate::prelude::entry;
37use crate::prelude::wasm_export;
38use crate::prelude::ChordStorageInterface;
39use crate::prelude::ChordStorageInterfaceCacheChecker;
40use crate::prelude::SessionSk;
41
42#[derive(Clone, Debug)]
45#[wasm_export]
46pub struct ProcessorConfig {
47 network_id: u32,
50 ice_servers: String,
52 external_address: Option<String>,
54 session_sk: SessionSk,
56 stabilize_interval: Duration,
58}
59
60#[wasm_export]
61impl ProcessorConfig {
62 pub fn new(
64 network_id: u32,
65 ice_servers: String,
66 session_sk: SessionSk,
67 stabilize_interval: u64,
68 ) -> Self {
69 Self {
70 network_id,
71 ice_servers,
72 external_address: None,
73 session_sk,
74 stabilize_interval: Duration::from_secs(stabilize_interval),
75 }
76 }
77
78 pub fn session_sk(&self) -> SessionSk {
80 self.session_sk.clone()
81 }
82}
83
84impl FromStr for ProcessorConfig {
85 type Err = Error;
86 fn from_str(ser: &str) -> Result<Self> {
88 serde_yaml::from_str::<ProcessorConfig>(ser).map_err(Error::SerdeYamlError)
89 }
90}
91
92#[derive(Serialize, Deserialize, Clone)]
95#[wasm_export]
96pub struct ProcessorConfigSerialized {
97 network_id: u32,
100 ice_servers: String,
102 external_address: Option<String>,
104 session_sk: String,
106 stabilize_interval: u64,
108}
109
110impl ProcessorConfigSerialized {
111 pub fn new(
113 network_id: u32,
114 ice_servers: String,
115 session_sk: String,
116 stabilize_interval: u64,
117 ) -> Self {
118 Self {
119 network_id,
120 ice_servers,
121 external_address: None,
122 session_sk,
123 stabilize_interval,
124 }
125 }
126
127 pub fn external_address(mut self, external_address: String) -> Self {
130 self.external_address = Some(external_address);
131 self
132 }
133}
134
135impl TryFrom<ProcessorConfig> for ProcessorConfigSerialized {
136 type Error = Error;
137 fn try_from(ins: ProcessorConfig) -> Result<Self> {
138 Ok(Self {
139 network_id: ins.network_id,
140 ice_servers: ins.ice_servers.clone(),
141 external_address: ins.external_address.clone(),
142 session_sk: ins.session_sk.dump()?,
143 stabilize_interval: ins.stabilize_interval.as_secs(),
144 })
145 }
146}
147
148impl TryFrom<ProcessorConfigSerialized> for ProcessorConfig {
149 type Error = Error;
150 fn try_from(ins: ProcessorConfigSerialized) -> Result<Self> {
151 Ok(Self {
152 network_id: ins.network_id,
153 ice_servers: ins.ice_servers.clone(),
154 external_address: ins.external_address.clone(),
155 session_sk: SessionSk::from_str(&ins.session_sk)?,
156 stabilize_interval: Duration::from_secs(ins.stabilize_interval),
157 })
158 }
159}
160
161impl Serialize for ProcessorConfig {
162 fn serialize<S: serde::Serializer>(
163 &self,
164 serializer: S,
165 ) -> core::result::Result<S::Ok, S::Error> {
166 let ins: ProcessorConfigSerialized = self
167 .clone()
168 .try_into()
169 .map_err(|e: Error| serde::ser::Error::custom(e.to_string()))?;
170 ProcessorConfigSerialized::serialize(&ins, serializer)
171 }
172}
173
174impl<'de> serde::de::Deserialize<'de> for ProcessorConfig {
175 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
176 where D: serde::Deserializer<'de> {
177 match ProcessorConfigSerialized::deserialize(deserializer) {
178 Ok(ins) => {
179 let cfg: ProcessorConfig = ins
180 .try_into()
181 .map_err(|e: Error| serde::de::Error::custom(e.to_string()))?;
182 Ok(cfg)
183 }
184 Err(e) => Err(e),
185 }
186 }
187}
188
189pub struct ProcessorBuilder {
191 network_id: u32,
192 ice_servers: String,
193 external_address: Option<String>,
194 session_sk: SessionSk,
195 storage: Option<EntryStorage>,
196 measure: Option<MeasureImpl>,
197 stabilize_interval: Duration,
198 dht_finger_table_size: usize,
199 reassembly_limits: ReassemblyLimits,
200}
201
202#[derive(Clone)]
204pub struct Processor {
205 pub swarm: Arc<Swarm>,
207 stabilize_interval: Duration,
208}
209
210impl ProcessorBuilder {
211 pub fn from_serialized(config: &str) -> Result<Self> {
213 let config =
214 serde_yaml::from_str::<ProcessorConfig>(config).map_err(Error::SerdeYamlError)?;
215 Self::from_config(&config)
216 }
217
218 pub fn from_config(config: &ProcessorConfig) -> Result<Self> {
220 Ok(Self {
221 network_id: config.network_id,
222 ice_servers: config.ice_servers.clone(),
223 external_address: config.external_address.clone(),
224 session_sk: config.session_sk.clone(),
225 storage: None,
226 measure: None,
227 stabilize_interval: config.stabilize_interval,
228 dht_finger_table_size: DEFAULT_FINGER_TABLE_SIZE,
229 reassembly_limits: ReassemblyLimits::production(),
230 })
231 }
232
233 pub fn storage(mut self, storage: EntryStorage) -> Self {
235 self.storage = Some(storage);
236 self
237 }
238
239 pub fn measure(mut self, implement: PeriodicMeasure) -> Self {
241 self.measure = Some(Box::new(implement));
242 self
243 }
244
245 pub fn dht_finger_table_size(mut self, size: usize) -> Self {
247 self.dht_finger_table_size = size;
248 self
249 }
250
251 pub fn reassembly_limits(mut self, limits: ReassemblyLimits) -> Self {
253 self.reassembly_limits = limits;
254 self
255 }
256
257 pub fn build(self) -> Result<Processor> {
259 self.session_sk
260 .session()
261 .verify_self()
262 .map_err(|e| Error::VerifyError(e.to_string()))?;
263
264 let storage = self.storage.unwrap_or_else(|| Box::new(MemStorage::new()));
265
266 let mut swarm_builder =
267 SwarmBuilder::new(self.network_id, &self.ice_servers, storage, self.session_sk);
268 swarm_builder = swarm_builder.dht_storage_redundancy(DATA_REDUNDANT);
269 swarm_builder = swarm_builder.dht_finger_table_size(self.dht_finger_table_size);
270 swarm_builder = swarm_builder.reassembly_limits(self.reassembly_limits);
271
272 if let Some(external_address) = self.external_address {
273 swarm_builder = swarm_builder.external_address(external_address);
274 }
275
276 if let Some(measure) = self.measure {
277 swarm_builder = swarm_builder.measure(measure);
278 }
279 let swarm = Arc::new(swarm_builder.build());
280
281 Ok(Processor {
282 swarm,
283 stabilize_interval: self.stabilize_interval,
284 })
285 }
286}
287
288impl Processor {
289 pub fn did(&self) -> Did {
291 self.swarm.did()
292 }
293
294 pub async fn listen(&self) {
296 let stabilizer = self.swarm.stabilizer();
297 Arc::new(stabilizer).wait(self.stabilize_interval).await
298 }
299
300 pub async fn connect_with_did(&self, did: Did) -> Result<()> {
306 self.swarm.connect(did).await.map_err(Error::ConnectError)?;
307 Ok(())
308 }
309
310 pub async fn disconnect(&self, did: Did) -> Result<()> {
312 self.swarm
313 .disconnect(did)
314 .await
315 .map_err(Error::CloseConnectionError)
316 }
317
318 pub async fn send_message(&self, destination: Did, msg: &[u8]) -> Result<uuid::Uuid> {
320 tracing::info!("send_message, message size: {:?}", msg.len());
321
322 let msg = Message::custom(msg).map_err(Error::SendMessage)?;
323
324 self.swarm
325 .send_message(msg, destination)
326 .await
327 .map_err(Error::SendMessage)
328 }
329
330 pub async fn send_e2e_handshake(&self, destination: Did) -> Result<uuid::Uuid> {
335 let public_key = self.swarm.account_pubkey().map_err(Error::SendMessage)?;
336 self.swarm
337 .send_message(
338 Message::E2eHandshakeRequest(E2eHandshakeRequest::new(public_key)),
339 destination,
340 )
341 .await
342 .map_err(Error::SendMessage)
343 }
344
345 pub async fn send_e2e_message(
349 &self,
350 destination: Did,
351 recipient_public_key: PublicKey<33>,
352 msg: &[u8],
353 ) -> Result<uuid::Uuid> {
354 self.send_e2e_message_with_frame_len(
355 destination,
356 recipient_public_key,
357 msg,
358 e2e::DEFAULT_E2E_PLAINTEXT_FRAME_LEN,
359 )
360 .await
361 }
362
363 pub async fn send_e2e_message_with_frame_len(
367 &self,
368 destination: Did,
369 recipient_public_key: PublicKey<33>,
370 msg: &[u8],
371 max_plaintext_frame_len: usize,
372 ) -> Result<uuid::Uuid> {
373 e2e::ensure_public_key_matches_did(recipient_public_key, destination)
374 .map_err(Error::SendMessage)?;
375 let sender_public_key = self.swarm.account_pubkey().map_err(Error::SendMessage)?;
376 let stream_id = uuid::Uuid::new_v4();
377 let frames = e2e::encrypt_stream_frames(
378 msg,
379 stream_id,
380 sender_public_key,
381 recipient_public_key,
382 max_plaintext_frame_len,
383 )
384 .map_err(Error::SendMessage)?;
385
386 for frame in frames {
387 let frame = frame.map_err(Error::SendMessage)?;
388 self.swarm
389 .send_message(Message::E2eStreamFrame(frame), destination)
390 .await
391 .map_err(Error::SendMessage)?;
392 }
393
394 Ok(stream_id)
395 }
396
397 pub fn verify_e2e_handshake_request(
399 &self,
400 requester: Did,
401 request: &E2eHandshakeRequest,
402 ) -> Result<PublicKey<33>> {
403 request
404 .verify_requester(requester)
405 .map_err(Error::CoreError)?;
406 Ok(request.requester_public_key)
407 }
408
409 pub fn verify_e2e_handshake_response(
411 &self,
412 responder: Did,
413 response: &E2eHandshakeResponse,
414 ) -> Result<PublicKey<33>> {
415 response
416 .verify_responder(responder)
417 .map_err(Error::CoreError)?;
418 Ok(response.responder_public_key)
419 }
420
421 pub fn e2e_stream_decryptor(
428 &self,
429 expected_sender: Did,
430 stream_id: e2e::E2eStreamId,
431 recipient_identity_key: SecretKey,
432 ) -> Result<E2eStreamDecryptor> {
433 e2e::ensure_public_key_matches_did(recipient_identity_key.pubkey(), self.did())
434 .map_err(Error::CoreError)?;
435 Ok(E2eStreamDecryptor::new(
436 stream_id,
437 expected_sender,
438 recipient_identity_key,
439 ))
440 }
441
442 pub fn decrypt_e2e_stream_frame(
444 &self,
445 decryptor: &mut E2eStreamDecryptor,
446 frame: &E2eStreamFrame,
447 ) -> Result<Vec<u8>> {
448 decryptor.decrypt_next(frame).map_err(Error::CoreError)
449 }
450
451 pub async fn send_envelope(
455 &self,
456 destination: Did,
457 envelope: &crate::extension::ext::Envelope,
458 ) -> Result<uuid::Uuid> {
459 let msg_bytes = envelope.encode()?;
460 self.send_message(destination, &msg_bytes).await
461 }
462
463 pub async fn storage_check_cache(&self, entry_key: Did) -> Option<entry::Entry> {
465 self.swarm.storage_check_cache(entry_key).await
466 }
467
468 pub async fn storage_fetch(&self, entry_key: Did) -> Result<()> {
470 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_fetch(&self.swarm, entry_key)
471 .await
472 .map_err(Error::EntryError)
473 }
474
475 pub async fn storage_store(&self, entry: entry::Entry) -> Result<()> {
477 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_store(&self.swarm, entry)
478 .await
479 .map_err(Error::EntryError)
480 }
481
482 pub async fn storage_append_data(&self, topic: &str, data: Encoded) -> Result<()> {
484 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_append_data(
485 &self.swarm,
486 topic,
487 data,
488 )
489 .await
490 .map_err(Error::EntryError)
491 }
492
493 pub async fn register_service(&self, name: &str) -> Result<()> {
495 let encoded_did = self
496 .did()
497 .to_string()
498 .encode()
499 .map_err(Error::ServiceRegisterError)?;
500 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_touch_data(
501 &self.swarm,
502 name,
503 encoded_did,
504 )
505 .await
506 .map_err(Error::ServiceRegisterError)
507 }
508
509 pub async fn get_node_info(&self) -> Result<NodeInfoResponse> {
511 Ok(NodeInfoResponse {
512 version: crate::util::build_version(),
513 swarm: Some(self.swarm.inspect().await.into()),
514 })
515 }
516}
517
518#[cfg(test)]
519#[cfg(feature = "node")]
520mod test {
521 use std::sync::Mutex;
522 use std::time::Duration;
523 use std::time::Instant;
524
525 use rings_core::storage::MemStorage;
526 use rings_core::swarm::callback::SwarmCallback;
527 use rings_core::swarm::callback::SwarmEvent;
528 use rings_transport::core::transport::WebrtcConnectionState;
529 use tokio::sync::Notify;
530
531 use super::*;
532 use crate::prelude::*;
533 use crate::tests::native::prepare_processor;
534
535 #[tokio::test]
536 async fn test_processor_create_offer() {
537 let peer_did = SecretKey::random().address().into();
538 let processor = prepare_processor().await;
539 processor.swarm.create_offer(peer_did).await.unwrap();
540 let conn_dids = processor.swarm.peers();
541 assert_eq!(conn_dids.len(), 1);
542 assert_eq!(conn_dids.first().unwrap().did, peer_did.to_string());
543 }
544
545 struct SwarmCallbackInstance {
546 inbound: Mutex<Vec<Message>>,
547 inbound_notify: Notify,
548 connected_notify: Notify,
549 }
550
551 #[async_trait]
552 impl SwarmCallback for SwarmCallbackInstance {
553 async fn on_inbound(
554 &self,
555 payload: &MessagePayload,
556 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
557 let msg: Message = payload.transaction.data().map_err(Box::new)?;
558 {
559 let mut inbound = self.inbound.lock().unwrap();
560 inbound.push(msg);
561 }
562 self.inbound_notify.notify_one();
563
564 Ok(())
565 }
566
567 async fn on_event(
568 &self,
569 event: &SwarmEvent,
570 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
571 if let SwarmEvent::ConnectionStateChange {
572 state: WebrtcConnectionState::Connected,
573 ..
574 } = event
575 {
576 self.connected_notify.notify_one();
577 }
578
579 Ok(())
580 }
581 }
582
583 fn test_callback() -> Arc<SwarmCallbackInstance> {
584 Arc::new(SwarmCallbackInstance {
585 inbound: Mutex::new(Vec::new()),
586 inbound_notify: Notify::new(),
587 connected_notify: Notify::new(),
588 })
589 }
590
591 async fn prepare_processor_with_identity_key(identity_key: SecretKey) -> Processor {
592 let session_sk = SessionSk::new_with_seckey(&identity_key).unwrap();
593 let config = ProcessorConfig::new(
594 0,
595 "stun://stun.l.google.com:19302".to_string(),
596 session_sk,
597 3,
598 );
599 let storage = Box::new(MemStorage::new());
600
601 ProcessorBuilder::from_config(&config)
602 .unwrap()
603 .storage(storage)
604 .dht_finger_table_size(8)
605 .build()
606 .unwrap()
607 }
608
609 async fn connect_processors(
610 p1: &Processor,
611 p2: &Processor,
612 callback1: &SwarmCallbackInstance,
613 callback2: &SwarmCallbackInstance,
614 ) {
615 let offer = p1.swarm.create_offer(p2.did()).await.unwrap();
616 let answer = p2.swarm.answer_offer(offer).await.unwrap();
617 p1.swarm.accept_answer(answer).await.unwrap();
618 wait_processors_connected(p1, p2, callback1, callback2).await;
619 }
620
621 async fn wait_processors_connected(
622 p1: &Processor,
623 p2: &Processor,
624 callback1: &SwarmCallbackInstance,
625 callback2: &SwarmCallbackInstance,
626 ) {
627 let deadline = Instant::now() + Duration::from_secs(5);
628 loop {
629 if processor_has_connected_peer(p1, p2.did())
630 && processor_has_connected_peer(p2, p1.did())
631 {
632 return;
633 }
634
635 let remaining = deadline
636 .checked_duration_since(Instant::now())
637 .expect("processors did not connect");
638 tokio::time::timeout(remaining, async {
639 tokio::select! {
640 _ = callback1.connected_notify.notified() => {}
641 _ = callback2.connected_notify.notified() => {}
642 }
643 })
644 .await
645 .expect("processors did not connect");
646 }
647 }
648
649 fn processor_has_connected_peer(processor: &Processor, peer: Did) -> bool {
650 let peer = peer.to_string();
651 processor
652 .swarm
653 .peers()
654 .into_iter()
655 .any(|conn| conn.did == peer && conn.state == "Connected")
656 }
657
658 async fn wait_for_inbound_message(
659 callback: &SwarmCallbackInstance,
660 predicate: impl Fn(&Message) -> bool,
661 ) -> Message {
662 let deadline = Instant::now() + Duration::from_secs(5);
663 loop {
664 {
665 let inbound = callback.inbound.lock().unwrap();
666 if let Some(msg) = inbound.iter().find(|msg| predicate(msg)).cloned() {
667 return msg;
668 }
669 }
670
671 let remaining = deadline
672 .checked_duration_since(Instant::now())
673 .expect("inbound message was not delivered");
674 tokio::time::timeout(remaining, callback.inbound_notify.notified())
675 .await
676 .expect("inbound message was not delivered");
677 }
678 }
679
680 async fn wait_for_e2e_stream_frames(
681 callback: &SwarmCallbackInstance,
682 stream_id: e2e::E2eStreamId,
683 ) -> Vec<E2eStreamFrame> {
684 let deadline = Instant::now() + Duration::from_secs(5);
685 loop {
686 {
687 let inbound = callback.inbound.lock().unwrap();
688 let frames = inbound
689 .iter()
690 .filter_map(|msg| match msg {
691 Message::E2eStreamFrame(frame) if frame.stream_id == stream_id => {
692 Some(frame.clone())
693 }
694 _ => None,
695 })
696 .collect::<Vec<_>>();
697 if frames.iter().any(|frame| frame.is_final) {
698 return frames;
699 }
700 }
701
702 let remaining = deadline
703 .checked_duration_since(Instant::now())
704 .expect("E2E stream final frame was not delivered");
705 tokio::time::timeout(remaining, callback.inbound_notify.notified())
706 .await
707 .expect("E2E stream final frame was not delivered");
708 }
709 }
710
711 #[tokio::test]
712 async fn test_processor_handshake_msg() {
713 let callback1 = test_callback();
714 let callback2 = test_callback();
715
716 let p1 = prepare_processor().await;
717 let p2 = prepare_processor().await;
718
719 p1.swarm.set_callback(callback1.clone()).unwrap();
720 p2.swarm.set_callback(callback2.clone()).unwrap();
721
722 let did1 = p1.did();
723 let did2 = p2.did();
724
725 let offer = p1.swarm.create_offer(p2.did()).await.unwrap();
726 assert_eq!(
727 p1.swarm
728 .peers()
729 .into_iter()
730 .find(|peer| peer.did == p2.did().to_string())
731 .unwrap()
732 .state,
733 "New"
734 );
735
736 let answer = p2.swarm.answer_offer(offer).await.unwrap();
737 p1.swarm.accept_answer(answer).await.unwrap();
738 wait_processors_connected(&p1, &p2, &callback1, &callback2).await;
739
740 let test_text1 = "test1";
741 let test_text2 = "test2";
742
743 p1.send_message(did2, test_text1.as_bytes()).await.unwrap();
744 p2.send_message(did1, test_text2.as_bytes()).await.unwrap();
745
746 let got_msg2 = wait_for_inbound_message(&callback2, |msg| {
747 matches!(msg, Message::CustomMessage(custom) if custom.0 == test_text1.as_bytes())
748 })
749 .await;
750 assert!(matches!(got_msg2, Message::CustomMessage(_)));
751
752 let got_msg1 = wait_for_inbound_message(&callback1, |msg| {
753 matches!(msg, Message::CustomMessage(custom) if custom.0 == test_text2.as_bytes())
754 })
755 .await;
756 assert!(matches!(got_msg1, Message::CustomMessage(_)));
757 }
758
759 #[tokio::test]
760 async fn test_processor_e2e_handshake_exchanges_verified_public_keys() {
761 let callback1 = test_callback();
762 let callback2 = test_callback();
763
764 let p1 = prepare_processor().await;
765 let p2 = prepare_processor().await;
766
767 p1.swarm.set_callback(callback1.clone()).unwrap();
768 p2.swarm.set_callback(callback2.clone()).unwrap();
769
770 connect_processors(&p1, &p2, &callback1, &callback2).await;
771
772 let did1 = p1.did();
773 let did2 = p2.did();
774 let requester_public_key = p1.swarm.account_pubkey().unwrap();
775 let responder_public_key = p2.swarm.account_pubkey().unwrap();
776
777 p1.send_e2e_handshake(did2).await.unwrap();
778
779 let request = wait_for_inbound_message(&callback2, |msg| {
780 matches!(msg, Message::E2eHandshakeRequest(_))
781 })
782 .await;
783 match request {
784 Message::E2eHandshakeRequest(request) => {
785 assert_eq!(request.requester_public_key, requester_public_key);
786 assert_eq!(
787 p2.verify_e2e_handshake_request(did1, &request).unwrap(),
788 requester_public_key
789 );
790 }
791 msg => panic!("expected E2eHandshakeRequest, got {msg:?}"),
792 }
793
794 let response = wait_for_inbound_message(&callback1, |msg| {
795 matches!(msg, Message::E2eHandshakeResponse(_))
796 })
797 .await;
798 match response {
799 Message::E2eHandshakeResponse(response) => {
800 assert_eq!(response.responder_public_key, responder_public_key);
801 assert_eq!(
802 p1.verify_e2e_handshake_response(did2, &response).unwrap(),
803 responder_public_key
804 );
805 }
806 msg => panic!("expected E2eHandshakeResponse, got {msg:?}"),
807 }
808 }
809
810 #[tokio::test]
811 async fn test_processor_e2e_message_streams_and_decrypts_with_receiver_identity_key() {
812 let callback1 = test_callback();
813 let callback2 = test_callback();
814 let identity1 = SecretKey::random();
815 let identity2 = SecretKey::random();
816
817 let p1 = prepare_processor_with_identity_key(identity1).await;
818 let p2 = prepare_processor_with_identity_key(identity2).await;
819
820 p1.swarm.set_callback(callback1.clone()).unwrap();
821 p2.swarm.set_callback(callback2.clone()).unwrap();
822
823 connect_processors(&p1, &p2, &callback1, &callback2).await;
824
825 let did1 = p1.did();
826 let did2 = p2.did();
827 let responder_public_key = p2.swarm.account_pubkey().unwrap();
828 let stream_id = p1
829 .send_e2e_message_with_frame_len(
830 did2,
831 responder_public_key,
832 b"homomorphic-ready streaming body",
833 8,
834 )
835 .await
836 .unwrap();
837
838 let frames = wait_for_e2e_stream_frames(&callback2, stream_id).await;
839 assert!(
840 frames.len() > 1,
841 "streaming send should emit more than one frame for this frame size"
842 );
843 assert_eq!(
844 frames.iter().filter(|frame| frame.is_final).count(),
845 1,
846 "streaming send should emit exactly one final frame"
847 );
848
849 let mut sequences = frames
850 .iter()
851 .map(|frame| frame.sequence)
852 .collect::<Vec<_>>();
853 sequences.sort_unstable();
854 let frame_count = u64::try_from(frames.len()).unwrap();
855 assert_eq!(sequences, (0..frame_count).collect::<Vec<_>>());
856
857 let mut decryptor = p2.e2e_stream_decryptor(did1, stream_id, identity2).unwrap();
858 let mut plaintext = Vec::new();
859 let mut delivered_frames = frames.clone();
860 delivered_frames.reverse();
861 for frame in &delivered_frames {
862 plaintext
863 .extend_from_slice(&p2.decrypt_e2e_stream_frame(&mut decryptor, frame).unwrap());
864 }
865 decryptor.finish().unwrap();
866 assert_eq!(plaintext, b"homomorphic-ready streaming body");
867
868 assert!(matches!(
869 p2.e2e_stream_decryptor(did1, stream_id, SecretKey::random()),
870 Err(Error::CoreError(
871 rings_core::error::Error::E2ePublicKeyDidMismatch { .. }
872 ))
873 ));
874 }
875}