@@ -21,11 +21,12 @@ use std::{
2121
2222use charon_p2p:: {
2323 config:: P2PConfig ,
24- k1,
24+ k1:: { self , key_path } ,
2525 p2p:: { Node , NodeType } ,
2626} ;
2727use charon_peerinfo:: { Behaviour , Config , Event , LocalPeerInfo } ;
2828use clap:: Parser ;
29+ use k256:: SecretKey ;
2930use libp2p:: {
3031 Multiaddr , Swarm ,
3132 futures:: StreamExt ,
@@ -41,13 +42,16 @@ use vise_exporter::MetricsExporter;
4142#[ command( name = "peerinfo-example" ) ]
4243#[ command( about = "Demonstrates the peerinfo protocol with mDNS discovery" ) ]
4344pub struct Args {
45+ #[ command( subcommand) ]
46+ pub command : Option < Command > ,
47+
4448 /// The port to listen on
4549 #[ arg( short, long, default_value = "4001" ) ]
4650 pub port : u16 ,
4751
48- /// Optional address to dial
52+ /// Optional addresses to dial
4953 #[ arg( short, long) ]
50- pub dial : Option < Multiaddr > ,
54+ pub dial : Vec < Multiaddr > ,
5155
5256 /// Nickname for this node
5357 #[ arg( short, long, default_value = "example-node" ) ]
@@ -66,6 +70,20 @@ pub struct Args {
6670 pub metrics_port : u16 ,
6771}
6872
73+ #[ derive( Debug , Parser ) ]
74+ pub enum Command {
75+ /// Initialize the node with a private key
76+ Init {
77+ /// Data directory for storing the private key
78+ #[ arg( long, default_value = ".charon-example" ) ]
79+ data_dir : PathBuf ,
80+
81+ /// Private key as a hex string
82+ #[ arg( long) ]
83+ private_key : String ,
84+ } ,
85+ }
86+
6987/// Combined behaviour with peerinfo, identify, ping, and mdns
7088#[ derive( NetworkBehaviour ) ]
7189pub struct CombinedBehaviour {
@@ -149,6 +167,28 @@ fn handle_event(event: SwarmEvent<CombinedEvent>, swarm: &mut Swarm<CombinedBeha
149167 }
150168}
151169
170+ fn init_node ( data_dir : & PathBuf , private_key : & str ) -> anyhow:: Result < ( ) > {
171+ // Decode the hex string
172+ let key_bytes = hex:: decode ( private_key. trim ( ) . trim_start_matches ( "0x" ) ) ?;
173+
174+ // Parse the secret key
175+ let key = SecretKey :: from_slice ( & key_bytes) ?;
176+
177+ // Create the data directory
178+ std:: fs:: create_dir_all ( data_dir) ?;
179+
180+ // Save the key
181+ let key_file = key_path ( data_dir) ;
182+ std:: fs:: write ( & key_file, hex:: encode ( key. to_bytes ( ) ) ) ?;
183+
184+ tracing:: info!(
185+ "Initialized node with private key in {}" ,
186+ key_file. display( )
187+ ) ;
188+
189+ Ok ( ( ) )
190+ }
191+
152192#[ tokio:: main]
153193async fn main ( ) -> anyhow:: Result < ( ) > {
154194 // Initialize logging
@@ -158,6 +198,15 @@ async fn main() -> anyhow::Result<()> {
158198
159199 let args = Args :: parse ( ) ;
160200
201+ // Handle init subcommand
202+ if let Some ( Command :: Init {
203+ data_dir,
204+ private_key,
205+ } ) = args. command
206+ {
207+ return init_node ( & data_dir, & private_key) ;
208+ }
209+
161210 // Run the metrics exporter
162211 let bind_address = SocketAddr :: from ( ( [ 0 , 0 , 0 , 0 ] , args. metrics_port ) ) ;
163212
@@ -238,8 +287,8 @@ async fn main() -> anyhow::Result<()> {
238287 let listen_addr: Multiaddr = format ! ( "/ip4/0.0.0.0/tcp/{}" , args. port) . parse ( ) ?;
239288 swarm. listen_on ( listen_addr) ?;
240289
241- // Dial the specified address if provided
242- if let Some ( dial_addr) = & args. dial {
290+ // Dial the specified addresses if provided
291+ for dial_addr in & args. dial {
243292 tracing:: info!( "Dialing {dial_addr}" ) ;
244293 swarm. dial ( dial_addr. clone ( ) ) ?;
245294 }
0 commit comments