1use chat_core::coordinator::CoordinatorEvent;
2use network_libp2p::NodeOptions;
3use clap::Parser;
4use futures::StreamExt;
5use std::time::Duration;
6
7#[derive(Parser, Debug)]
8#[command(name = "chat-test-peer")]
9#[command(about = "Minimal test peer for P2Pandemonium E2E tests")]
10struct Cli {
11 #[arg(short, long, default_value = "e2e-test-room")]
13 room: String,
14
15 #[arg(short, long, default_value = "/ip4/127.0.0.1/tcp/40233/ws")]
17 listen: String,
18
19 #[arg(short, long)]
21 bootstrap: Option<String>,
22}
23
24#[tokio::main]
25async fn main() -> anyhow::Result<()> {
26 tracing_subscriber::fmt()
27 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
28 .init();
29
30 let cli = Cli::parse();
31
32 let bootstrap_peers = cli
33 .bootstrap
34 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
35 .unwrap_or_default();
36
37 let options = NodeOptions {
38 listen_addresses: vec![cli.listen.clone()],
39 bootstrap_peers,
40 max_connections: 100,
41 dht_client_mode: false, dht_discovery_enabled: true, stun_servers: None,
44 };
45
46 let mut seed: [u8; 32] = [
49 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
50 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
51 ];
52 let secret = libp2p_identity::ed25519::SecretKey::try_from_bytes(&mut seed)?;
53 let keypair = libp2p_identity::Keypair::from(libp2p_identity::ed25519::Keypair::from(secret));
54 let local_peer_id = libp2p::PeerId::from(keypair.public());
55 tracing::info!("Test peer deterministic ID: {}", local_peer_id);
56
57 let mut node = network_libp2p::native::build_node_with_keypair(
58 options, keypair.clone(),
59 ).await.map_err(|e| anyhow::anyhow!(e))?;
60 tracing::info!("Test peer started: {}", local_peer_id);
61
62 let mut relay_seed: [u8; 32] = [
68 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
69 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
70 ];
71 let relay_secret = libp2p_identity::ed25519::SecretKey::try_from_bytes(&mut relay_seed)?;
72 let relay_keypair = libp2p_identity::Keypair::from(libp2p_identity::ed25519::Keypair::from(relay_secret));
73 let relay_peer_id = libp2p::PeerId::from(relay_keypair.public());
74 tracing::info!("Relay server deterministic peer ID: {}", relay_peer_id);
75 let relay_listen: libp2p::Multiaddr = "/ip4/127.0.0.1/tcp/40234/ws".parse()?;
76 let mut relay_swarm = build_relay_swarm(&relay_keypair, relay_peer_id).await?;
77 relay_swarm.listen_on(relay_listen.clone())?;
78 relay_swarm.add_external_address(relay_listen.clone());
79 tracing::info!("Relay server listening on: {}", relay_listen);
80
81 tokio::spawn(async move {
82 use futures::StreamExt;
83 while let Some(event) = relay_swarm.next().await {
84 tracing::info!("[relay-server] event: {:?}", event);
85 }
86 });
87
88 let mut listen_addrs = Vec::new();
90 let timeout = tokio::time::Duration::from_secs(5);
91 let deadline = tokio::time::Instant::now() + timeout;
92
93 while tokio::time::Instant::now() < deadline {
94 tokio::select! {
95 event = node.swarm.select_next_some() => {
96 if let libp2p::swarm::SwarmEvent::NewListenAddr { address, .. } = event {
97 tracing::info!("Listening on: {}", address);
98 listen_addrs.push(address);
99 }
100 }
101 _ = tokio::time::sleep(Duration::from_millis(10)) => {}
102 }
103 if !listen_addrs.is_empty() {
104 break;
105 }
106 }
107
108 let conn = rusqlite::Connection::open_in_memory()?;
109 let storage = chat_core::storage::SqliteStorage::new(conn);
110 storage.init_schema()?;
111
112 let (handle, mut events, loop_fut) = chat_core::coordinator::build(node, storage);
113
114 tokio::spawn(loop_fut.run());
115
116 tokio::spawn(async move {
118 while let Ok(event) = events.recv().await {
119 match event {
120 CoordinatorEvent::SystemMessage(msg) => {
121 tracing::info!("[coord] {}", msg);
122 }
123 CoordinatorEvent::ErrorMessage(msg) => {
124 tracing::error!("[coord] {}", msg);
125 }
126 CoordinatorEvent::PeerConnected { peer_id } => {
127 tracing::info!("[coord] Peer connected: {}", peer_id);
128 }
129 CoordinatorEvent::PeerDisconnected { peer_id } => {
130 tracing::info!("[coord] Peer disconnected: {}", peer_id);
131 }
132 _ => {}
133 }
134 }
135 });
136
137 handle.join_room(cli.room.clone()).await;
138 tracing::info!("Joined room: {}", cli.room);
139 println!("TEST_PEER_READY room={} peer={}", cli.room, local_peer_id);
140
141 for addr in &listen_addrs {
143 let dialable = addr.clone().with(libp2p::multiaddr::Protocol::P2p(local_peer_id));
144 println!("TEST_PEER_MULTIADDR={}", dialable);
145 tracing::info!("Browser tabs can dial: {}", dialable);
146 }
147
148 let relay_dialable = relay_listen.clone().with(libp2p::multiaddr::Protocol::P2p(relay_peer_id));
150 println!("RELAY_SERVER_MULTIADDR={}", relay_dialable);
151 tracing::info!("Relay server dialable: {}", relay_dialable);
152
153 tokio::signal::ctrl_c().await?;
155 handle.shutdown().await;
156 Ok(())
157}
158
159#[derive(libp2p::swarm::NetworkBehaviour)]
161#[behaviour(prelude = "libp2p_swarm::derive_prelude")]
162struct RelayBehaviour {
163 relay: libp2p::relay::Behaviour,
164 identify: libp2p::identify::Behaviour,
165}
166
167async fn build_relay_swarm(
169 keypair: &libp2p_identity::Keypair,
170 local_peer_id: libp2p::PeerId,
171) -> anyhow::Result<libp2p::swarm::Swarm<RelayBehaviour>> {
172 let builder = libp2p::SwarmBuilder::with_existing_identity(keypair.clone())
173 .with_tokio()
174 .with_websocket(
175 libp2p::noise::Config::new,
176 libp2p::yamux::Config::default,
177 )
178 .await?;
179
180 let swarm = builder
181 .with_behaviour(|_keypair| {
182 let relay = libp2p::relay::Behaviour::new(
183 local_peer_id,
184 libp2p::relay::Config::default(),
185 );
186 let identify = libp2p::identify::Behaviour::new(
187 libp2p::identify::Config::new(
188 "/p2pandemonium/identify/1.0.0".into(),
189 keypair.public(),
190 ),
191 );
192 RelayBehaviour { relay, identify }
193 })?
194 .with_swarm_config(|cfg| {
195 cfg.with_idle_connection_timeout(Duration::from_secs(60))
196 })
197 .build();
198
199 Ok(swarm)
200}
201
202#[cfg(test)]
203mod tests {
204 #[test]
205 fn print_deterministic_peer_id() {
206 let mut seed: [u8; 32] = [
207 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
208 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
209 ];
210 let secret = libp2p_identity::ed25519::SecretKey::try_from_bytes(&mut seed).unwrap();
211 let keypair = libp2p_identity::Keypair::from(libp2p_identity::ed25519::Keypair::from(secret));
212 let peer_id = libp2p::PeerId::from(keypair.public());
213 println!("DETERMINISTIC_PEER_ID={}", peer_id);
214 }
215}