rachel-rose 5 лет назад
Родитель
Сommit
f1db6c5443
1 измененных файлов с 369 добавлено и 0 удалено
  1. 369 0
      src/bin/wallet/darkd.rs

+ 369 - 0
src/bin/wallet/darkd.rs

@@ -0,0 +1,369 @@
+#[macro_use]
+extern crate clap;
+use async_executor::Executor;
+use async_native_tls::TlsAcceptor;
+use async_std::sync::Mutex;
+use easy_parallel::Parallel;
+use http_types::{Request, Response, StatusCode};
+use log::*;
+use serde_json::json;
+use smol::Async;
+use std::net::SocketAddr;
+use std::net::TcpListener;
+use std::sync::Arc;
+
+use sapvi::{net, Result};
+
+/// Listens for incoming connections and serves them.
+async fn listen(
+    executor: Arc<Executor<'_>>,
+    rpc: Arc<RpcInterface>,
+    listener: Async<TcpListener>,
+    tls: Option<TlsAcceptor>,
+) -> Result<()> {
+    // Format the full host address.
+    let host = match &tls {
+        None => format!("http://{}", listener.get_ref().local_addr()?),
+        Some(_) => format!("https://{}", listener.get_ref().local_addr()?),
+    };
+    println!("Listening on {}", host);
+
+    loop {
+        // Accept the next connection.
+        let (stream, _) = listener.accept().await?;
+
+        // Spawn a background task serving this connection.
+        let task = match &tls {
+            None => {
+                let stream = async_dup::Arc::new(stream);
+                let rpc = rpc.clone();
+                executor.spawn(async move {
+                    if let Err(err) = async_h1::accept(stream, move |req| {
+                        let rpc = rpc.clone();
+                        rpc.serve(req)
+                    })
+                    .await
+                    {
+                        println!("Connection error: {:#?}", err);
+                    }
+                })
+            }
+            Some(tls) => {
+                // In case of HTTPS, establish a secure TLS connection first.
+                match tls.accept(stream).await {
+                    Ok(stream) => {
+                        let _stream = async_dup::Arc::new(async_dup::Mutex::new(stream));
+                        executor.spawn(async move {
+                            /*if let Err(err) = async_h1::accept(stream, serve).await {
+                                println!("Connection error: {:#?}", err);
+                            }*/
+                            unimplemented!();
+                        })
+                    }
+                    Err(err) => {
+                        println!("Failed to establish secure TLS connection: {:#?}", err);
+                        continue;
+                    }
+                }
+            }
+        };
+
+        // Detach the task to let it run in the background.
+        task.detach();
+    }
+}
+
+struct RpcInterface {
+    p2p: Arc<net::P2p>,
+    started: Mutex<bool>,
+    stop_send: async_channel::Sender<()>,
+    stop_recv: async_channel::Receiver<()>,
+}
+
+impl RpcInterface {
+    fn new(p2p: Arc<net::P2p>) -> Arc<Self> {
+        let (stop_send, stop_recv) = async_channel::unbounded::<()>();
+
+        Arc::new(Self {
+            p2p,
+            started: Mutex::new(false),
+            stop_send,
+            stop_recv,
+        })
+    }
+
+    // add new methods to handle wallet commands
+    async fn serve(self: Arc<Self>, mut req: Request) -> http_types::Result<Response> {
+        info!("RPC serving {}", req.url());
+
+        let request = req.body_string().await?;
+
+        let mut io = jsonrpc_core::IoHandler::new();
+        io.add_sync_method("say_hello", |_| {
+            Ok(jsonrpc_core::Value::String("Hello World!".into()))
+        });
+
+        let self2 = self.clone();
+        io.add_method("get_info", move |_| {
+            let self2 = self2.clone();
+            async move {
+                Ok(json!({
+                    "started": *self2.started.lock().await,
+                    "connections": self2.p2p.connections_count().await
+                }))
+            }
+        });
+
+        let stop_send = self.stop_send.clone();
+        io.add_method("stop", move |_| {
+            let stop_send = stop_send.clone();
+            async move {
+                let _ = stop_send.send(()).await;
+                Ok(jsonrpc_core::Value::Null)
+            }
+        });
+
+        let response = io
+            .handle_request_sync(&request)
+            .ok_or(sapvi::Error::BadOperationType)?;
+
+        let mut res = Response::new(StatusCode::Ok);
+        res.insert_header("Content-Type", "text/plain");
+        res.set_body(response);
+        Ok(res)
+    }
+
+    async fn wait_for_quit(self: Arc<Self>) -> Result<()> {
+        Ok(self.stop_recv.recv().await?)
+    }
+}
+
+async fn start(executor: Arc<Executor<'_>>, options: ProgramOptions) -> Result<()> {
+    let p2p = net::P2p::new(options.network_settings);
+
+    let rpc = RpcInterface::new(p2p.clone());
+    let http = listen(
+        executor.clone(),
+        rpc.clone(),
+        Async::<TcpListener>::bind(([127, 0, 0, 1], options.rpc_port))?,
+        None,
+    );
+
+    let http_task = executor.spawn(http);
+
+    *rpc.started.lock().await = true;
+
+    p2p.clone().start(executor.clone()).await?;
+
+    p2p.run(executor).await?;
+
+    rpc.wait_for_quit().await?;
+
+    http_task.cancel().await;
+
+    Ok(())
+}
+
+/*
+async fn start2(executor: Arc<Executor<'_>>, options: ProgramOptions) -> Result<()> {
+    let connections = Arc::new(Mutex::new(HashMap::new()));
+
+    let stored_addrs = Arc::new(Mutex::new(Vec::new()));
+
+    let executor2 = executor.clone();
+    let stored_addrs2 = stored_addrs.clone();
+
+    let mut server_task = None;
+    if let Some(accept_addr) = options.accept_addr {
+        let accept_addr = accept_addr.clone();
+
+        let protocol = ServerProtocol::new(connections.clone(), accept_addr, stored_addrs2);
+        server_task = Some(executor.spawn(async move {
+            protocol.start(executor2).await?;
+            Ok::<(), sapvi::Error>(())
+        }));
+    }
+
+    let mut seed_protocols = Vec::with_capacity(options.seed_addrs.len());
+
+    // Normally we query this from a server
+    let accept_addr = options.accept_addr.clone();
+
+    for seed_addr in options.seed_addrs.iter() {
+        let protocol = SeedProtocol::new(seed_addr.clone(), accept_addr, stored_addrs.clone());
+        protocol.clone().start(executor.clone()).await;
+        seed_protocols.push(protocol);
+    }
+
+    debug!("Waiting for seed node queries to finish...");
+
+    for seed_protocol in seed_protocols {
+        seed_protocol.await_finish().await;
+    }
+
+    debug!("Seed nodes queried.");
+
+    let mut client_slots = vec![];
+    for i in 0..options.connection_slots {
+        debug!("Starting connection slot {}", i);
+
+        let client = Channel::new(
+            connections.clone(),
+            accept_addr.clone(),
+            stored_addrs.clone(),
+        );
+        client.clone().start(executor.clone()).await;
+        client_slots.push(client);
+    }
+
+    for remote_addr in options.manual_connects {
+        debug!("Starting connection (manual) to {}", remote_addr);
+
+        let client = Channel::new(
+            connections.clone(),
+            accept_addr.clone(),
+            stored_addrs.clone(),
+        );
+        client
+            .clone()
+            .start_manual(remote_addr, executor.clone())
+            .await;
+        client_slots.push(client);
+    }
+
+    let rpc = RpcInterface::new();
+    let http = listen(
+        executor.clone(),
+        rpc.clone(),
+        Async::<TcpListener>::bind(([127, 0, 0, 1], 8000))?,
+        None,
+    );
+
+    let http_task = executor.spawn(http);
+
+    rpc.stop_recv.recv().await?;
+
+    http_task.cancel().await;
+
+    match server_task {
+        None => {}
+        Some(server_task) => {
+            server_task.cancel().await;
+        }
+    }
+    Ok(())
+}
+*/
+
+struct ProgramOptions {
+    network_settings: net::Settings,
+    log_path: Box<std::path::PathBuf>,
+    rpc_port: u16,
+}
+
+impl ProgramOptions {
+    fn load() -> Result<ProgramOptions> {
+        let app = clap_app!(dfi =>
+            (version: "0.1.0")
+            (author: "Amir Taaki <amir@dyne.org>")
+            (about: "Dark node")
+            (@arg ACCEPT: -a --accept +takes_value "Accept address")
+            (@arg SEED_NODES: -s --seeds ... "Seed nodes")
+            (@arg CONNECTS: -c --connect ... "Manual connections")
+            (@arg CONNECT_SLOTS: --slots +takes_value "Connection slots")
+            (@arg LOG_PATH: --log +takes_value "Logfile path")
+            (@arg RPC_PORT: -r --rpc +takes_value "RPC port")
+        )
+        .get_matches();
+
+        let accept_addr = if let Some(accept_addr) = app.value_of("ACCEPT") {
+            Some(accept_addr.parse()?)
+        } else {
+            None
+        };
+
+        let mut seed_addrs: Vec<SocketAddr> = vec![];
+        if let Some(seeds) = app.values_of("SEED_NODES") {
+            for seed in seeds {
+                seed_addrs.push(seed.parse()?);
+            }
+        }
+
+        let mut manual_connects: Vec<SocketAddr> = vec![];
+        if let Some(connections) = app.values_of("CONNECTS") {
+            for connect in connections {
+                manual_connects.push(connect.parse()?);
+            }
+        }
+
+        let connection_slots = if let Some(connection_slots) = app.value_of("CONNECT_SLOTS") {
+            connection_slots.parse()?
+        } else {
+            0
+        };
+
+        let log_path = Box::new(
+            if let Some(log_path) = app.value_of("LOG_PATH") {
+                std::path::Path::new(log_path)
+            } else {
+                std::path::Path::new("/tmp/darkfid.log")
+            }
+            .to_path_buf(),
+        );
+
+        let rpc_port = if let Some(rpc_port) = app.value_of("RPC_PORT") {
+            rpc_port.parse()?
+        } else {
+            8000
+        };
+
+        Ok(ProgramOptions {
+            network_settings: net::Settings {
+                inbound: accept_addr,
+                outbound_connections: connection_slots,
+                external_addr: accept_addr,
+                peers: manual_connects,
+                seeds: seed_addrs,
+                ..Default::default()
+            },
+            log_path,
+            rpc_port,
+        })
+    }
+}
+
+fn main() -> Result<()> {
+    use simplelog::*;
+
+    let options = ProgramOptions::load()?;
+
+    let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+
+    CombinedLogger::init(vec![
+        TermLogger::new(LevelFilter::Debug, logger_config, TerminalMode::Mixed).unwrap(),
+        WriteLogger::new(
+            LevelFilter::Debug,
+            Config::default(),
+            std::fs::File::create(options.log_path.as_path()).unwrap(),
+        ),
+    ])
+    .unwrap();
+
+    let ex = Arc::new(Executor::new());
+    let (signal, shutdown) = async_channel::unbounded::<()>();
+    let ex2 = ex.clone();
+
+    let (_, result) = Parallel::new()
+        // Run four executor threads.
+        .each(0..3, |_| smol::future::block_on(ex.run(shutdown.recv())))
+        // Run the main future on the current thread.
+        .finish(|| {
+            smol::future::block_on(async move {
+                start(ex2, options).await?;
+                drop(signal);
+                Ok::<(), sapvi::Error>(())
+            })
+        });
+
+    result
+}