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
426fn 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 public_addresses: Vec<Multiaddr>,
623
624 pub boot_nodes: Vec<MultiaddrWithPeerId>,
626
627 pub node_key: NodeKeyConfig,
629
630 pub default_peers_set: SetConfig,
632
633 pub default_peers_set_num_full: u32,
638
639 pub client_version: String,
641
642 pub node_name: String,
644
645 pub transport: TransportConfig,
647
648 pub idle_connection_timeout: Duration,
652
653 pub max_parallel_downloads: u32,
655
656 pub max_blocks_per_request: u32,
658
659 pub min_peers_to_start_warp_sync: Option<usize>,
661
662 pub sync_mode: SyncMode,
664
665 pub enable_dht_random_walk: bool,
669
670 pub allow_non_globals_in_dht: bool,
672
673 pub kademlia_disjoint_query_paths: bool,
676
677 pub kademlia_replication_factor: NonZeroUsize,
682
683 pub ipfs_server: bool,
685
686 pub network_backend: NetworkBackendType,
688}
689
690impl NetworkConfiguration {
691 pub fn new<SN: Into<String>, SV: Into<String>>(
693 node_name: SN,
694 client_version: SV,
695 node_key: NodeKeyConfig,
696 net_config_path: Option<PathBuf>,
697 ) -> Self {
698 let default_peers_set = SetConfig::default();
699 Self {
700 net_config_path,
701 listen_addresses: Vec::new(),
702 public_addresses: Vec::new(),
703 boot_nodes: Vec::new(),
704 node_key,
705 default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers,
706 default_peers_set,
707 client_version: client_version.into(),
708 node_name: node_name.into(),
709 transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true },
710 idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
711 max_parallel_downloads: 5,
712 max_blocks_per_request: 64,
713 min_peers_to_start_warp_sync: None,
714 sync_mode: SyncMode::Full,
715 enable_dht_random_walk: true,
716 allow_non_globals_in_dht: false,
717 kademlia_disjoint_query_paths: false,
718 kademlia_replication_factor: NonZeroUsize::new(DEFAULT_KADEMLIA_REPLICATION_FACTOR)
719 .expect("value is a constant; constant is non-zero; qed."),
720 ipfs_server: false,
721 network_backend: NetworkBackendType::Litep2p,
722 }
723 }
724
725 pub fn new_local() -> NetworkConfiguration {
728 let mut config =
729 NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
730
731 config.listen_addresses =
732 vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
733 .chain(iter::once(multiaddr::Protocol::Tcp(0)))
734 .collect()];
735
736 config.allow_non_globals_in_dht = true;
737 config
738 }
739
740 pub fn new_memory() -> NetworkConfiguration {
743 let mut config =
744 NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
745
746 config.listen_addresses =
747 vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
748 .chain(iter::once(multiaddr::Protocol::Tcp(0)))
749 .collect()];
750
751 config.allow_non_globals_in_dht = true;
752 config
753 }
754}
755
756pub struct Params<Block: BlockT, H: ExHashT, N: NetworkBackend<Block, H>> {
758 pub role: Role,
760
761 pub executor: Box<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send + Sync>,
763
764 pub network_config: FullNetworkConfiguration<Block, H, N>,
766
767 pub protocol_id: ProtocolId,
769
770 pub genesis_hash: Block::Hash,
772
773 pub fork_id: Option<String>,
776
777 pub metrics_registry: Option<Registry>,
779
780 pub block_announce_config: N::NotificationProtocolConfig,
782
783 pub bitswap_config: Option<N::BitswapConfig>,
785
786 pub notification_metrics: NotificationMetrics,
788}
789
790pub struct FullNetworkConfiguration<B: BlockT + 'static, H: ExHashT, N: NetworkBackend<B, H>> {
792 pub(crate) notification_protocols: Vec<N::NotificationProtocolConfig>,
794
795 pub(crate) request_response_protocols: Vec<N::RequestResponseProtocolConfig>,
797
798 pub network_config: NetworkConfiguration,
800
801 peer_store: Option<N::PeerStore>,
803
804 peer_store_handle: Arc<dyn PeerStoreProvider>,
806
807 pub metrics_registry: Option<Registry>,
809}
810
811impl<B: BlockT + 'static, H: ExHashT, N: NetworkBackend<B, H>> FullNetworkConfiguration<B, H, N> {
812 pub fn new(network_config: &NetworkConfiguration, metrics_registry: Option<Registry>) -> Self {
814 let bootnodes = network_config.boot_nodes.iter().map(|bootnode| bootnode.peer_id).collect();
815 let peer_store = N::peer_store(bootnodes, metrics_registry.clone());
816 let peer_store_handle = peer_store.handle();
817
818 Self {
819 peer_store: Some(peer_store),
820 peer_store_handle,
821 notification_protocols: Vec::new(),
822 request_response_protocols: Vec::new(),
823 network_config: network_config.clone(),
824 metrics_registry,
825 }
826 }
827
828 pub fn add_notification_protocol(&mut self, config: N::NotificationProtocolConfig) {
830 self.notification_protocols.push(config);
831 }
832
833 pub fn notification_protocols(&self) -> &Vec<N::NotificationProtocolConfig> {
835 &self.notification_protocols
836 }
837
838 pub fn add_request_response_protocol(&mut self, config: N::RequestResponseProtocolConfig) {
840 self.request_response_protocols.push(config);
841 }
842
843 pub fn peer_store_handle(&self) -> Arc<dyn PeerStoreProvider> {
845 Arc::clone(&self.peer_store_handle)
846 }
847
848 pub fn take_peer_store(&mut self) -> N::PeerStore {
856 self.peer_store
857 .take()
858 .expect("`PeerStore` can only be taken once when it's started; qed")
859 }
860
861 pub fn sanity_check_addresses(&self) -> Result<(), crate::error::Error> {
863 ensure_addresses_consistent_with_transport(
864 self.network_config.listen_addresses.iter(),
865 &self.network_config.transport,
866 )?;
867 ensure_addresses_consistent_with_transport(
868 self.network_config.boot_nodes.iter().map(|x| &x.multiaddr),
869 &self.network_config.transport,
870 )?;
871 ensure_addresses_consistent_with_transport(
872 self.network_config
873 .default_peers_set
874 .reserved_nodes
875 .iter()
876 .map(|x| &x.multiaddr),
877 &self.network_config.transport,
878 )?;
879
880 for notification_protocol in &self.notification_protocols {
881 ensure_addresses_consistent_with_transport(
882 notification_protocol.set_config().reserved_nodes.iter().map(|x| &x.multiaddr),
883 &self.network_config.transport,
884 )?;
885 }
886 ensure_addresses_consistent_with_transport(
887 self.network_config.public_addresses.iter(),
888 &self.network_config.transport,
889 )?;
890
891 Ok(())
892 }
893
894 pub fn sanity_check_bootnodes(&self) -> Result<(), crate::error::Error> {
896 self.network_config.boot_nodes.iter().try_for_each(|bootnode| {
897 if let Some(other) = self
898 .network_config
899 .boot_nodes
900 .iter()
901 .filter(|o| o.multiaddr == bootnode.multiaddr)
902 .find(|o| o.peer_id != bootnode.peer_id)
903 {
904 Err(crate::error::Error::DuplicateBootnode {
905 address: bootnode.multiaddr.clone().into(),
906 first_id: bootnode.peer_id.into(),
907 second_id: other.peer_id.into(),
908 })
909 } else {
910 Ok(())
911 }
912 })
913 }
914
915 pub fn known_addresses(&self) -> Vec<(PeerId, Multiaddr)> {
917 let mut addresses: Vec<_> = self
918 .network_config
919 .default_peers_set
920 .reserved_nodes
921 .iter()
922 .map(|reserved| (reserved.peer_id, reserved.multiaddr.clone()))
923 .chain(self.notification_protocols.iter().flat_map(|protocol| {
924 protocol
925 .set_config()
926 .reserved_nodes
927 .iter()
928 .map(|reserved| (reserved.peer_id, reserved.multiaddr.clone()))
929 }))
930 .chain(
931 self.network_config
932 .boot_nodes
933 .iter()
934 .map(|bootnode| (bootnode.peer_id, bootnode.multiaddr.clone())),
935 )
936 .collect();
937
938 addresses.sort();
940 addresses.dedup();
941
942 addresses
943 }
944}
945
946#[derive(Debug, Clone, Default, Copy)]
948pub enum NetworkBackendType {
949 #[default]
953 Litep2p,
954
955 Libp2p,
962}
963
964#[cfg(test)]
965mod tests {
966 use super::*;
967 use tempfile::TempDir;
968
969 fn tempdir_with_prefix(prefix: &str) -> TempDir {
970 tempfile::Builder::new().prefix(prefix).tempdir().unwrap()
971 }
972
973 fn secret_bytes(kp: ed25519::Keypair) -> Vec<u8> {
974 kp.secret().to_bytes().into()
975 }
976
977 #[test]
978 fn test_secret_file() {
979 let tmp = tempdir_with_prefix("x");
980 std::fs::remove_dir(tmp.path()).unwrap(); let file = tmp.path().join("x").to_path_buf();
982 let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
983 let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
984 assert!(file.is_file() && secret_bytes(kp1) == secret_bytes(kp2))
985 }
986
987 #[test]
988 fn test_secret_input() {
989 let sk = ed25519::SecretKey::generate();
990 let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap();
991 let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap();
992 assert!(secret_bytes(kp1) == secret_bytes(kp2));
993 }
994
995 #[test]
996 fn test_secret_new() {
997 let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
998 let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
999 assert!(secret_bytes(kp1) != secret_bytes(kp2));
1000 }
1001}