1pub use crate::{
25 discovery::DEFAULT_KADEMLIA_REPLICATION_FACTOR,
26 peer_store::PeerStoreProvider,
27 protocol::{notification_service, NotificationsSink, ProtocolHandlePair},
28 request_responses::{
29 IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
30 },
31 service::{
32 metrics::NotificationMetrics,
33 traits::{NotificationConfig, NotificationService, PeerStore},
34 },
35 types::ProtocolName,
36};
37
38pub use sc_network_types::{build_multiaddr, ed25519};
39use sc_network_types::{
40 multiaddr::{self, Multiaddr},
41 PeerId,
42};
43
44use crate::service::{ensure_addresses_consistent_with_transport, traits::NetworkBackend};
45use codec::Encode;
46use prometheus_endpoint::Registry;
47use zeroize::Zeroize;
48
49pub use sc_network_common::{
50 role::{Role, Roles},
51 sync::SyncMode,
52 ExHashT,
53};
54
55use sp_runtime::traits::Block as BlockT;
56
57use std::{
58 error::Error,
59 fmt, fs,
60 future::Future,
61 io::{self, Write},
62 iter,
63 net::Ipv4Addr,
64 num::NonZeroUsize,
65 path::{Path, PathBuf},
66 pin::Pin,
67 str::{self, FromStr},
68 sync::Arc,
69 time::Duration,
70};
71
72pub const DEFAULT_IDLE_CONNECTION_TIMEOUT: Duration = Duration::from_secs(10);
76
77pub const KADEMLIA_MAX_PROVIDER_KEYS: usize = 10000;
81
82pub const KADEMLIA_PROVIDER_RECORD_TTL: Duration = Duration::from_secs(10 * 3600);
86
87pub const KADEMLIA_PROVIDER_REPUBLISH_INTERVAL: Duration = Duration::from_secs(12600);
91
92#[derive(Clone, PartialEq, Eq, Hash)]
96pub struct ProtocolId(smallvec::SmallVec<[u8; 6]>);
97
98impl<'a> From<&'a str> for ProtocolId {
99 fn from(bytes: &'a str) -> ProtocolId {
100 Self(bytes.as_bytes().into())
101 }
102}
103
104impl AsRef<str> for ProtocolId {
105 fn as_ref(&self) -> &str {
106 str::from_utf8(&self.0[..])
107 .expect("the only way to build a ProtocolId is through a UTF-8 String; qed")
108 }
109}
110
111impl fmt::Debug for ProtocolId {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 fmt::Debug::fmt(self.as_ref(), f)
114 }
115}
116
117pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> {
132 let addr: Multiaddr = addr_str.parse()?;
133 parse_addr(addr)
134}
135
136pub fn parse_addr(mut addr: Multiaddr) -> Result<(PeerId, Multiaddr), ParseErr> {
138 let multihash = match addr.pop() {
139 Some(multiaddr::Protocol::P2p(multihash)) => multihash,
140 _ => return Err(ParseErr::PeerIdMissing),
141 };
142 let peer_id = PeerId::from_multihash(multihash).map_err(|_| ParseErr::InvalidPeerId)?;
143
144 Ok((peer_id, addr))
145}
146
147#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
162#[serde(try_from = "String", into = "String")]
163pub struct MultiaddrWithPeerId {
164 pub multiaddr: Multiaddr,
166 pub peer_id: PeerId,
168}
169
170impl MultiaddrWithPeerId {
171 pub fn concat(&self) -> Multiaddr {
173 let mut addr = self.multiaddr.clone();
174 if matches!(addr.iter().last(), Some(multiaddr::Protocol::P2p(_))) {
176 addr.pop();
177 }
178 addr.with(multiaddr::Protocol::P2p(From::from(self.peer_id)))
179 }
180}
181
182impl fmt::Display for MultiaddrWithPeerId {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 fmt::Display::fmt(&self.concat(), f)
185 }
186}
187
188impl FromStr for MultiaddrWithPeerId {
189 type Err = ParseErr;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 let (peer_id, multiaddr) = parse_str_addr(s)?;
193 Ok(Self { peer_id, multiaddr })
194 }
195}
196
197impl From<MultiaddrWithPeerId> for String {
198 fn from(ma: MultiaddrWithPeerId) -> String {
199 format!("{}", ma)
200 }
201}
202
203impl TryFrom<String> for MultiaddrWithPeerId {
204 type Error = ParseErr;
205 fn try_from(string: String) -> Result<Self, Self::Error> {
206 string.parse()
207 }
208}
209
210#[derive(Debug)]
212pub enum ParseErr {
213 MultiaddrParse(multiaddr::ParseError),
215 InvalidPeerId,
217 PeerIdMissing,
219}
220
221impl fmt::Display for ParseErr {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 match self {
224 Self::MultiaddrParse(err) => write!(f, "{}", err),
225 Self::InvalidPeerId => write!(f, "Peer id at the end of the address is invalid"),
226 Self::PeerIdMissing => write!(f, "Peer id is missing from the address"),
227 }
228 }
229}
230
231impl std::error::Error for ParseErr {
232 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
233 match self {
234 Self::MultiaddrParse(err) => Some(err),
235 Self::InvalidPeerId => None,
236 Self::PeerIdMissing => None,
237 }
238 }
239}
240
241impl From<multiaddr::ParseError> for ParseErr {
242 fn from(err: multiaddr::ParseError) -> ParseErr {
243 Self::MultiaddrParse(err)
244 }
245}
246
247#[derive(Debug, Clone)]
249pub struct NotificationHandshake(Vec<u8>);
250
251impl NotificationHandshake {
252 pub fn new<H: Encode>(handshake: H) -> Self {
254 Self(handshake.encode())
255 }
256
257 pub fn from_bytes(bytes: Vec<u8>) -> Self {
259 Self(bytes)
260 }
261}
262
263impl std::ops::Deref for NotificationHandshake {
264 type Target = Vec<u8>;
265
266 fn deref(&self) -> &Self::Target {
267 &self.0
268 }
269}
270
271#[derive(Clone, Debug)]
273pub enum TransportConfig {
274 Normal {
276 enable_mdns: bool,
279
280 allow_private_ip: bool,
284 },
285
286 MemoryOnly,
289}
290
291#[derive(Clone, Debug, PartialEq, Eq)]
293pub enum NonReservedPeerMode {
294 Accept,
296 Deny,
298}
299
300impl NonReservedPeerMode {
301 pub fn parse(s: &str) -> Option<Self> {
303 match s {
304 "accept" => Some(Self::Accept),
305 "deny" => Some(Self::Deny),
306 _ => None,
307 }
308 }
309
310 pub fn is_reserved_only(&self) -> bool {
312 matches!(self, NonReservedPeerMode::Deny)
313 }
314}
315
316#[derive(Clone, Debug)]
320pub enum NodeKeyConfig {
321 Ed25519(Secret<ed25519::SecretKey>),
323}
324
325impl Default for NodeKeyConfig {
326 fn default() -> NodeKeyConfig {
327 Self::Ed25519(Secret::New)
328 }
329}
330
331pub type Ed25519Secret = Secret<ed25519::SecretKey>;
333
334#[derive(Clone)]
336pub enum Secret<K> {
337 Input(K),
339 File(PathBuf),
345 New,
347}
348
349impl<K> fmt::Debug for Secret<K> {
350 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351 match self {
352 Self::Input(_) => f.debug_tuple("Secret::Input").finish(),
353 Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(),
354 Self::New => f.debug_tuple("Secret::New").finish(),
355 }
356 }
357}
358
359impl NodeKeyConfig {
360 pub fn into_keypair(self) -> io::Result<ed25519::Keypair> {
371 use NodeKeyConfig::*;
372 match self {
373 Ed25519(Secret::New) => Ok(ed25519::Keypair::generate()),
374
375 Ed25519(Secret::Input(k)) => Ok(ed25519::Keypair::from(k).into()),
376
377 Ed25519(Secret::File(f)) => get_secret(
378 f,
379 |mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| {
380 if s.len() == 64 {
381 array_bytes::hex2bytes(&s).ok()
382 } else {
383 None
384 }
385 }) {
386 Some(s) => ed25519::SecretKey::try_from_bytes(s),
387 _ => ed25519::SecretKey::try_from_bytes(&mut b),
388 },
389 ed25519::SecretKey::generate,
390 |b| b.as_ref().to_vec(),
391 )
392 .map(ed25519::Keypair::from),
393 }
394 }
395}
396
397fn get_secret<P, F, G, E, W, K>(file: P, parse: F, generate: G, serialize: W) -> io::Result<K>
401where
402 P: AsRef<Path>,
403 F: for<'r> FnOnce(&'r mut [u8]) -> Result<K, E>,
404 G: FnOnce() -> K,
405 E: Error + Send + Sync + 'static,
406 W: Fn(&K) -> Vec<u8>,
407{
408 std::fs::read(&file)
409 .and_then(|mut sk_bytes| {
410 parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
411 })
412 .or_else(|e| {
413 if e.kind() == io::ErrorKind::NotFound {
414 file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?;
415 let sk = generate();
416 let mut sk_vec = serialize(&sk);
417 write_secret_file(file, &sk_vec)?;
418 sk_vec.zeroize();
419 Ok(sk)
420 } else {
421 Err(e)
422 }
423 })
424}
425
426pub(super) fn write_secret_file<P>(path: P, sk_bytes: &[u8]) -> io::Result<()>
428where
429 P: AsRef<Path>,
430{
431 let mut file = open_secret_file(&path)?;
432 file.write_all(sk_bytes)
433}
434
435#[cfg(unix)]
437fn open_secret_file<P>(path: P) -> io::Result<fs::File>
438where
439 P: AsRef<Path>,
440{
441 use std::os::unix::fs::OpenOptionsExt;
442 fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path)
443}
444
445#[cfg(not(unix))]
447fn open_secret_file<P>(path: P) -> Result<fs::File, io::Error>
448where
449 P: AsRef<Path>,
450{
451 fs::OpenOptions::new().write(true).create_new(true).open(path)
452}
453
454#[derive(Clone, Debug)]
456pub struct SetConfig {
457 pub in_peers: u32,
459
460 pub out_peers: u32,
462
463 pub reserved_nodes: Vec<MultiaddrWithPeerId>,
465
466 pub non_reserved_mode: NonReservedPeerMode,
469}
470
471impl Default for SetConfig {
472 fn default() -> Self {
473 Self {
474 in_peers: 25,
475 out_peers: 75,
476 reserved_nodes: Vec::new(),
477 non_reserved_mode: NonReservedPeerMode::Accept,
478 }
479 }
480}
481
482#[derive(Debug)]
487pub struct NonDefaultSetConfig {
488 protocol_name: ProtocolName,
494
495 fallback_names: Vec<ProtocolName>,
502
503 handshake: Option<NotificationHandshake>,
509
510 max_notification_size: u64,
512
513 set_config: SetConfig,
515
516 protocol_handle_pair: ProtocolHandlePair,
524}
525
526impl NonDefaultSetConfig {
527 pub fn new(
530 protocol_name: ProtocolName,
531 fallback_names: Vec<ProtocolName>,
532 max_notification_size: u64,
533 handshake: Option<NotificationHandshake>,
534 set_config: SetConfig,
535 ) -> (Self, Box<dyn NotificationService>) {
536 let (protocol_handle_pair, notification_service) =
537 notification_service(protocol_name.clone());
538 (
539 Self {
540 protocol_name,
541 max_notification_size,
542 fallback_names,
543 handshake,
544 set_config,
545 protocol_handle_pair,
546 },
547 notification_service,
548 )
549 }
550
551 pub fn protocol_name(&self) -> &ProtocolName {
553 &self.protocol_name
554 }
555
556 pub fn fallback_names(&self) -> impl Iterator<Item = &ProtocolName> {
558 self.fallback_names.iter()
559 }
560
561 pub fn handshake(&self) -> &Option<NotificationHandshake> {
563 &self.handshake
564 }
565
566 pub fn max_notification_size(&self) -> u64 {
568 self.max_notification_size
569 }
570
571 pub fn set_config(&self) -> &SetConfig {
573 &self.set_config
574 }
575
576 pub fn take_protocol_handle(self) -> ProtocolHandlePair {
578 self.protocol_handle_pair
579 }
580
581 pub fn allow_non_reserved(&mut self, in_peers: u32, out_peers: u32) {
583 self.set_config.in_peers = in_peers;
584 self.set_config.out_peers = out_peers;
585 self.set_config.non_reserved_mode = NonReservedPeerMode::Accept;
586 }
587
588 pub fn add_reserved(&mut self, peer: MultiaddrWithPeerId) {
590 self.set_config.reserved_nodes.push(peer);
591 }
592
593 pub fn add_fallback_names(&mut self, fallback_names: Vec<ProtocolName>) {
597 self.fallback_names.extend(fallback_names);
598 }
599}
600
601impl NotificationConfig for NonDefaultSetConfig {
602 fn set_config(&self) -> &SetConfig {
603 &self.set_config
604 }
605
606 fn protocol_name(&self) -> &ProtocolName {
608 &self.protocol_name
609 }
610}
611
612#[derive(Clone, Debug)]
614pub struct NetworkConfiguration {
615 pub net_config_path: Option<PathBuf>,
617
618 pub listen_addresses: Vec<Multiaddr>,
620
621 pub experimental_webrtc: bool,
623
624 pub public_addresses: Vec<Multiaddr>,
626
627 pub boot_nodes: Vec<MultiaddrWithPeerId>,
629
630 pub node_key: NodeKeyConfig,
632
633 pub default_peers_set: SetConfig,
635
636 pub default_peers_set_num_full: u32,
641
642 pub client_version: String,
644
645 pub node_name: String,
647
648 pub transport: TransportConfig,
650
651 pub idle_connection_timeout: Duration,
655
656 pub max_parallel_downloads: u32,
658
659 pub max_blocks_per_request: u32,
661
662 pub min_peers_to_start_warp_sync: Option<usize>,
664
665 pub sync_mode: SyncMode,
667
668 pub enable_dht_random_walk: bool,
672
673 pub allow_non_globals_in_dht: bool,
675
676 pub kademlia_disjoint_query_paths: bool,
679
680 pub kademlia_replication_factor: NonZeroUsize,
685
686 pub ipfs_server: bool,
688
689 pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
695
696 pub network_backend: NetworkBackendType,
698}
699
700impl NetworkConfiguration {
701 pub fn new<SN: Into<String>, SV: Into<String>>(
703 node_name: SN,
704 client_version: SV,
705 node_key: NodeKeyConfig,
706 net_config_path: Option<PathBuf>,
707 ) -> Self {
708 let default_peers_set = SetConfig::default();
709 Self {
710 net_config_path,
711 listen_addresses: Vec::new(),
712 experimental_webrtc: false,
713 public_addresses: Vec::new(),
714 boot_nodes: Vec::new(),
715 node_key,
716 default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers,
717 default_peers_set,
718 client_version: client_version.into(),
719 node_name: node_name.into(),
720 transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true },
721 idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
722 max_parallel_downloads: 5,
723 max_blocks_per_request: 64,
724 min_peers_to_start_warp_sync: None,
725 sync_mode: SyncMode::Full,
726 enable_dht_random_walk: true,
727 allow_non_globals_in_dht: false,
728 kademlia_disjoint_query_paths: false,
729 kademlia_replication_factor: NonZeroUsize::new(DEFAULT_KADEMLIA_REPLICATION_FACTOR)
730 .expect("value is a constant; constant is non-zero; qed."),
731 ipfs_server: false,
732 ipfs_bootnodes: Vec::new(),
733 network_backend: NetworkBackendType::Litep2p,
734 }
735 }
736
737 pub fn new_local() -> NetworkConfiguration {
740 let mut config =
741 NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
742
743 config.listen_addresses =
744 vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
745 .chain(iter::once(multiaddr::Protocol::Tcp(0)))
746 .collect()];
747
748 config.allow_non_globals_in_dht = true;
749 config
750 }
751
752 pub fn new_memory() -> NetworkConfiguration {
755 let mut config =
756 NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
757
758 config.listen_addresses =
759 vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
760 .chain(iter::once(multiaddr::Protocol::Tcp(0)))
761 .collect()];
762
763 config.allow_non_globals_in_dht = true;
764 config
765 }
766}
767
768pub struct IpfsConfig<Block: BlockT, H: ExHashT, N: NetworkBackend<Block, H>> {
770 pub bitswap_config: N::BitswapConfig,
772 pub block_provider: Box<dyn crate::IpfsBlockProvider>,
774 pub bootnodes: Vec<MultiaddrWithPeerId>,
776}
777
778pub struct Params<Block: BlockT, H: ExHashT, N: NetworkBackend<Block, H>> {
780 pub role: Role,
782
783 pub executor: Box<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send + Sync>,
785
786 pub network_config: FullNetworkConfiguration<Block, H, N>,
788
789 pub protocol_id: ProtocolId,
791
792 pub genesis_hash: Block::Hash,
794
795 pub fork_id: Option<String>,
798
799 pub metrics_registry: Option<Registry>,
801
802 pub block_announce_config: N::NotificationProtocolConfig,
804
805 pub ipfs_config: Option<IpfsConfig<Block, H, N>>,
807
808 pub notification_metrics: NotificationMetrics,
810}
811
812pub struct FullNetworkConfiguration<B: BlockT + 'static, H: ExHashT, N: NetworkBackend<B, H>> {
814 pub(crate) notification_protocols: Vec<N::NotificationProtocolConfig>,
816
817 pub(crate) request_response_protocols: Vec<N::RequestResponseProtocolConfig>,
819
820 pub network_config: NetworkConfiguration,
822
823 peer_store: Option<N::PeerStore>,
825
826 peer_store_handle: Arc<dyn PeerStoreProvider>,
828
829 pub metrics_registry: Option<Registry>,
831}
832
833impl<B: BlockT + 'static, H: ExHashT, N: NetworkBackend<B, H>> FullNetworkConfiguration<B, H, N> {
834 pub fn new(network_config: &NetworkConfiguration, metrics_registry: Option<Registry>) -> Self {
836 let bootnodes = network_config.boot_nodes.iter().map(|bootnode| bootnode.peer_id).collect();
837 let peer_store = N::peer_store(bootnodes, metrics_registry.clone());
838 let peer_store_handle = peer_store.handle();
839
840 Self {
841 peer_store: Some(peer_store),
842 peer_store_handle,
843 notification_protocols: Vec::new(),
844 request_response_protocols: Vec::new(),
845 network_config: network_config.clone(),
846 metrics_registry,
847 }
848 }
849
850 pub fn add_notification_protocol(&mut self, config: N::NotificationProtocolConfig) {
852 self.notification_protocols.push(config);
853 }
854
855 pub fn notification_protocols(&self) -> &Vec<N::NotificationProtocolConfig> {
857 &self.notification_protocols
858 }
859
860 pub fn add_request_response_protocol(&mut self, config: N::RequestResponseProtocolConfig) {
862 self.request_response_protocols.push(config);
863 }
864
865 pub fn peer_store_handle(&self) -> Arc<dyn PeerStoreProvider> {
867 Arc::clone(&self.peer_store_handle)
868 }
869
870 pub fn take_peer_store(&mut self) -> N::PeerStore {
878 self.peer_store
879 .take()
880 .expect("`PeerStore` can only be taken once when it's started; qed")
881 }
882
883 pub fn sanity_check_addresses(&self) -> Result<(), crate::error::Error> {
885 ensure_addresses_consistent_with_transport(
886 self.network_config.listen_addresses.iter(),
887 &self.network_config.transport,
888 )?;
889 ensure_addresses_consistent_with_transport(
890 self.network_config.boot_nodes.iter().map(|x| &x.multiaddr),
891 &self.network_config.transport,
892 )?;
893 ensure_addresses_consistent_with_transport(
894 self.network_config
895 .default_peers_set
896 .reserved_nodes
897 .iter()
898 .map(|x| &x.multiaddr),
899 &self.network_config.transport,
900 )?;
901
902 for notification_protocol in &self.notification_protocols {
903 ensure_addresses_consistent_with_transport(
904 notification_protocol.set_config().reserved_nodes.iter().map(|x| &x.multiaddr),
905 &self.network_config.transport,
906 )?;
907 }
908 ensure_addresses_consistent_with_transport(
909 self.network_config.public_addresses.iter(),
910 &self.network_config.transport,
911 )?;
912
913 Ok(())
914 }
915
916 pub fn sanity_check_bootnodes(&self) -> Result<(), crate::error::Error> {
918 self.network_config.boot_nodes.iter().try_for_each(|bootnode| {
919 if let Some(other) = self
920 .network_config
921 .boot_nodes
922 .iter()
923 .filter(|o| o.multiaddr == bootnode.multiaddr)
924 .find(|o| o.peer_id != bootnode.peer_id)
925 {
926 Err(crate::error::Error::DuplicateBootnode {
927 address: bootnode.multiaddr.clone().into(),
928 first_id: bootnode.peer_id.into(),
929 second_id: other.peer_id.into(),
930 })
931 } else {
932 Ok(())
933 }
934 })
935 }
936
937 pub fn known_addresses(&self) -> Vec<(PeerId, Multiaddr)> {
939 let mut addresses: Vec<_> = self
940 .network_config
941 .default_peers_set
942 .reserved_nodes
943 .iter()
944 .map(|reserved| (reserved.peer_id, reserved.multiaddr.clone()))
945 .chain(self.notification_protocols.iter().flat_map(|protocol| {
946 protocol
947 .set_config()
948 .reserved_nodes
949 .iter()
950 .map(|reserved| (reserved.peer_id, reserved.multiaddr.clone()))
951 }))
952 .chain(
953 self.network_config
954 .boot_nodes
955 .iter()
956 .map(|bootnode| (bootnode.peer_id, bootnode.multiaddr.clone())),
957 )
958 .collect();
959
960 addresses.sort();
962 addresses.dedup();
963
964 addresses
965 }
966}
967
968#[derive(Debug, Clone, Default, Copy)]
970pub enum NetworkBackendType {
971 #[default]
975 Litep2p,
976
977 Libp2p,
984}
985
986#[cfg(test)]
987mod tests {
988 use super::*;
989 use tempfile::TempDir;
990
991 fn tempdir_with_prefix(prefix: &str) -> TempDir {
992 tempfile::Builder::new().prefix(prefix).tempdir().unwrap()
993 }
994
995 fn secret_bytes(kp: ed25519::Keypair) -> Vec<u8> {
996 kp.secret().to_bytes().into()
997 }
998
999 #[test]
1000 fn test_secret_file() {
1001 let tmp = tempdir_with_prefix("x");
1002 std::fs::remove_dir(tmp.path()).unwrap(); let file = tmp.path().join("x").to_path_buf();
1004 let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
1005 let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
1006 assert!(file.is_file() && secret_bytes(kp1) == secret_bytes(kp2))
1007 }
1008
1009 #[test]
1010 fn test_secret_input() {
1011 let sk = ed25519::SecretKey::generate();
1012 let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap();
1013 let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap();
1014 assert!(secret_bytes(kp1) == secret_bytes(kp2));
1015 }
1016
1017 #[test]
1018 fn test_secret_new() {
1019 let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
1020 let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
1021 assert!(secret_bytes(kp1) != secret_bytes(kp2));
1022 }
1023}