Эх сурвалжийг харах

dnetview: remove dnetview from /bin and Cargo.toml

we are working on a rewrite in python and this current version is broken
on master and unmaintained.
lunar-mining 3 жил өмнө
parent
commit
cd9cb7b6de

+ 0 - 1
Cargo.toml

@@ -29,7 +29,6 @@ members = [
     "bin/genev/genevd",
     "bin/genev/genev-cli",
     "bin/darkirc",
-    #"bin/dnetview",
     "bin/tau/taud",
     "bin/tau/tau-cli",
     "bin/vanityaddr",

+ 0 - 39
bin/dnetview/Cargo.toml

@@ -1,39 +0,0 @@
-[package]
-name = "dnetview"
-description = "P2P network monitoring TUI utility"
-version = "0.4.1"
-edition = "2021"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-license = "AGPL-3.0-only"
-homepage = "https://dark.fi"
-repository = "https://github.com/darkrenaissance/darkfi"
-
-[dependencies.darkfi]
-path = "../../"
-features = ["rpc"]
-
-[dependencies]
-# Tui
-termion = "2.0.1"
-#tui = {version = "0.19.0", features = ["termion"]}
-ratatui = { version = "0.22.1-alpha.2", features = ["all-widgets", "termion"]}
-
-# Async
-smol = "1.3.0"
-async-std = {version = "1.12.0", features = ["attributes"]}
-easy-parallel = "3.3.0"
-async-channel = "1.9.0"
-
-# Misc
-clap = {version = "4.3.24", features = ["derive"]}
-rand = "0.8.5"
-simplelog = "0.12.1"
-libsqlite3-sys = {version = "0.26.0", features = ["bundled-sqlcipher-vendored-openssl"]}
-log = "0.4.20"
-url = "2.4.0"
-thiserror = "1.0.47"
-
-# Encoding and parsing
-serde_json = "1.0.105"
-serde = {version = "1.0.185", features = ["derive"]}
-hex = "0.4.3"

+ 0 - 52
bin/dnetview/README.md

@@ -1,52 +0,0 @@
-# Dnetview
-
-A simple tui to explore darkfi p2p network topology. Explore:
-
-1. Active p2p nodes
-2. Outgoing, incoming and manual sessions
-3. Each associated connection and recent messages.
-
-Dnetview is based on the design-pattern Model, View, Controller. We
-create a logical seperation between the underlying data structure or
-Model; the ui rendering aspect which is the View; and the Controller or
-game engine that makes everything run.
-
-## Install 
-
-```shell
-% git clone https://github.com/darkrenaissance/darkfi 
-% cd darkfi
-% make BINS=dnetview
-```
-
-## Usage
-
-On first run, dnetview will create a config file in .config/darkfi. You
-must manually enter the RPC ports of the nodes you want to connect to
-and title them as you see fit.
-
-Check the [example config file](dnetview_config.toml) for more details.
-
-Run dnetview as follows:
-
-```shell
-dnetview -v
-```
-
-Navigate up and down using `j` and `k`.
-
-## Logging
-
-Dnetview creates a logging file in /tmp/dnetview.log. To see json data
-and other debug info, tail the file like so:
-
-```shell
-tail -f /tmp/dnetview.log
-```
-
-Or use multitail for colored output:
-
-```shell
-multitail -c /tmp/dnetview.log
-```
-

+ 0 - 26
bin/dnetview/dnetview_config.toml

@@ -1,26 +0,0 @@
-## map configuration file
-##
-## Please make sure you go through all the settings so you can configure
-## map properly.
-
-[[nodes]]
-name = "darkirc"
-rpc_url = "tcp://127.0.0.1:26660"
-node_type = "NORMAL"
-
-[[nodes]]
-name = "taud"
-rpc_url = "tcp://127.0.0.1:23330"
-node_type = "NORMAL"
-
-# Darkfid node can run both sync and consensus p2p networks,
-# so we need to create a record for each network.
-[[nodes]]
-name = "darkfid-sync"
-rpc_url = "tcp://127.0.0.1:8340"
-node_type = "NORMAL"
-
-[[nodes]]
-name = "darkfid-consensus"
-rpc_url = "tcp://127.0.0.1:8340"
-node_type = "CONSENSUS"

+ 0 - 41
bin/dnetview/src/config.rs

@@ -1,41 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use serde::{Deserialize, Serialize};
-
-pub const CONFIG_FILE: &str = "dnetview_config.toml";
-pub const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../dnetview_config.toml");
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct DnvConfig {
-    pub nodes: Vec<Node>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Node {
-    pub name: String,
-    pub rpc_url: String,
-    pub node_type: NodeType,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum NodeType {
-    LILITH,
-    NORMAL,
-    CONSENSUS,
-}

+ 0 - 74
bin/dnetview/src/error.rs

@@ -1,74 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//use serde_json::Value;
-//use darkfi::rpc::jsonrpc::{error as jsonerr, response as jsonresp, ErrorCode, JsonResult};
-
-#[derive(Debug, thiserror::Error)]
-pub enum DnetViewError {
-    #[error("RPC reply is empty")]
-    EmptyRpcReply,
-    #[error("Json Value is not an object")]
-    ValueIsNotObject,
-    #[error("Failed to find ID at current index")]
-    NoIdAtIndex,
-    #[error("Message log does not contain ID")]
-    CannotFindId,
-    #[error("ID does not return a selectable object")]
-    NotSelectableObject,
-    #[error("JSON data does not contain an external addr")]
-    NoExternalAddr,
-    #[error("Found unexpected data in View")]
-    UnexpectedData(String),
-    #[error("InternalError")]
-    Darkfi(#[from] darkfi::error::Error),
-    #[error("Json serialization error: `{0}`")]
-    SerdeJsonError(String),
-    #[error("IO error: {0}")]
-    Io(std::io::ErrorKind),
-    #[error("SetLogger (log crate) failed: {0}")]
-    SetLoggerError(String),
-    #[error("URL parse error: {0}")]
-    UrlParse(String),
-}
-
-pub type DnetViewResult<T> = std::result::Result<T, DnetViewError>;
-
-impl From<serde_json::Error> for DnetViewError {
-    fn from(err: serde_json::Error) -> DnetViewError {
-        DnetViewError::SerdeJsonError(err.to_string())
-    }
-}
-
-impl From<std::io::Error> for DnetViewError {
-    fn from(err: std::io::Error) -> Self {
-        Self::Io(err.kind())
-    }
-}
-
-impl From<log::SetLoggerError> for DnetViewError {
-    fn from(err: log::SetLoggerError) -> Self {
-        Self::SetLoggerError(err.to_string())
-    }
-}
-
-impl From<url::ParseError> for DnetViewError {
-    fn from(err: url::ParseError) -> Self {
-        Self::UrlParse(err.to_string())
-    }
-}

+ 0 - 175
bin/dnetview/src/main.rs

@@ -1,175 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{fs::File, io, io::Read};
-
-use async_std::sync::Arc;
-use clap::Parser;
-use easy_parallel::Parallel;
-use log::{debug, info};
-use ratatui::{
-    backend::{Backend, TermionBackend},
-    Terminal,
-};
-use simplelog::*;
-use smol::Executor;
-use termion::{async_stdin, event::Key, input::TermRead, raw::IntoRawMode};
-
-use darkfi::util::{
-    async_util,
-    cli::{get_log_config, get_log_level, spawn_config, Config},
-    path::{expand_path, get_config_path},
-};
-
-pub mod config;
-pub mod error;
-pub mod model;
-pub mod options;
-pub mod parser;
-pub mod rpc;
-pub mod util;
-pub mod view;
-
-use crate::{
-    config::{DnvConfig, CONFIG_FILE, CONFIG_FILE_CONTENTS},
-    error::{DnetViewError, DnetViewResult},
-    model::Model,
-    options::Args,
-    parser::DataParser,
-    view::View,
-};
-
-struct DnetView {
-    model: Arc<Model>,
-    view: View,
-}
-
-impl DnetView {
-    fn new(model: Arc<Model>, view: View) -> Self {
-        Self { model, view }
-    }
-
-    async fn render_view<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> DnetViewResult<()> {
-        let mut asi = async_stdin();
-
-        terminal.clear()?;
-
-        self.view.id_menu.state.select(Some(0));
-        self.view.msg_list.state.select(Some(0));
-
-        loop {
-            self.view.update(
-                self.model.msg_map.lock().await.clone(),
-                self.model.selectables.lock().await.clone(),
-            );
-
-            let mut err: Option<DnetViewError> = None;
-
-            terminal.draw(|f| match self.view.render(f) {
-                Ok(()) => {}
-                Err(e) => {
-                    err = Some(e);
-                }
-            })?;
-
-            if let Some(e) = err {
-                return Err(e)
-            }
-
-            self.view.msg_list.scroll()?;
-
-            for k in asi.by_ref().keys() {
-                match k.unwrap() {
-                    Key::Char('q') => {
-                        terminal.clear()?;
-                        return Ok(())
-                    }
-                    Key::Char('j') => {
-                        self.view.id_menu.next();
-                    }
-                    Key::Char('k') => {
-                        self.view.id_menu.previous();
-                    }
-                    Key::Char('u') => {
-                        // TODO
-                        //view.msg_list.next();
-                    }
-                    Key::Char('d') => {
-                        // TODO
-                        //view.msg_list.previous();
-                    }
-                    _ => (),
-                }
-            }
-            async_util::msleep(100).await;
-        }
-    }
-}
-
-#[async_std::main]
-async fn main() -> DnetViewResult<()> {
-    let args = Args::parse();
-
-    let log_level = get_log_level(args.verbose);
-    let log_config = get_log_config(args.verbose);
-
-    let log_file_path = expand_path(&args.log_path)?;
-    if let Some(parent) = log_file_path.parent() {
-        std::fs::create_dir_all(parent)?;
-    };
-
-    let file = File::create(log_file_path)?;
-    WriteLogger::init(log_level, log_config, file)?;
-    info!("Log level: {}", log_level);
-
-    let config_path = get_config_path(args.config, CONFIG_FILE)?;
-    spawn_config(&config_path, CONFIG_FILE_CONTENTS)?;
-
-    let config = Config::<DnvConfig>::load(config_path)?;
-
-    let stdout = io::stdout().into_raw_mode()?;
-    let backend = TermionBackend::new(stdout);
-    let mut terminal = Terminal::new(backend)?;
-
-    terminal.clear()?;
-
-    let model = Model::new();
-    let view = View::new();
-
-    let ex = Arc::new(Executor::new());
-    let ex2 = ex.clone();
-
-    let mut dnetview = DnetView::new(model.clone(), view);
-    let parser = DataParser::new(model, config);
-
-    let nthreads = std::thread::available_parallelism().unwrap().get();
-    let (signal, shutdown) = async_channel::unbounded::<()>();
-
-    let (_, result) = Parallel::new()
-        .each(0..nthreads, |_| smol::future::block_on(ex.run(shutdown.recv())))
-        .finish(|| {
-            smol::future::block_on(async move {
-                parser.start_connect_slots(ex2).await?;
-                dnetview.render_view(&mut terminal).await?;
-                drop(signal);
-                Ok(())
-            })
-        });
-
-    result
-}

+ 0 - 163
bin/dnetview/src/model.rs

@@ -1,163 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-use std::collections::HashMap;
-
-use async_std::sync::{Arc, Mutex};
-use serde::{Deserialize, Serialize};
-
-use darkfi::util::time::NanoTimestamp;
-
-type MsgLog = Vec<(NanoTimestamp, String, String)>;
-type MsgMap = Mutex<HashMap<String, MsgLog>>;
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub enum Session {
-    Inbound,
-    Outbound,
-    //Manual,
-    Offline,
-    Null,
-}
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub enum SelectableObject {
-    Node(NodeInfo),
-    Lilith(LilithInfo),
-    Network(NetworkInfo),
-    Session(SessionInfo),
-    Slot(SlotInfo),
-}
-
-#[derive(Debug)]
-pub struct Model {
-    pub msg_map: MsgMap,
-    pub log: Mutex<MsgLog>,
-    pub selectables: Mutex<HashMap<String, SelectableObject>>,
-}
-
-impl Model {
-    pub fn new() -> Arc<Self> {
-        let selectables = Mutex::new(HashMap::new());
-        let msg_map = Mutex::new(HashMap::new());
-        let log = Mutex::new(Vec::new());
-        Arc::new(Model { msg_map, log, selectables })
-    }
-}
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub struct NodeInfo {
-    pub dnet_id: String,
-    pub name: String,
-    pub hosts: Vec<String>,
-    pub inbound: Vec<SessionInfo>,
-    pub outbound: Vec<SessionInfo>,
-    pub is_offline: bool,
-    pub dnet_enabled: bool,
-}
-
-impl NodeInfo {
-    pub fn new(
-        dnet_id: String,
-        name: String,
-        hosts: Vec<String>,
-        inbound: Vec<SessionInfo>,
-        outbound: Vec<SessionInfo>,
-        is_offline: bool,
-        dnet_enabled: bool,
-    ) -> Self {
-        Self { dnet_id, name, hosts, inbound, outbound, is_offline, dnet_enabled }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub struct SessionInfo {
-    pub dnet_id: String,
-    pub node_id: String,
-    pub addr: String,
-    pub state: Option<String>,
-    pub info: SlotInfo,
-    pub sort: Session,
-    pub is_empty: bool,
-}
-
-impl SessionInfo {
-    pub fn new(
-        dnet_id: String,
-        node_id: String,
-        addr: String,
-        state: Option<String>,
-        info: SlotInfo,
-        sort: Session,
-        is_empty: bool,
-    ) -> Self {
-        Self { dnet_id, node_id, addr, state, info, sort, is_empty }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub struct SlotInfo {
-    pub dnet_id: String,
-    pub node_id: String,
-    pub addr: String,
-    pub random_id: u64,
-    pub remote_id: String,
-    pub log: Vec<(NanoTimestamp, String, String)>,
-    pub is_empty: bool,
-}
-
-impl SlotInfo {
-    #[allow(clippy::too_many_arguments)]
-    pub fn new(
-        dnet_id: String,
-        node_id: String,
-        addr: String,
-        random_id: u64,
-        remote_id: String,
-        log: Vec<(NanoTimestamp, String, String)>,
-        is_empty: bool,
-    ) -> Self {
-        Self { dnet_id, addr, random_id, remote_id, log, node_id, is_empty }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub struct LilithInfo {
-    pub id: String,
-    pub name: String,
-    pub networks: Vec<NetworkInfo>,
-}
-
-impl LilithInfo {
-    pub fn new(id: String, name: String, networks: Vec<NetworkInfo>) -> Self {
-        Self { id, name, networks }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
-pub struct NetworkInfo {
-    pub id: String,
-    pub name: String,
-    pub urls: Vec<String>,
-    pub nodes: Vec<String>,
-}
-
-impl NetworkInfo {
-    pub fn new(id: String, name: String, urls: Vec<String>, nodes: Vec<String>) -> Self {
-        Self { id, name, urls, nodes }
-    }
-}

+ 0 - 35
bin/dnetview/src/options.rs

@@ -1,35 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi::cli_desc;
-
-#[derive(clap::Parser)]
-#[clap(name = "dnetview", about = cli_desc!(), version)]
-pub struct Args {
-    #[clap(short, action = clap::ArgAction::Count)]
-    /// Increase verbosity (-vvv supported)
-    pub verbose: u8,
-
-    /// Logfile path
-    #[clap(default_value = "~/.local/darkfi/dnetview.log")]
-    pub log_path: String,
-
-    /// Sets a custom config file
-    #[clap(short, long)]
-    pub config: Option<String>,
-}

+ 0 - 473
bin/dnetview/src/parser.rs

@@ -1,473 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::collections::hash_map::Entry;
-
-use async_std::sync::Arc;
-use log::{debug, error, info};
-use serde_json::Value;
-use smol::Executor;
-use url::Url;
-
-use darkfi::util::{async_util, time::NanoTimestamp};
-
-use crate::{
-    config::{DnvConfig, Node, NodeType},
-    error::{DnetViewError, DnetViewResult},
-    model::{
-        LilithInfo, Model, NetworkInfo, NodeInfo, SelectableObject, Session, SessionInfo, SlotInfo,
-    },
-    rpc::RpcConnect,
-    util::{
-        make_empty_id, make_info_id, make_network_id, make_node_id, make_null_id, make_session_id,
-    },
-};
-
-pub struct DataParser {
-    model: Arc<Model>,
-    config: DnvConfig,
-}
-
-impl DataParser {
-    pub fn new(model: Arc<Model>, config: DnvConfig) -> Arc<Self> {
-        Arc::new(Self { model, config })
-    }
-
-    pub async fn start_connect_slots(self: Arc<Self>, ex: Arc<Executor<'_>>) -> DnetViewResult<()> {
-        debug!(target: "dnetview", "start_connect_slots() START");
-        for node in &self.config.nodes {
-            debug!(target: "dnetview", "attempting to spawn...");
-            ex.clone().spawn(self.clone().try_connect(node.clone())).detach();
-        }
-        Ok(())
-    }
-
-    async fn try_connect(self: Arc<Self>, node: Node) -> DnetViewResult<()> {
-        debug!(target: "dnetview", "try_connect() START");
-        loop {
-            info!("Attempting to poll {}, RPC URL: {}", node.name, node.rpc_url);
-            // Parse node config and execute poll.
-            // On any failure, sleep and retry.
-            match RpcConnect::new(Url::parse(&node.rpc_url)?, node.name.clone()).await {
-                Ok(client) => {
-                    // We start by enabling dnet
-                    if let Err(e) = client.dnet_enable(true).await {
-                        error!("dnet_enable error: {:?}", e);
-                    }
-                    if let Err(e) = self.poll(&node, client).await {
-                        error!("Poll execution error: {:?}", e);
-                    }
-                }
-                Err(e) => {
-                    error!("RPC client creation error: {:?}", e);
-                }
-            }
-            self.parse_offline(node.name.clone()).await?;
-            async_util::sleep(2).await;
-        }
-    }
-
-    async fn poll(&self, node: &Node, client: RpcConnect) -> DnetViewResult<()> {
-        loop {
-            // Ping the node to verify if its online.
-            if let Err(e) = client.ping().await {
-                return Err(DnetViewError::Darkfi(e))
-            }
-
-            // Retrieve node info, based on its type
-            let response = match &node.node_type {
-                NodeType::LILITH => client.lilith_spawns().await,
-                NodeType::NORMAL => client.dnet_info().await,
-                NodeType::CONSENSUS => client.get_consensus_info().await,
-            };
-
-            // Parse response
-            match response {
-                Ok(reply) => {
-                    if reply.as_object().is_none() || reply.as_object().unwrap().is_empty() {
-                        return Err(DnetViewError::EmptyRpcReply)
-                    }
-
-                    match &node.node_type {
-                        NodeType::LILITH => {
-                            self.parse_lilith_data(
-                                reply.as_object().unwrap().clone(),
-                                node.name.clone(),
-                            )
-                            .await?
-                        }
-                        _ => self.parse_data(reply.as_object().unwrap(), node.name.clone()).await?,
-                    };
-                }
-                Err(e) => return Err(e),
-            }
-
-            // Sleep until next poll
-            async_util::sleep(2).await;
-        }
-    }
-
-    // If poll times out, inititalize data structures with empty values.
-    async fn parse_offline(&self, node_name: String) -> DnetViewResult<()> {
-        debug!(target: "dnetview", "parse_offline() START");
-        let sort = Session::Offline;
-
-        let mut sessions: Vec<SessionInfo> = Vec::new();
-        let hosts = Vec::new();
-
-        let node_id = make_node_id(&node_name)?;
-        let dnet_id = make_empty_id(&node_id, &sort, 0)?;
-        let addr = "Null".to_string();
-        let state = None;
-        let random_id = 0;
-        let remote_id = "Null".to_string();
-        let log = Vec::new();
-        let is_empty = true;
-
-        let slot = SlotInfo::new(
-            dnet_id.clone(),
-            node_id.clone(),
-            addr.clone(),
-            random_id,
-            remote_id,
-            log,
-            is_empty,
-        );
-
-        let session_info = SessionInfo::new(
-            dnet_id,
-            node_id.clone(),
-            addr.clone(),
-            state,
-            slot,
-            sort.clone(),
-            is_empty,
-        );
-        sessions.push(session_info);
-
-        // TODO: clean this up
-        let node = NodeInfo::new(
-            node_id.clone(),
-            node_name.clone(),
-            hosts,
-            sessions.clone(),
-            sessions.clone(),
-            is_empty,
-            true,
-        );
-
-        self.update_selectables(node).await?;
-        Ok(())
-    }
-
-    async fn parse_data(
-        &self,
-        reply: &serde_json::Map<String, Value>,
-        name: String,
-    ) -> DnetViewResult<()> {
-        let hosts = &reply["hosts"];
-        let inbound = &reply["inbound"];
-        let outbound = &reply["outbound"];
-
-        let node_id = make_node_id(&name)?;
-
-        let dnet_enabled: bool = {
-            if hosts.is_null() && inbound.is_null() && outbound.is_null() {
-                false
-            } else {
-                true
-            }
-        };
-
-        let hosts = self.parse_hosts(hosts).await?;
-        let inbound = self.parse_session(inbound, &node_id, Session::Inbound).await?;
-        let outbound = self.parse_session(outbound, &node_id, Session::Outbound).await?;
-
-        let node = NodeInfo::new(
-            node_id,
-            name,
-            hosts,
-            inbound.clone(),
-            outbound.clone(),
-            false,
-            dnet_enabled,
-        );
-
-        self.update_selectables(node).await?;
-        self.update_msgs(inbound.clone(), outbound.clone()).await?;
-
-        Ok(())
-    }
-
-    async fn parse_lilith_data(
-        &self,
-        reply: serde_json::Map<String, Value>,
-        name: String,
-    ) -> DnetViewResult<()> {
-        let spawns: Vec<serde_json::Map<String, Value>> =
-            serde_json::from_value(reply.get("spawns").unwrap().clone()).unwrap();
-
-        let mut networks = vec![];
-        for spawn in spawns {
-            let name = spawn.get("name").unwrap().as_str().unwrap().to_string();
-            let id = make_network_id(&name)?;
-            let urls: Vec<String> =
-                serde_json::from_value(spawn.get("urls").unwrap().clone()).unwrap();
-            let nodes: Vec<String> =
-                serde_json::from_value(spawn.get("hosts").unwrap().clone()).unwrap();
-            let network = NetworkInfo::new(id, name, urls, nodes);
-            networks.push(network);
-        }
-        let id = make_node_id(&name)?;
-        let lilith = LilithInfo::new(id.clone(), name, networks);
-        let lilith_obj = SelectableObject::Lilith(lilith.clone());
-
-        self.model.selectables.lock().await.insert(id, lilith_obj);
-        for network in lilith.networks {
-            let network_obj = SelectableObject::Network(network.clone());
-            self.model.selectables.lock().await.insert(network.id, network_obj);
-        }
-
-        Ok(())
-    }
-
-    async fn update_msgs(
-        &self,
-        inbounds: Vec<SessionInfo>,
-        outbounds: Vec<SessionInfo>,
-    ) -> DnetViewResult<()> {
-        for inbound in inbounds {
-            if !self.model.msg_map.lock().await.contains_key(&inbound.info.dnet_id) {
-                // we don't have this ID: it is a new node
-                self.model
-                    .msg_map
-                    .lock()
-                    .await
-                    .insert(inbound.info.dnet_id, inbound.info.log.clone());
-            } else {
-                // we have this id: append the msg values
-                match self.model.msg_map.lock().await.entry(inbound.info.dnet_id) {
-                    Entry::Vacant(e) => {
-                        e.insert(inbound.info.log);
-                    }
-                    Entry::Occupied(mut e) => {
-                        for msg in inbound.info.log {
-                            e.get_mut().push(msg);
-                        }
-                    }
-                }
-            }
-        }
-        for outbound in outbounds {
-            if !self.model.msg_map.lock().await.contains_key(&outbound.info.dnet_id) {
-                // we don't have this ID: it is a new node
-                self.model
-                    .msg_map
-                    .lock()
-                    .await
-                    .insert(outbound.info.dnet_id, outbound.info.log.clone());
-            } else {
-                // we have this id: append the msg values
-                match self.model.msg_map.lock().await.entry(outbound.info.dnet_id) {
-                    Entry::Vacant(e) => {
-                        e.insert(outbound.info.log);
-                    }
-                    Entry::Occupied(mut e) => {
-                        for msg in outbound.info.log {
-                            e.get_mut().push(msg);
-                        }
-                    }
-                }
-            }
-        }
-
-        Ok(())
-    }
-
-    async fn update_selectables(&self, node: NodeInfo) -> DnetViewResult<()> {
-        if node.is_offline && !node.dnet_enabled {
-            let node_obj = SelectableObject::Node(node.clone());
-            self.model.selectables.lock().await.insert(node.dnet_id.clone(), node_obj.clone());
-        } else {
-            let node_obj = SelectableObject::Node(node.clone());
-            self.model.selectables.lock().await.insert(node.dnet_id.clone(), node_obj.clone());
-            for inbound in node.inbound {
-                if !inbound.is_empty {
-                    let inbound_obj = SelectableObject::Session(inbound.clone());
-                    self.model
-                        .selectables
-                        .lock()
-                        .await
-                        .insert(inbound.clone().dnet_id, inbound_obj.clone());
-                    let info_obj = SelectableObject::Slot(inbound.info.clone());
-                    self.model
-                        .selectables
-                        .lock()
-                        .await
-                        .insert(inbound.info.clone().dnet_id, info_obj.clone());
-                }
-            }
-            for outbound in node.outbound {
-                if !outbound.is_empty {
-                    let outbound_obj = SelectableObject::Session(outbound.clone());
-                    self.model
-                        .selectables
-                        .lock()
-                        .await
-                        .insert(outbound.clone().dnet_id, outbound_obj.clone());
-                    let info_obj = SelectableObject::Slot(outbound.info.clone());
-                    self.model
-                        .selectables
-                        .lock()
-                        .await
-                        .insert(outbound.info.clone().dnet_id, info_obj.clone());
-                }
-            }
-        }
-        Ok(())
-    }
-
-    async fn parse_session(
-        &self,
-        reply: &Value,
-        node_id: &String,
-        sort: Session,
-    ) -> DnetViewResult<Vec<SessionInfo>> {
-        let session_id = make_session_id(&node_id, &sort)?;
-        let mut session_info: Vec<SessionInfo> = Vec::new();
-
-        // Dnetview is not enabled.
-        if reply.is_null() {
-            let sort2 = Session::Null;
-            let info_id = make_null_id(&node_id)?;
-            let node_id = node_id.to_string();
-            let addr = "Null".to_string();
-            let random_id = 0;
-            let remote_id = "Null".to_string();
-            let log = Vec::new();
-            let is_empty = true;
-
-            let slot = SlotInfo::new(
-                info_id.clone(),
-                node_id.clone(),
-                addr,
-                random_id,
-                remote_id,
-                log,
-                is_empty,
-            );
-            let is_empty = true;
-
-            let addr = "Null".to_string();
-            let state = None;
-            let session = SessionInfo::new(
-                // ..
-                info_id.clone(),
-                node_id.clone(),
-                addr,
-                state,
-                slot,
-                sort2.clone(),
-                is_empty,
-            );
-            session_info.push(session);
-
-            return Ok(session_info)
-        }
-
-        let sessions = reply.as_array().unwrap();
-
-        for session in sessions {
-            // TODO: display empty sessions?
-            if !session.is_null() {
-                match session.as_object() {
-                    Some(obj) => {
-                        let addr = obj.get("addr").unwrap().as_str().unwrap().to_string();
-
-                        let state: Option<String> = match obj.get("state") {
-                            Some(state) => Some(state.as_str().unwrap().to_string()),
-                            None => None,
-                        };
-
-                        let info: serde_json::Map<String, Value> =
-                            serde_json::from_value(obj.get("info").unwrap().clone()).unwrap();
-
-                        let slot_addr = info.get("addr").unwrap().as_str().unwrap().to_string();
-                        let random_id = info.get("random_id").unwrap().as_u64().unwrap();
-                        let remote_id =
-                            info.get("remote_id").unwrap().as_str().unwrap().to_string();
-                        let info_id = make_info_id(&random_id)?;
-
-                        let log: Vec<(NanoTimestamp, String, String)> =
-                            serde_json::from_value(info.get("log").unwrap().clone()).unwrap();
-
-                        // ...
-                        let node_id = node_id.to_string();
-                        let is_empty = false;
-
-                        let slot = SlotInfo::new(
-                            info_id.clone(),
-                            node_id.clone(),
-                            slot_addr,
-                            random_id,
-                            remote_id,
-                            log,
-                            is_empty,
-                        );
-
-                        let session = SessionInfo::new(
-                            session_id.clone(),
-                            node_id.clone(),
-                            addr.clone(),
-                            state,
-                            slot.clone(),
-                            sort.clone(),
-                            is_empty,
-                        );
-                        session_info.push(session);
-                    }
-                    None => return Err(DnetViewError::ValueIsNotObject),
-                }
-            }
-        }
-
-        Ok(session_info)
-    }
-
-    async fn parse_hosts(&self, hosts: &Value) -> DnetViewResult<Vec<String>> {
-        match hosts.as_array() {
-            Some(h) => match h.is_empty() {
-                true => Ok(Vec::new()),
-                false => {
-                    let hosts: Vec<String> =
-                        h.iter().map(|addr| addr.as_str().unwrap().to_string()).collect();
-                    Ok(hosts)
-                }
-            },
-
-            None => {
-                if hosts.is_null() {
-                    // TODO: this should probs just say null
-                    let h = Vec::new();
-                    return Ok(h)
-                }
-                Err(DnetViewError::ValueIsNotObject)
-            }
-        }
-    }
-}

+ 0 - 87
bin/dnetview/src/rpc.rs

@@ -1,87 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use serde_json::{json, Value};
-use url::Url;
-
-use darkfi::{
-    error::Result,
-    rpc::{client::RpcClient, jsonrpc::JsonRequest},
-};
-
-use crate::error::{DnetViewError, DnetViewResult};
-
-pub struct RpcConnect {
-    pub name: String,
-    pub rpc_client: RpcClient,
-}
-
-impl RpcConnect {
-    pub async fn new(url: Url, name: String) -> Result<Self> {
-        let rpc_client = RpcClient::new(url, None).await?;
-        Ok(Self { name, rpc_client })
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": "pong", "id": 42}
-    pub async fn ping(&self) -> Result<Value> {
-        let req = JsonRequest::new("ping", json!([]));
-        self.rpc_client.request(req).await
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "dnet_info", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": {"nodeID": [], "nodeinfo" [], "id": 42}
-    pub async fn dnet_info(&self) -> DnetViewResult<Value> {
-        let req = JsonRequest::new("dnet_info", json!([]));
-        match self.rpc_client.request(req).await {
-            Ok(req) => Ok(req),
-            Err(e) => Err(DnetViewError::Darkfi(e)),
-        }
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "dnet_switch", "params": [true], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": true, "id": 42}
-    pub async fn dnet_enable(&self, params: bool) -> DnetViewResult<Value> {
-        let req = JsonRequest::new("dnet_switch", json!([params]));
-        match self.rpc_client.request(req).await {
-            Ok(req) => Ok(req),
-            Err(e) => Err(DnetViewError::Darkfi(e)),
-        }
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "get_consensus_info", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": {"nodeID": [], "nodeinfo" [], "id": 42}
-    pub async fn get_consensus_info(&self) -> DnetViewResult<Value> {
-        let req = JsonRequest::new("get_consensus_info", json!([]));
-        match self.rpc_client.request(req).await {
-            Ok(req) => Ok(req),
-            Err(e) => Err(DnetViewError::Darkfi(e)),
-        }
-    }
-
-    // Returns all lilith node spawned networks names with their node addresses.
-    // --> {"jsonrpc": "2.0", "method": "spawns", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": "{spawns}", "id": 42}
-    pub async fn lilith_spawns(&self) -> DnetViewResult<Value> {
-        let req = JsonRequest::new("spawns", json!([]));
-        match self.rpc_client.request(req).await {
-            Ok(req) => Ok(req),
-            Err(e) => Err(DnetViewError::Darkfi(e)),
-        }
-    }
-}

+ 0 - 150
bin/dnetview/src/util.rs

@@ -1,150 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi::Result;
-
-use crate::model::{Session, SlotInfo};
-
-pub fn make_node_id(node_name: &String) -> Result<String> {
-    let mut id = hex::encode(node_name);
-    id.insert_str(0, "NODE");
-    Ok(id)
-}
-
-pub fn make_network_id(node_name: &String) -> Result<String> {
-    let mut id = hex::encode(node_name);
-    id.insert_str(0, "NETWORK");
-    Ok(id)
-}
-
-pub fn make_null_id(node_name: &String) -> Result<String> {
-    let mut id = hex::encode(node_name);
-    id.insert_str(0, "NULL");
-    Ok(id)
-}
-
-pub fn make_session_id(node_id: &str, session: &Session) -> Result<String> {
-    let mut num = 0_u64;
-
-    let session_chars = match session {
-        Session::Inbound => vec!['i', 'n'],
-        Session::Outbound => vec!['o', 'u', 't'],
-        //Session::Manual => vec!['m', 'a', 'n'],
-        Session::Offline => vec!['o', 'f', 'f'],
-        Session::Null => vec!['n', 'u', 'l', 'l'],
-    };
-
-    for i in session_chars {
-        num += i as u64
-    }
-
-    for i in node_id.chars() {
-        num += i as u64
-    }
-
-    let mut id = hex::encode(num.to_ne_bytes());
-    id.insert_str(0, "SESSION");
-    Ok(id)
-}
-
-pub fn make_info_id(id: &u64) -> Result<String> {
-    let mut id = hex::encode(id.to_ne_bytes());
-    id.insert_str(0, "INFO");
-    Ok(id)
-}
-
-pub fn make_empty_id(node_id: &str, session: &Session, count: u64) -> Result<String> {
-    let count = count * 2;
-
-    let mut num = 0_u64;
-
-    let id = match session {
-        Session::Inbound => {
-            let session_chars = vec!['i', 'n'];
-            for i in session_chars {
-                num += i as u64
-            }
-            for i in node_id.chars() {
-                num += i as u64
-            }
-            num += count;
-            let mut id = hex::encode(num.to_ne_bytes());
-            id.insert_str(0, "EMPTYIN");
-            id
-        }
-        Session::Outbound => {
-            let session_chars = vec!['o', 'u', 't'];
-            for i in session_chars {
-                num += i as u64
-            }
-            for i in node_id.chars() {
-                num += i as u64
-            }
-            num += count;
-            let mut id = hex::encode(num.to_ne_bytes());
-            id.insert_str(0, "EMPTYOUT");
-            id
-        }
-        //Session::Manual => {
-        //    let session_chars = vec!['m', 'a', 'n'];
-        //    for i in session_chars {
-        //        num += i as u64
-        //    }
-        //    for i in node_id.chars() {
-        //        num += i as u64
-        //    }
-        //    num += count;
-        //    let mut id = hex::encode(num.to_ne_bytes());
-        //    id.insert_str(0, "EMPTYMAN");
-        //    id
-        //}
-        Session::Offline => {
-            let session_chars = vec!['o', 'f', 'f'];
-            for i in session_chars {
-                num += i as u64
-            }
-            for i in node_id.chars() {
-                num += i as u64
-            }
-            num += count;
-            let mut id = hex::encode(num.to_ne_bytes());
-            id.insert_str(0, "EMPTYOFF");
-            id
-        }
-        Session::Null => {
-            let session_chars = vec!['n', 'u', 'l', 'l'];
-            for i in session_chars {
-                num += i as u64
-            }
-            for i in node_id.chars() {
-                num += i as u64
-            }
-            num += count;
-            let mut id = hex::encode(num.to_ne_bytes());
-            id.insert_str(0, "NULL");
-            id
-        }
-    };
-
-    Ok(id)
-}
-
-// TODO: Rename to is empty slot.
-pub fn is_empty_session(connects: &[SlotInfo]) -> bool {
-    return connects.iter().all(|conn| conn.is_empty)
-}

+ 0 - 545
bin/dnetview/src/view.rs

@@ -1,545 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//use log::debug;
-use std::collections::HashMap;
-
-use ratatui::{
-    backend::Backend,
-    layout::{Constraint, Direction, Layout, Rect},
-    style::{Color, Modifier, Style},
-    text::{Line, Span},
-    widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
-    Frame,
-};
-
-use darkfi::util::time::NanoTimestamp;
-
-use crate::{
-    error::{DnetViewError, DnetViewResult},
-    model::{NodeInfo, SelectableObject},
-};
-
-type MsgLog = Vec<(NanoTimestamp, String, String)>;
-type MsgMap = HashMap<String, MsgLog>;
-
-#[derive(Debug, Clone)]
-pub struct View {
-    pub id_menu: IdMenu,
-    pub msg_list: MsgList,
-    pub selectables: HashMap<String, SelectableObject>,
-    pub ordered_list: Vec<String>,
-}
-
-impl Default for View {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl<'a> View {
-    pub fn new() -> Self {
-        let msg_map = HashMap::new();
-        let msg_list = MsgList::new(msg_map, 0);
-        let selectables = HashMap::new();
-        let id_menu = IdMenu::new(Vec::new());
-        let ordered_list = Vec::new();
-
-        Self { id_menu, msg_list, selectables, ordered_list }
-    }
-
-    pub fn update(&mut self, msg_map: MsgMap, selectables: HashMap<String, SelectableObject>) {
-        self.update_selectable(selectables.clone());
-        self.update_msg_list(msg_map);
-        self.update_id_menu(selectables);
-        self.update_msg_index();
-        self.make_ordered_list();
-    }
-
-    // We copy the values into a string to initialize List as a StatefulObject.
-    fn update_id_menu(&mut self, selectables: HashMap<String, SelectableObject>) {
-        for id in selectables.keys() {
-            if !self.id_menu.ids.iter().any(|i| i == id) {
-                self.id_menu.ids.push(id.to_string());
-            }
-        }
-    }
-
-    // We first add every selectable object into a hashmap to avoid duplicates.
-    fn update_selectable(&mut self, selectables: HashMap<String, SelectableObject>) {
-        for (id, obj) in selectables {
-            self.selectables.insert(id, obj);
-        }
-    }
-
-    // The order of the ordered_list created here must match the order
-    // of the Vec<ListItem> created in render_left().
-    // This is used to render_right() correctly.
-    fn make_ordered_list(&mut self) {
-        for obj in self.selectables.values() {
-            match obj {
-                SelectableObject::Node(node) => {
-                    if !self.ordered_list.iter().any(|i| i == &node.dnet_id) {
-                        self.ordered_list.push(node.dnet_id.clone());
-                    }
-                    if !node.is_offline && node.dnet_enabled {
-                        for inbound in &node.inbound {
-                            if !inbound.is_empty {
-                                if !self.ordered_list.iter().any(|i| i == &inbound.dnet_id) {
-                                    self.ordered_list.push(inbound.dnet_id.clone());
-                                }
-                                if !self.ordered_list.iter().any(|i| i == &inbound.info.dnet_id) {
-                                    self.ordered_list.push(inbound.info.dnet_id.clone());
-                                }
-                            }
-                        }
-                        for outbound in &node.outbound {
-                            if !outbound.is_empty {
-                                if !self.ordered_list.iter().any(|i| i == &outbound.dnet_id) {
-                                    self.ordered_list.push(outbound.dnet_id.clone());
-                                }
-                                if !self.ordered_list.iter().any(|i| i == &outbound.info.dnet_id) {
-                                    self.ordered_list.push(outbound.info.dnet_id.clone());
-                                }
-                            }
-                        }
-                    }
-                }
-                SelectableObject::Lilith(lilith) => {
-                    if !self.ordered_list.iter().any(|i| i == &lilith.id) {
-                        self.ordered_list.push(lilith.id.clone());
-                    }
-                    for network in &lilith.networks {
-                        if !self.ordered_list.iter().any(|i| i == &network.id) {
-                            self.ordered_list.push(network.id.clone());
-                        }
-                    }
-                }
-                _ => (),
-            }
-        }
-    }
-
-    // TODO: this function displays msgs according to what id is
-    // selected.  It's ugly, would prefer something more simple.
-    fn update_msg_index(&mut self) {
-        if let Some(sel) = self.id_menu.state.selected() {
-            if let Some(ord) = self.ordered_list.get(sel) {
-                if let Some(i) = self.msg_list.msg_map.get(ord) {
-                    self.msg_list.index = i.len();
-                }
-            }
-        }
-    }
-
-    fn update_msg_list(&mut self, msg_map: MsgMap) {
-        for (id, msg) in msg_map {
-            self.msg_list.msg_map.insert(id, msg);
-        }
-    }
-
-    pub fn render<B: Backend>(&mut self, f: &mut Frame<'_, B>) -> DnetViewResult<()> {
-        let margin = 2;
-        let direction = Direction::Horizontal;
-        let cnstrnts = vec![Constraint::Percentage(50), Constraint::Percentage(50)];
-
-        let slice = Layout::default()
-            .direction(direction)
-            .margin(margin)
-            .constraints(cnstrnts)
-            .split(f.size());
-
-        self.render_left(f, slice[0])?;
-        if self.ordered_list.is_empty() {
-            // we have not received any data
-            Ok(())
-        } else {
-            // get the id at the current index
-            match self.id_menu.state.selected() {
-                Some(i) => match self.ordered_list.get(i) {
-                    Some(i) => {
-                        let id = i.clone();
-                        self.render_right(f, slice[1], id)?;
-                        Ok(())
-                    }
-                    None => Err(DnetViewError::NoIdAtIndex),
-                },
-                // nothing is selected right now
-                None => Ok(()),
-            }
-        }
-    }
-
-    fn render_left<B: Backend>(&mut self, f: &mut Frame<'_, B>, slice: Rect) -> DnetViewResult<()> {
-        let style = Style::default();
-        let mut nodes = Vec::new();
-
-        for obj in self.selectables.values() {
-            match obj {
-                SelectableObject::Node(node) => {
-                    if node.is_offline {
-                        let style =
-                            Style::default().fg(Color::LightBlue).add_modifier(Modifier::ITALIC);
-                        let mut name = String::new();
-                        name.push_str(&node.name);
-                        name.push_str("(Offline)");
-                        let name_span = Span::styled(name, style);
-                        let lines = vec![Line::from(name_span)];
-                        let names = ListItem::new(lines);
-                        nodes.push(names);
-                    } else {
-                        if !node.dnet_enabled {
-                            let style =
-                                Style::default().fg(Color::LightBlue).add_modifier(Modifier::BOLD);
-                            let mut name = String::new();
-                            name.push_str(&node.name);
-                            name.push_str("(dnetview is not enabled)");
-                            let name_span = Span::styled(name, style);
-                            let lines = vec![Line::from(name_span)];
-                            let names = ListItem::new(lines);
-                            nodes.push(names);
-                        } else {
-                            let name_span = Span::raw(&node.name);
-                            let lines = vec![Line::from(name_span)];
-                            let names = ListItem::new(lines);
-                            nodes.push(names);
-
-                            if !node.inbound.is_empty() {
-                                let name = Span::styled(format!("    Inbound"), style);
-                                let lines = vec![Line::from(name)];
-                                let names = ListItem::new(lines);
-                                nodes.push(names);
-
-                                for inbound in &node.inbound {
-                                    let mut infos = Vec::new();
-                                    match inbound.info.addr.as_str() {
-                                        "Null" => {
-                                            let style = Style::default()
-                                                .fg(Color::Blue)
-                                                .add_modifier(Modifier::ITALIC);
-                                            let name = Span::styled(
-                                                format!("        {} ", inbound.info.addr),
-                                                style,
-                                            );
-                                            infos.push(name);
-                                        }
-                                        addr => {
-                                            let name =
-                                                Span::styled(format!("        {}", addr), style);
-                                            infos.push(name);
-                                            if !inbound.info.remote_id.is_empty() {
-                                                let remote_id = Span::styled(
-                                                    format!("({})", inbound.info.remote_id),
-                                                    style,
-                                                );
-                                                infos.push(remote_id)
-                                            }
-                                        }
-                                    }
-                                    let lines = vec![Line::from(infos)];
-                                    let names = ListItem::new(lines);
-                                    nodes.push(names);
-                                }
-                            }
-
-                            if !&node.outbound.is_empty() {
-                                let name = Span::styled(format!("    Outbound"), style);
-                                let lines = vec![Line::from(name)];
-                                let names = ListItem::new(lines);
-                                nodes.push(names);
-
-                                for outbound in &node.outbound {
-                                    let mut infos = Vec::new();
-                                    match outbound.info.addr.as_str() {
-                                        "Null" => {
-                                            let style = Style::default()
-                                                .fg(Color::Blue)
-                                                .add_modifier(Modifier::ITALIC);
-                                            let name = Span::styled(
-                                                format!("        {} ", outbound.info.addr),
-                                                style,
-                                            );
-                                            infos.push(name);
-                                        }
-                                        addr => {
-                                            let name =
-                                                Span::styled(format!("        {}", addr), style);
-                                            infos.push(name);
-                                            if !outbound.info.remote_id.is_empty() {
-                                                let remote_id = Span::styled(
-                                                    format!("({})", outbound.info.remote_id),
-                                                    style,
-                                                );
-                                                infos.push(remote_id)
-                                            }
-                                        }
-                                    }
-                                    let lines = vec![Line::from(infos)];
-                                    let names = ListItem::new(lines);
-                                    nodes.push(names);
-                                }
-                            }
-                        }
-                    }
-                }
-                SelectableObject::Lilith(lilith) => {
-                    let name_span = Span::raw(&lilith.name);
-                    let lines = vec![Line::from(name_span)];
-                    let names = ListItem::new(lines);
-                    nodes.push(names);
-                    for network in &lilith.networks {
-                        let name = Span::styled(format!("    {}", network.name), style);
-                        let lines = vec![Line::from(name)];
-                        let names = ListItem::new(lines);
-                        nodes.push(names);
-                    }
-                }
-                _ => (),
-            }
-        }
-        let nodes =
-            List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> ");
-
-        f.render_stateful_widget(nodes, slice, &mut self.id_menu.state);
-
-        Ok(())
-    }
-
-    fn parse_msg_list(&self, info_id: String) -> DnetViewResult<List<'a>> {
-        let send_style = Style::default().fg(Color::LightCyan);
-        let recv_style = Style::default().fg(Color::DarkGray);
-        let mut texts = Vec::new();
-        let mut lines = Vec::new();
-        let log = self.msg_list.msg_map.get(&info_id);
-        match log {
-            Some(values) => {
-                for (i, (t, k, v)) in values.iter().enumerate() {
-                    lines.push(match k.as_str() {
-                        "send" => {
-                            Span::styled(format!("{}  {}             S: {}", i, t, v), send_style)
-                        }
-                        "recv" => {
-                            Span::styled(format!("{}  {}             R: {}", i, t, v), recv_style)
-                        }
-                        data => return Err(DnetViewError::UnexpectedData(data.to_string())),
-                    });
-                }
-            }
-            None => return Err(DnetViewError::CannotFindId),
-        }
-        for line in lines.clone() {
-            let text = ListItem::new(line);
-            texts.push(text);
-        }
-
-        let msg_list = List::new(texts).block(Block::default().borders(Borders::ALL));
-
-        Ok(msg_list)
-    }
-
-    fn render_right<B: Backend>(
-        &mut self,
-        f: &mut Frame<'_, B>,
-        slice: Rect,
-        selected: String,
-    ) -> DnetViewResult<()> {
-        //debug!(target: "dnetview", "render_right() selected ID: {}", selected.clone());
-        let style = Style::default();
-        let mut lines = Vec::new();
-
-        if self.selectables.is_empty() {
-            // we have not received any selectable data
-            return Ok(())
-        } else {
-            let info = self.selectables.get(&selected);
-
-            match info {
-                Some(SelectableObject::Node(node)) => {
-                    lines.push(Line::from(Span::styled("Type: Normal", style)));
-                    lines.push(Line::from(Span::styled("Hosts:", style)));
-                    for host in &node.hosts {
-                        lines.push(Line::from(Span::styled(format!("   {}", host), style)));
-                    }
-                }
-                Some(SelectableObject::Session(session)) => {
-                    let addr = Span::styled(format!("Addr: {}", session.addr), style);
-                    lines.push(Line::from(addr));
-
-                    if session.state.is_some() {
-                        let addr = Span::styled(
-                            format!("State: {}", session.state.as_ref().unwrap()),
-                            style,
-                        );
-                        lines.push(Line::from(addr));
-                    }
-                }
-                Some(SelectableObject::Slot(slot)) => {
-                    let text = self.parse_msg_list(slot.dnet_id.clone())?;
-                    f.render_stateful_widget(text, slice, &mut self.msg_list.state);
-                }
-                Some(SelectableObject::Lilith(_lilith)) => {
-                    lines.push(Line::from(Span::styled("Type: Lilith", style)));
-                }
-                Some(SelectableObject::Network(network)) => {
-                    lines.push(Line::from(Span::styled("URLs:", style)));
-                    for url in &network.urls {
-                        lines.push(Line::from(Span::styled(format!("   {}", url), style)));
-                    }
-                    lines.push(Line::from(Span::styled("Hosts:", style)));
-                    for node in &network.nodes {
-                        lines.push(Line::from(Span::styled(format!("   {}", node), style)));
-                    }
-                }
-                None => return Err(DnetViewError::NotSelectableObject),
-            }
-        }
-
-        let graph = Paragraph::new(lines)
-            .block(Block::default().borders(Borders::ALL))
-            .style(Style::default());
-
-        f.render_widget(graph, slice);
-
-        Ok(())
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct IdMenu {
-    pub state: ListState,
-    pub ids: Vec<String>,
-}
-
-impl IdMenu {
-    pub fn new(ids: Vec<String>) -> IdMenu {
-        IdMenu { state: ListState::default(), ids }
-    }
-
-    pub fn next(&mut self) {
-        let i = match self.state.selected() {
-            Some(i) => {
-                if i >= self.ids.len() - 1 {
-                    0
-                } else {
-                    i + 1
-                }
-            }
-            None => 0,
-        };
-        self.state.select(Some(i));
-    }
-
-    pub fn previous(&mut self) {
-        let i = match self.state.selected() {
-            Some(i) => {
-                if i == 0 {
-                    self.ids.len() - 1
-                } else {
-                    i - 1
-                }
-            }
-            None => 0,
-        };
-        self.state.select(Some(i));
-    }
-
-    pub fn unselect(&mut self) {
-        self.state.select(None);
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct MsgList {
-    pub state: ListState,
-    pub msg_map: MsgMap,
-    pub index: usize,
-}
-
-impl MsgList {
-    pub fn new(msg_map: MsgMap, index: usize) -> MsgList {
-        MsgList { state: ListState::default(), msg_map, index }
-    }
-
-    // TODO: reimplement
-    //pub fn next(&mut self) {
-    //    let i = match self.state.selected() {
-    //        Some(i) => {
-    //            if i >= self.msg_len - 1 {
-    //                0
-    //            } else {
-    //                i + 1
-    //            }
-    //        }
-    //        None => 0,
-    //    };
-    //    self.state.select(Some(i));
-    //}
-
-    //pub fn previous(&mut self) {
-    //    let i = match self.state.selected() {
-    //        Some(i) => {
-    //            if i == 0 {
-    //                self.msg_len - 1
-    //            } else {
-    //                i - 1
-    //            }
-    //        }
-    //        None => 0,
-    //    };
-    //    self.state.select(Some(i));
-    //}
-
-    pub fn scroll(&mut self) -> DnetViewResult<()> {
-        let i = match self.state.selected() {
-            Some(i) => i + self.index,
-            None => 0,
-        };
-        self.state.select(Some(i));
-        Ok(())
-    }
-
-    pub fn unselect(&mut self) {
-        self.state.select(None);
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct NodeInfoView {
-    pub index: usize,
-    pub infos: HashMap<String, NodeInfo>,
-}
-
-impl NodeInfoView {
-    pub fn new(infos: HashMap<String, NodeInfo>) -> NodeInfoView {
-        let index = 0;
-
-        NodeInfoView { index, infos }
-    }
-
-    //pub fn next(&mut self) {
-    //    self.index = (self.index + 1) % self.infos.len();
-    //}
-
-    //pub fn previous(&mut self) {
-    //    if self.index > 0 {
-    //        self.index -= 1;
-    //    } else {
-    //        self.index = self.infos.len() - 1;
-    //    }
-    //}
-}