1use std::io;
14use std::io::Read;
15use std::path::Path;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::Arc;
18use std::thread;
19use std::time::Duration;
20
21use rns_core::destination::{destination_hash, name_hash};
22use rns_core::packet::RawPacket;
23use rns_core::transport::types::TransportConfig;
24use rns_crypto::identity::Identity;
25
26use crate::driver::{Callbacks, Driver};
27use crate::event;
28use crate::event::Event;
29use crate::hdlc;
30use crate::interface::local::LocalClientConfig;
31use crate::interface::{InterfaceEntry, InterfaceStats};
32use crate::node::RnsNode;
33use crate::storage;
34use crate::time;
35
36pub struct SharedClientConfig {
38 pub instance_name: String,
40 pub port: u16,
42 pub rpc_port: u16,
44}
45
46impl Default for SharedClientConfig {
47 fn default() -> Self {
48 SharedClientConfig {
49 instance_name: "default".into(),
50 port: 37428,
51 rpc_port: 37429,
52 }
53 }
54}
55
56impl RnsNode {
57 pub fn connect_shared(
63 config: SharedClientConfig,
64 callbacks: Box<dyn Callbacks>,
65 ) -> io::Result<Self> {
66 Self::connect_shared_with_reconnect_wait(config, callbacks, Duration::from_secs(8))
67 }
68
69 fn connect_shared_with_reconnect_wait(
70 config: SharedClientConfig,
71 callbacks: Box<dyn Callbacks>,
72 reconnect_wait: Duration,
73 ) -> io::Result<Self> {
74 let transport_config = TransportConfig {
75 transport_enabled: false,
76 identity_hash: None,
77 prefer_shorter_path: false,
78 max_paths_per_destination: 1,
79 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
80 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
81 max_path_destinations: rns_core::transport::types::DEFAULT_MAX_PATH_DESTINATIONS,
82 max_tunnel_destinations_total: usize::MAX,
83 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
84 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
85 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
86 announce_sig_cache_enabled: true,
87 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
88 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
89 announce_queue_max_entries: 256,
90 announce_queue_max_interfaces: 1024,
91 };
92
93 let (tx, rx) = event::channel();
94 let tick_interval_ms = Arc::new(AtomicU64::new(1000));
95 let mut driver = Driver::new(transport_config, rx, tx.clone(), callbacks);
96 driver.set_tick_interval_handle(Arc::clone(&tick_interval_ms));
97
98 let local_config = LocalClientConfig {
100 name: "Local shared instance".into(),
101 instance_name: config.instance_name.clone(),
102 port: config.port,
103 interface_id: rns_core::transport::types::InterfaceId(1),
104 reconnect_wait,
105 };
106
107 let id = local_config.interface_id;
108 let info = rns_core::transport::types::InterfaceInfo {
109 id,
110 name: "LocalInterface".into(),
111 mode: rns_core::constants::MODE_FULL,
112 out_capable: true,
113 in_capable: true,
114 bitrate: Some(1_000_000_000),
115 airtime_profile: None,
116 announce_rate_target: None,
117 announce_rate_grace: 0,
118 announce_rate_penalty: 0.0,
119 announce_cap: rns_core::constants::ANNOUNCE_CAP,
120 is_local_client: true,
121 wants_tunnel: false,
122 tunnel_id: None,
123 mtu: 65535,
124 ia_freq: 0.0,
125 ip_freq: 0.0,
126 op_freq: 0.0,
127 op_samples: 0,
128 started: time::now(),
129 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
130 };
131
132 let writer = crate::interface::local::start_client(local_config, tx.clone())?;
133
134 driver.engine.register_interface(info.clone());
135 driver.interfaces.insert(
136 id,
137 InterfaceEntry {
138 id,
139 info,
140 writer,
141 async_writer_metrics: None,
142 enabled: true,
143 online: false,
144 dynamic: false,
145 ifac: None,
146 stats: InterfaceStats {
147 started: time::now(),
148 ..Default::default()
149 },
150 interface_type: "LocalClientInterface".to_string(),
151 send_retry_at: None,
152 send_retry_backoff: Duration::ZERO,
153 },
154 );
155
156 let timer_tx = tx.clone();
158 let timer_interval = Arc::clone(&tick_interval_ms);
159 thread::Builder::new()
160 .name("rns-timer-client".into())
161 .spawn(move || loop {
162 let ms = timer_interval.load(Ordering::Relaxed);
163 thread::sleep(Duration::from_millis(ms));
164 if timer_tx.send(event::Event::Tick).is_err() {
165 break;
166 }
167 })?;
168
169 let driver_handle = thread::Builder::new()
171 .name("rns-driver-client".into())
172 .spawn(move || {
173 driver.run();
174 })?;
175
176 Ok(RnsNode::from_parts(
177 tx,
178 driver_handle,
179 None,
180 tick_interval_ms,
181 ))
182 }
183
184 pub fn connect_shared_from_config(
188 config_path: Option<&Path>,
189 callbacks: Box<dyn Callbacks>,
190 ) -> io::Result<Self> {
191 let config_dir = storage::resolve_config_dir(config_path);
192
193 let config_file = config_dir.join("config");
195 let rns_config = if config_file.exists() {
196 crate::config::parse_file(&config_file)
197 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
198 } else {
199 crate::config::parse("")
200 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
201 };
202
203 let shared_config = SharedClientConfig {
204 instance_name: rns_config.reticulum.instance_name.clone(),
205 port: rns_config.reticulum.shared_instance_port,
206 rpc_port: rns_config.reticulum.instance_control_port,
207 };
208
209 Self::connect_shared(shared_config, callbacks)
210 }
211}
212
213#[doc(hidden)]
214pub fn bench_shared_client_replay_once(
215 announce_count: usize,
216 reconnect_wait: Duration,
217) -> io::Result<usize> {
218 struct BenchNoopCallbacks;
219 impl Callbacks for BenchNoopCallbacks {
220 fn on_announce(&mut self, _: crate::destination::AnnouncedIdentity) {}
221 fn on_path_updated(&mut self, _: rns_core::types::DestHash, _: u8) {}
222 fn on_local_delivery(
223 &mut self,
224 _: rns_core::types::DestHash,
225 _: Vec<u8>,
226 _: rns_core::types::PacketHash,
227 ) {
228 }
229 }
230
231 fn build_shared_announce_raw(
232 dest_hash: &[u8; 16],
233 name_hash: &[u8; 10],
234 identity_prv_key: &[u8; 64],
235 app_data: Option<&[u8]>,
236 path_response: bool,
237 ) -> Vec<u8> {
238 let identity = Identity::from_private_key(identity_prv_key);
239 let mut random_hash = [0u8; 10];
240 random_hash[..5].copy_from_slice(&[0xA5; 5]);
241 random_hash[5..10].copy_from_slice(&[0, 0, 0, 0, 1]);
242
243 let (announce_data, _) = rns_core::announce::AnnounceData::pack(
244 &identity,
245 dest_hash,
246 name_hash,
247 &random_hash,
248 None,
249 app_data,
250 )
251 .unwrap();
252
253 let flags = rns_core::packet::PacketFlags {
254 header_type: rns_core::constants::HEADER_1,
255 context_flag: rns_core::constants::FLAG_UNSET,
256 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
257 destination_type: rns_core::constants::DESTINATION_SINGLE,
258 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
259 };
260 let context = if path_response {
261 rns_core::constants::CONTEXT_PATH_RESPONSE
262 } else {
263 rns_core::constants::CONTEXT_NONE
264 };
265
266 rns_core::packet::RawPacket::pack(flags, 0, dest_hash, None, context, &announce_data)
267 .unwrap()
268 .raw
269 }
270
271 fn read_until_frames(
272 stream: &mut std::net::TcpStream,
273 expected: usize,
274 expected_context: u8,
275 ) -> io::Result<Vec<Vec<u8>>> {
276 let mut decoder = hdlc::Decoder::new();
277 let mut buf = [0u8; 4096];
278 let mut frames = Vec::new();
279 let deadline = std::time::Instant::now() + Duration::from_secs(2);
280 while frames.len() < expected {
281 let n = match stream.read(&mut buf) {
282 Ok(n) => n,
283 Err(e)
284 if e.kind() == io::ErrorKind::WouldBlock
285 || e.kind() == io::ErrorKind::TimedOut =>
286 {
287 if std::time::Instant::now() >= deadline {
288 return Err(io::Error::new(
289 io::ErrorKind::TimedOut,
290 format!(
291 "timed out waiting for {} frames, got {}",
292 expected,
293 frames.len()
294 ),
295 ));
296 }
297 continue;
298 }
299 Err(e) => return Err(e),
300 };
301 for frame in decoder.feed(&buf[..n]) {
302 let packet = RawPacket::unpack(&frame)
303 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{e}")))?;
304 if packet.context == expected_context {
305 frames.push(frame);
306 }
307 }
308 }
309 Ok(frames)
310 }
311
312 let port = {
313 let listener = std::net::TcpListener::bind("127.0.0.1:0")?;
314 listener.local_addr()?.port()
315 };
316 let instance_name = format!("bench-shared-replay-{port}");
317
318 let listener1 = std::net::TcpListener::bind(format!("127.0.0.1:{port}"))?;
319 let (accepted1_tx, accepted1_rx) = std::sync::mpsc::channel();
320 thread::spawn(move || {
321 let (stream, _) = listener1.accept().unwrap();
322 accepted1_tx.send(stream).unwrap();
323 });
324
325 let transport_config = TransportConfig {
326 transport_enabled: false,
327 identity_hash: None,
328 prefer_shorter_path: false,
329 max_paths_per_destination: 1,
330 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
331 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
332 max_path_destinations: rns_core::transport::types::DEFAULT_MAX_PATH_DESTINATIONS,
333 max_tunnel_destinations_total: usize::MAX,
334 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
335 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
336 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
337 announce_sig_cache_enabled: true,
338 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
339 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
340 announce_queue_max_entries: 256,
341 announce_queue_max_interfaces: 1024,
342 };
343
344 let (tx, rx) = event::channel();
345 let tick_interval_ms = Arc::new(AtomicU64::new(1000));
346 let mut driver = Driver::new(
347 transport_config,
348 rx,
349 tx.clone(),
350 Box::new(BenchNoopCallbacks),
351 );
352 driver.set_tick_interval_handle(Arc::clone(&tick_interval_ms));
353
354 let local_config = LocalClientConfig {
355 name: "Shared replay bench".into(),
356 instance_name: instance_name.clone(),
357 port,
358 interface_id: rns_core::transport::types::InterfaceId(1),
359 reconnect_wait,
360 };
361
362 let id = local_config.interface_id;
363 let info = rns_core::transport::types::InterfaceInfo {
364 id,
365 name: "LocalInterface".into(),
366 mode: rns_core::constants::MODE_FULL,
367 out_capable: true,
368 in_capable: true,
369 bitrate: Some(1_000_000_000),
370 airtime_profile: None,
371 announce_rate_target: None,
372 announce_rate_grace: 0,
373 announce_rate_penalty: 0.0,
374 announce_cap: rns_core::constants::ANNOUNCE_CAP,
375 is_local_client: true,
376 wants_tunnel: false,
377 tunnel_id: None,
378 mtu: 65535,
379 ia_freq: 0.0,
380 ip_freq: 0.0,
381 op_freq: 0.0,
382 op_samples: 0,
383 started: time::now(),
384 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
385 };
386
387 let writer = crate::interface::local::start_client(local_config, tx.clone())?;
388 driver.engine.register_interface(info.clone());
389 driver.interfaces.insert(
390 id,
391 InterfaceEntry {
392 id,
393 info,
394 writer,
395 async_writer_metrics: None,
396 enabled: true,
397 online: false,
398 dynamic: false,
399 ifac: None,
400 stats: InterfaceStats {
401 started: time::now(),
402 ..Default::default()
403 },
404 interface_type: "LocalClientInterface".to_string(),
405 send_retry_at: None,
406 send_retry_backoff: Duration::ZERO,
407 },
408 );
409
410 let driver_handle = thread::Builder::new()
411 .name("rns-driver-bench-client".into())
412 .spawn(move || {
413 driver.run();
414 })?;
415
416 let mut stream1 = accepted1_rx
417 .recv_timeout(Duration::from_secs(2))
418 .map_err(|e| {
419 io::Error::new(
420 io::ErrorKind::TimedOut,
421 format!("shared bench initial accept failed: {e}"),
422 )
423 })?;
424 stream1.set_read_timeout(Some(Duration::from_secs(2)))?;
425
426 let mut records = Vec::new();
427 for i in 0..announce_count {
428 let mut prv_key = [0u8; 64];
429 for (j, byte) in prv_key.iter_mut().enumerate() {
430 *byte = (i as u8)
431 .wrapping_mul(23)
432 .wrapping_add(j as u8)
433 .wrapping_add(5);
434 }
435 let identity = Identity::from_private_key(&prv_key);
436 let aspect = format!("echo-{i}");
437 let name_hash = name_hash("shared-bench", &[&aspect]);
438 let dest_hash = destination_hash("shared-bench", &[&aspect], Some(identity.hash()));
439 let app_data = format!("hello-{i}").into_bytes();
440 records.push((dest_hash, name_hash, prv_key, app_data));
441 }
442
443 for (dest_hash, name_hash, prv_key, app_data) in &records {
444 let raw = build_shared_announce_raw(dest_hash, name_hash, prv_key, Some(app_data), false);
445 tx.send(Event::StoreSharedAnnounce {
446 dest_hash: *dest_hash,
447 name_hash: *name_hash,
448 identity_prv_key: *prv_key,
449 app_data: Some(app_data.clone()),
450 })
451 .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, format!("{e}")))?;
452 tx.send(Event::SendOutbound {
453 raw,
454 dest_type: rns_core::constants::DESTINATION_SINGLE,
455 attached_interface: None,
456 })
457 .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, format!("{e}")))?;
458 }
459
460 let _ = read_until_frames(
461 &mut stream1,
462 announce_count,
463 rns_core::constants::CONTEXT_NONE,
464 )?;
465 drop(stream1);
466
467 let listener2 = std::net::TcpListener::bind(format!("127.0.0.1:{port}"))?;
468 let (accepted2_tx, accepted2_rx) = std::sync::mpsc::channel();
469 thread::spawn(move || {
470 let (stream, _) = listener2.accept().unwrap();
471 accepted2_tx.send(stream).unwrap();
472 });
473
474 let mut stream2 = accepted2_rx
475 .recv_timeout(Duration::from_secs(2))
476 .map_err(|e| {
477 io::Error::new(
478 io::ErrorKind::TimedOut,
479 format!("shared bench reconnect accept failed: {e}"),
480 )
481 })?;
482 stream2.set_read_timeout(Some(Duration::from_secs(2)))?;
483 let frames = read_until_frames(
484 &mut stream2,
485 announce_count,
486 rns_core::constants::CONTEXT_PATH_RESPONSE,
487 )?;
488
489 tx.send(Event::Shutdown)
490 .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, format!("{e}")))?;
491 let _ = driver_handle.join();
492
493 Ok(frames.len())
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499 use crate::hdlc;
500 use rns_core::packet::RawPacket;
501 use rns_core::types::IdentityHash;
502 use rns_crypto::identity::Identity;
503 use rns_crypto::OsRng;
504 use std::io::Read;
505 use std::sync::atomic::AtomicU64;
506 use std::sync::mpsc;
507 use std::sync::Arc;
508
509 use crate::interface::local::LocalServerConfig;
510
511 struct NoopCallbacks;
512 impl Callbacks for NoopCallbacks {
513 fn on_announce(&mut self, _: crate::destination::AnnouncedIdentity) {}
514 fn on_path_updated(&mut self, _: rns_core::types::DestHash, _: u8) {}
515 fn on_local_delivery(
516 &mut self,
517 _: rns_core::types::DestHash,
518 _: Vec<u8>,
519 _: rns_core::types::PacketHash,
520 ) {
521 }
522 }
523
524 fn find_free_port() -> u16 {
525 std::net::TcpListener::bind("127.0.0.1:0")
526 .unwrap()
527 .local_addr()
528 .unwrap()
529 .port()
530 }
531
532 #[test]
533 fn connect_shared_to_tcp_server() {
534 let port = find_free_port();
535 let next_id = Arc::new(AtomicU64::new(50000));
536 let (server_tx, server_rx) = crate::event::channel();
537
538 let server_config = LocalServerConfig {
540 instance_name: "test-shared-connect".into(),
541 port,
542 interface_id: rns_core::transport::types::InterfaceId(99),
543 };
544
545 crate::interface::local::start_server(server_config, server_tx, next_id).unwrap();
546 thread::sleep(Duration::from_millis(50));
547
548 let config = SharedClientConfig {
550 instance_name: "test-shared-connect".into(),
551 port,
552 rpc_port: 0,
553 };
554
555 let node = RnsNode::connect_shared(config, Box::new(NoopCallbacks)).unwrap();
556
557 let event = server_rx.recv_timeout(Duration::from_secs(2)).unwrap();
559 assert!(matches!(event, crate::event::Event::InterfaceUp(_, _, _)));
560
561 node.shutdown();
562 }
563
564 #[test]
565 fn shared_client_register_destination() {
566 let port = find_free_port();
567 let next_id = Arc::new(AtomicU64::new(51000));
568 let (server_tx, _server_rx) = crate::event::channel();
569
570 let server_config = LocalServerConfig {
571 instance_name: "test-shared-reg".into(),
572 port,
573 interface_id: rns_core::transport::types::InterfaceId(98),
574 };
575
576 crate::interface::local::start_server(server_config, server_tx, next_id).unwrap();
577 thread::sleep(Duration::from_millis(50));
578
579 let config = SharedClientConfig {
580 instance_name: "test-shared-reg".into(),
581 port,
582 rpc_port: 0,
583 };
584
585 let node = RnsNode::connect_shared(config, Box::new(NoopCallbacks)).unwrap();
586
587 let dest_hash = [0xAA; 16];
589 node.register_destination(dest_hash, rns_core::constants::DESTINATION_SINGLE)
590 .unwrap();
591
592 thread::sleep(Duration::from_millis(100));
594
595 node.shutdown();
596 }
597
598 #[test]
599 fn shared_client_send_packet() {
600 let port = find_free_port();
601 let next_id = Arc::new(AtomicU64::new(52000));
602 let (server_tx, server_rx) = crate::event::channel();
603
604 let server_config = LocalServerConfig {
605 instance_name: "test-shared-send".into(),
606 port,
607 interface_id: rns_core::transport::types::InterfaceId(97),
608 };
609
610 crate::interface::local::start_server(server_config, server_tx, next_id).unwrap();
611 thread::sleep(Duration::from_millis(50));
612
613 let config = SharedClientConfig {
614 instance_name: "test-shared-send".into(),
615 port,
616 rpc_port: 0,
617 };
618
619 let node = RnsNode::connect_shared(config, Box::new(NoopCallbacks)).unwrap();
620
621 let raw = vec![0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD]; node.send_raw(raw, rns_core::constants::DESTINATION_PLAIN, None)
624 .unwrap();
625
626 for _ in 0..10 {
629 match server_rx.recv_timeout(Duration::from_secs(1)) {
630 Ok(crate::event::Event::Frame { .. }) => {
631 break;
632 }
633 Ok(_) => continue,
634 Err(_) => break,
635 }
636 }
637 node.shutdown();
641 }
642
643 #[test]
644 fn shared_client_replays_single_announces_after_reconnect() {
645 let port = find_free_port();
646 let addr = format!("127.0.0.1:{}", port);
647 let instance_name = format!("test-shared-replay-{}", port);
648
649 let listener1 = std::net::TcpListener::bind(&addr).unwrap();
650 let (accepted1_tx, accepted1_rx) = mpsc::channel();
651 thread::spawn(move || {
652 let (stream, _) = listener1.accept().unwrap();
653 accepted1_tx.send(stream).unwrap();
654 });
655
656 let node = RnsNode::connect_shared(
657 SharedClientConfig {
658 instance_name,
659 port,
660 rpc_port: 0,
661 },
662 Box::new(NoopCallbacks),
663 )
664 .unwrap();
665
666 let identity = Identity::new(&mut OsRng);
667 let dest = crate::destination::Destination::single_in(
668 "shared-replay",
669 &["echo"],
670 IdentityHash(*identity.hash()),
671 );
672 node.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())
673 .unwrap();
674 node.announce(&dest, &identity, Some(b"hello")).unwrap();
675
676 let mut stream1 = accepted1_rx.recv_timeout(Duration::from_secs(2)).unwrap();
677 stream1
678 .set_read_timeout(Some(Duration::from_secs(2)))
679 .unwrap();
680
681 let mut decoder = hdlc::Decoder::new();
682 let mut buf = [0u8; 4096];
683 let n = stream1.read(&mut buf).unwrap();
684 let frames = decoder.feed(&buf[..n]);
685 assert!(!frames.is_empty(), "expected initial announce frame");
686 let packet1 = RawPacket::unpack(&frames[0]).unwrap();
687 assert_eq!(packet1.destination_hash, dest.hash.0);
688 assert_eq!(packet1.context, rns_core::constants::CONTEXT_NONE);
689
690 drop(stream1);
691
692 let listener2 = std::net::TcpListener::bind(&addr).unwrap();
693 let (accepted2_tx, accepted2_rx) = mpsc::channel();
694 thread::spawn(move || {
695 let (stream, _) = listener2.accept().unwrap();
696 accepted2_tx.send(stream).unwrap();
697 });
698
699 let mut stream2 = accepted2_rx.recv_timeout(Duration::from_secs(15)).unwrap();
700 stream2
701 .set_read_timeout(Some(Duration::from_secs(15)))
702 .unwrap();
703
704 let mut decoder = hdlc::Decoder::new();
705 let n = stream2.read(&mut buf).unwrap();
706 let frames = decoder.feed(&buf[..n]);
707 assert!(!frames.is_empty(), "expected replayed announce frame");
708 let packet2 = RawPacket::unpack(&frames[0]).unwrap();
709 assert_eq!(packet2.destination_hash, dest.hash.0);
710 assert_eq!(packet2.context, rns_core::constants::CONTEXT_PATH_RESPONSE);
711
712 node.shutdown();
713 }
714
715 #[test]
716 fn connect_shared_fails_no_server() {
717 let port = find_free_port();
718
719 let config = SharedClientConfig {
720 instance_name: "nonexistent-instance-12345".into(),
721 port,
722 rpc_port: 0,
723 };
724
725 let result = RnsNode::connect_shared(config, Box::new(NoopCallbacks));
727 assert!(result.is_err());
728 }
729
730 #[test]
731 fn shared_config_defaults() {
732 let config = SharedClientConfig::default();
733 assert_eq!(config.instance_name, "default");
734 assert_eq!(config.port, 37428);
735 assert_eq!(config.rpc_port, 37429);
736 }
737}