Просмотр исходного кода

Merge branch 'master' of https://github.com/darkrenaissance/darkfi

mohab 4 лет назад
Родитель
Сommit
22b0bd6e66
43 измененных файлов с 753 добавлено и 722 удалено
  1. 3 8
      Cargo.toml
  2. 10 6
      Makefile
  3. 28 27
      bin/ircd/src/main.rs
  4. 21 11
      bin/ircd/src/protocol_privmsg.rs
  5. 0 86
      bin/map/src/app.rs
  6. 4 5
      bin/map/src/lib.rs
  7. 0 50
      bin/map/src/list.rs
  8. 104 69
      bin/map/src/main.rs
  9. 81 0
      bin/map/src/model.rs
  10. 0 51
      bin/map/src/node_info.rs
  11. 0 56
      bin/map/src/notes/list.rs
  12. 0 2
      bin/map/src/types.rs
  13. 13 12
      bin/map/src/ui.rs
  14. 1 1
      book/src/tutorial.md
  15. 10 4
      proof/burn.rs
  16. 10 4
      proof/mint.rs
  17. 14 13
      src/net/channel.rs
  18. 2 2
      src/net/message.rs
  19. 4 2
      src/net/message_subscriber.rs
  20. 6 5
      src/net/mod.rs
  21. 18 6
      src/net/p2p.rs
  22. 18 0
      src/net/protocol/mod.rs
  23. 36 25
      src/net/protocol/protocol_address.rs
  24. 14 0
      src/net/protocol/protocol_base.rs
  25. 0 0
      src/net/protocol/protocol_jobs_manager.rs
  26. 50 36
      src/net/protocol/protocol_ping.rs
  27. 56 0
      src/net/protocol/protocol_registry.rs
  28. 34 19
      src/net/protocol/protocol_seed.rs
  29. 7 7
      src/net/protocol/protocol_version.rs
  30. 12 8
      src/net/session/inbound_session.rs
  31. 11 8
      src/net/session/manual_session.rs
  32. 8 0
      src/net/session/mod.rs
  33. 11 8
      src/net/session/outbound_session.rs
  34. 11 7
      src/net/session/seed_session.rs
  35. 27 4
      src/net/session/session.rs
  36. 1 4
      src/zk/mod.rs
  37. 1 1
      src/zk/vm_stack.rs
  38. 14 48
      src/zkas/analyzer.rs
  39. 11 34
      src/zkas/compiler.rs
  40. 41 0
      src/zkas/error.rs
  41. 19 35
      src/zkas/lexer.rs
  42. 2 0
      src/zkas/mod.rs
  43. 40 58
      src/zkas/parser.rs

+ 3 - 8
Cargo.toml

@@ -193,6 +193,7 @@ crypto = [
     "bs58",
 
     "util",
+    "zkas",
 ]
 
 wallet = [
@@ -223,12 +224,6 @@ zkas = [
     "util",
 ]
 
-zkvm = [
-    "crypto",
-    "zkas",
-]
-
-
 [[example]]
 name = "net"
 path = "example/net.rs"
@@ -252,9 +247,9 @@ required-features = ["crypto"]
 [[example]]
 name = "mint"
 path = "proof/mint.rs"
-required-features = ["cli", "zkvm"]
+required-features = ["cli", "crypto", "zkas"]
 
 [[example]]
 name = "burn"
 path = "proof/burn.rs"
-required-features = ["cli", "zkvm"]
+required-features = ["cli", "crypto", "zkas"]

+ 10 - 6
Makefile

@@ -37,13 +37,17 @@ test: test-vm test-tx
 	$(CARGO) test --release --all-features --all
 
 test-tx:
-	$(CARGO) run --release --features=node --example tx
+	$(CARGO) run --release --features=node,zkas --example tx
 
-test-vm: zkas
-	./zkas proof/mint.zk
-	$(CARGO) run --release --features=cli,zkvm --example mint
-	./zkas proof/burn.zk
-	$(CARGO) run --release --features=cli,zkvm --example burn
+VM_SRC = proof/mint.zk proof/burn.zk
+VM_BIN = $(VM_SRC:=.bin)
+
+$(VM_BIN): zkas $(VM_SRC)
+	./zkas $(basename $@) -o $@
+
+test-vm: $(VM_BIN)
+	$(CARGO) run --release --features=cli,crypto,zkas --example mint
+	$(CARGO) run --release --features=cli,crypto,zkas --example burn
 
 clean:
 	rm -f $(BINS)

+ 28 - 27
bin/ircd/src/main.rs

@@ -98,25 +98,6 @@ async fn process_user_input(
     Ok(())
 }
 
-async fn channel_loop(
-    p2p: net::P2pPtr,
-    sender: async_channel::Sender<Arc<PrivMsg>>,
-    seen_privmsg_ids: SeenPrivMsgIdsPtr,
-    executor: Arc<Executor<'_>>,
-) -> Result<()> {
-    let new_channel_sub = p2p.subscribe_channel().await;
-
-    loop {
-        let channel = new_channel_sub.receive().await?;
-
-        let protocol_privmsg =
-            ProtocolPrivMsg::new(channel, sender.clone(), seen_privmsg_ids.clone(), p2p.clone())
-                .await;
-
-        protocol_privmsg.start(executor.clone()).await;
-    }
-}
-
 async fn start(executor: Arc<Executor<'_>>, options: ProgramOptions) -> Result<()> {
     let listener = match Async::<TcpListener>::bind(options.irc_accept_addr) {
         Ok(listener) => listener,
@@ -145,7 +126,28 @@ async fn start(executor: Arc<Executor<'_>>, options: ProgramOptions) -> Result<(
 
     let seen_privmsg_ids = SeenPrivMsgIds::new();
 
-    let p2p = net::P2p::new(options.network_settings);
+    //
+    // PrivMsg protocol
+    //
+    let p2p = net::P2p::new(options.network_settings).await;
+    let registry = p2p.protocol_registry();
+
+    let (sender, recvr) = async_channel::unbounded();
+    let seen_privmsg_ids2 = seen_privmsg_ids.clone();
+    let sender2 = sender.clone();
+    registry.register(
+        !net::SESSION_SEED,
+        move |channel, p2p| {
+            let sender = sender2.clone();
+            let seen_privmsg_ids = seen_privmsg_ids2.clone();
+            async move {
+                ProtocolPrivMsg::new(channel, sender, seen_privmsg_ids, p2p).await
+            }
+        }).await;
+
+    //
+    // p2p network main instance
+    //
     // Performs seed session
     p2p.clone().start(executor.clone()).await?;
     // Actual main p2p session
@@ -159,13 +161,9 @@ async fn start(executor: Arc<Executor<'_>>, options: ProgramOptions) -> Result<(
         })
         .detach();
 
-    let (sender, recvr) = async_channel::unbounded();
-    // for now the p2p and channel sub sessions just run forever
-    // so detach them as background processes.
-    executor
-        .spawn(channel_loop(p2p.clone(), sender, seen_privmsg_ids.clone(), executor.clone()))
-        .detach();
-
+    //
+    // RPC interface
+    //
     let ex2 = executor.clone();
     let ex3 = ex2.clone();
     let rpc_interface = Arc::new(JsonRpcInterface {});
@@ -173,6 +171,9 @@ async fn start(executor: Arc<Executor<'_>>, options: ProgramOptions) -> Result<(
         .spawn(async move { listen_and_serve(server_config, rpc_interface, ex3).await })
         .detach();
 
+    //
+    // IRC instance
+    //
     loop {
         let (stream, peer_addr) = match listener.accept().await {
             Ok((s, a)) => (s, a),

+ 21 - 11
bin/ircd/src/protocol_privmsg.rs

@@ -1,3 +1,4 @@
+use async_trait::async_trait;
 use async_executor::Executor;
 
 use darkfi::{net, Result};
@@ -20,12 +21,10 @@ impl ProtocolPrivMsg {
         notify_queue_sender: async_channel::Sender<Arc<PrivMsg>>,
         seen_privmsg_ids: SeenPrivMsgIdsPtr,
         p2p: net::P2pPtr,
-    ) -> Arc<Self> {
+    ) -> net::ProtocolBasePtr {
         let message_subsytem = channel.get_message_subsystem();
         message_subsytem.add_dispatch::<PrivMsg>().await;
 
-        debug!("ADDED DISPATCH");
-
         let privmsg_sub =
             channel.subscribe_msg::<PrivMsg>().await.expect("Missing PrivMsg dispatcher!");
 
@@ -38,15 +37,8 @@ impl ProtocolPrivMsg {
         })
     }
 
-    pub async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) {
-        debug!(target: "ircd", "ProtocolPrivMsg::start() [START]");
-        self.jobsman.clone().start(executor.clone());
-        self.jobsman.clone().spawn(self.clone().handle_receive_privmsg(), executor.clone()).await;
-        debug!(target: "ircd", "ProtocolPrivMsg::start() [END]");
-    }
-
     async fn handle_receive_privmsg(self: Arc<Self>) -> Result<()> {
-        debug!(target: "ircd", "ProtocolAddress::handle_receive_privmsg() [START]");
+        debug!(target: "ircd", "ProtocolPrivMsg::handle_receive_privmsg() [START]");
         loop {
             let privmsg = self.privmsg_sub.receive().await?;
 
@@ -71,3 +63,21 @@ impl ProtocolPrivMsg {
         }
     }
 }
+
+#[async_trait]
+impl net::ProtocolBase for ProtocolPrivMsg {
+    /// Starts ping-pong keep-alive messages exchange. Runs ping-pong in the
+    /// protocol task manager, then queues the reply. Sends out a ping and
+    /// waits for pong reply. Waits for ping and replies with a pong.
+    async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
+        debug!(target: "ircd", "ProtocolPrivMsg::start() [START]");
+        self.jobsman.clone().start(executor.clone());
+        self.jobsman.clone().spawn(self.clone().handle_receive_privmsg(), executor.clone()).await;
+        debug!(target: "ircd", "ProtocolPrivMsg::start() [END]");
+        Ok(())
+    }
+
+    fn name(&self) -> &'static str {
+        "ProtocolPrivMsg"
+    }
+}

+ 0 - 86
bin/map/src/app.rs

@@ -1,86 +0,0 @@
-use crate::{
-    list::NodeIdList,
-    node_info::{NodeInfo, NodeInfoView},
-};
-//use smol::Timer;
-//use std::{collections::HashMap, time::Duration};
-
-// make a structure to be able to modify and read them
-// protect using a mutex
-// arc reference
-#[derive(Clone)]
-pub struct App {
-    pub node_list: NodeIdList,
-    pub node_info: NodeInfoView,
-}
-
-impl App {
-    pub fn new() -> App {
-        // append to vector of node info
-        let infos = vec![
-            NodeInfo {
-                id: "0385048034sodisofjhosd1111q3434".to_string(),
-                connections: 10,
-                is_active: true,
-                last_message: "hey how are you?".to_string(),
-            },
-            NodeInfo {
-                id: "09w30we9wsnfksdfkdjflsjkdfjdfsd".to_string(),
-                connections: 5,
-                is_active: false,
-                last_message: "lmao".to_string(),
-            },
-            NodeInfo {
-                id: "038043325alsdlasjfrsdfsdfsdjsdf".to_string(),
-                connections: 7,
-                is_active: true,
-                last_message: "gm".to_string(),
-            },
-            NodeInfo {
-                id: "04985034953ldflsdjflsdjflsdjfii".to_string(),
-                connections: 2,
-                is_active: true,
-                last_message: "hihi".to_string(),
-            },
-            NodeInfo {
-                id: "09850249352asdjapsdikalskasdkas".to_string(),
-                connections: 10,
-                is_active: true,
-                last_message: "wtf".to_string(),
-            },
-        ];
-
-        let node_info = NodeInfoView::new(infos.clone());
-
-        let ids = vec![
-            infos[0].id.clone(),
-            infos[1].id.clone(),
-            infos[2].id.clone(),
-            infos[3].id.clone(),
-            infos[4].id.clone(),
-        ];
-
-        let node_list = NodeIdList::new(ids);
-        App { node_list, node_info }
-    }
-
-    // TODO: implement this
-    //async fn sleep(self, dur: Duration) {
-    //    Timer::after(dur).await;
-    //}
-
-    pub async fn update(self, node_vec: Vec<NodeInfo>) -> App {
-        let node_info = NodeInfoView::new(node_vec.clone());
-
-        let ids = vec![node_vec[0].id.clone(), node_vec[1].id.clone(), node_vec[2].id.clone()];
-
-        let node_list = NodeIdList::new(ids);
-        App { node_list, node_info }
-    }
-}
-
-impl Default for App {
-    fn default() -> Self {
-        Self::new()
-    }
-}

+ 4 - 5
bin/map/src/lib.rs

@@ -1,8 +1,7 @@
-pub mod app;
-pub mod list;
-pub mod node_info;
-pub mod types;
+pub mod model;
 pub mod ui;
+pub mod view;
 
-pub use app::App;
+pub use model::{IdList, InfoList, Model, NodeInfo};
 pub use ui::ui;
+pub use view::{IdListView, InfoListView, View};

+ 0 - 50
bin/map/src/list.rs

@@ -1,50 +0,0 @@
-//use std::collections::HashMap;
-use tui::widgets::ListState;
-
-#[derive(Clone)]
-pub struct NodeIdList {
-    pub state: ListState,
-    pub node_id: Vec<String>,
-}
-
-impl NodeIdList {
-    pub fn new(node_id: Vec<String>) -> NodeIdList {
-        NodeIdList { state: ListState::default(), node_id }
-    }
-
-    pub fn next(&mut self) {
-        let i = match self.state.selected() {
-            Some(i) => {
-                if i >= self.node_id.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.node_id.len() - 1
-                } else {
-                    i - 1
-                }
-            }
-            None => 0,
-        };
-        self.state.select(Some(i));
-    }
-
-    pub fn unselect(&mut self) {
-        self.state.select(None);
-    }
-}
-
-//pub async fn add_seen(&self, id: u32) {
-//    self.privmsg_ids.lock().await.insert(id);
-//}

+ 104 - 69
bin/map/src/main.rs

@@ -1,14 +1,10 @@
-// select each connection and show log of traffic
-// use rpc to get some info from the ircd network
-// ircd::logger keeps track of network info
-// map rpc polls logger for info about nodes, etc
 use darkfi::{
     error::{Error, Result},
     rpc::{jsonrpc, jsonrpc::JsonResult},
     util::async_util,
 };
 
-use async_std::sync::Arc;
+use async_std::sync::{Arc, Mutex};
 use easy_parallel::Parallel;
 use log::debug;
 use serde_json::{json, Value};
@@ -20,7 +16,12 @@ use tui::{
     Terminal,
 };
 
-use map::{node_info::NodeInfo, ui, App};
+use map::{
+    model::{IdList, InfoList, NodeInfo},
+    ui,
+    view::{IdListView, InfoListView},
+    Model, View,
+};
 
 struct Map {
     url: String,
@@ -78,7 +79,53 @@ async fn main() -> Result<()> {
 
     terminal.clear()?;
 
-    let app = App::new();
+    let infos = vec![
+        NodeInfo {
+            id: "0385048034sodisofjhosd1111q3434".to_string(),
+            connections: 10,
+            is_active: true,
+            last_message: "hey how are you?".to_string(),
+        },
+        NodeInfo {
+            id: "09w30we9wsnfksdfkdjflsjkdfjdfsd".to_string(),
+            connections: 5,
+            is_active: false,
+            last_message: "lmao".to_string(),
+        },
+        NodeInfo {
+            id: "038043325alsdlasjfrsdfsdfsdjsdf".to_string(),
+            connections: 7,
+            is_active: true,
+            last_message: "gm".to_string(),
+        },
+        NodeInfo {
+            id: "04985034953ldflsdjflsdjflsdjfii".to_string(),
+            connections: 2,
+            is_active: true,
+            last_message: "hihi".to_string(),
+        },
+        NodeInfo {
+            id: "09850249352asdjapsdikalskasdkas".to_string(),
+            connections: 10,
+            is_active: true,
+            last_message: "wtf".to_string(),
+        },
+    ];
+
+    let info_list = InfoList::new(infos.clone());
+
+    let ids = vec![
+        infos[0].id.clone(),
+        infos[1].id.clone(),
+        infos[2].id.clone(),
+        infos[3].id.clone(),
+        infos[4].id.clone(),
+    ];
+
+    let id_list = IdList::new(ids);
+
+    let model = Arc::new(Model::new(id_list, info_list));
+    //let model = Model::new(id_list, info_list);
 
     let nthreads = num_cpus::get();
     let (signal, shutdown) = async_channel::unbounded::<()>();
@@ -91,8 +138,8 @@ async fn main() -> Result<()> {
         // Run the main future on the current thread.
         .finish(|| {
             smol::future::block_on(async move {
-                start(ex2.clone(), app.clone()).await?;
-                run_app(&mut terminal, app).await?;
+                run_rpc(ex2.clone(), model.clone()).await?;
+                render(&mut terminal, model.clone()).await?;
                 drop(signal);
                 Ok::<(), darkfi::Error>(())
             })
@@ -101,98 +148,86 @@ async fn main() -> Result<()> {
     result
 }
 
-async fn start(ex: Arc<Executor<'_>>, app: App) -> Result<()> {
+async fn run_rpc(ex: Arc<Executor<'_>>, model: Arc<Model>) -> Result<()> {
     let client = Map::new("tcp://127.0.0.1:8000".to_string());
 
-    ex.spawn(async {
-        let _ = poll(client, app).await;
-    })
-    .detach();
+    ex.spawn(poll(client, model)).detach();
 
     Ok(())
 }
 
-async fn poll(client: Map, app: App) -> Result<()> {
+async fn poll(client: Map, _model: Arc<Model>) -> Result<()> {
     loop {
         let reply = client.get_info().await?;
-        update(app.clone(), reply).await?;
-        async_util::sleep(1).await;
-    }
-}
 
-async fn update(app: App, reply: Value) -> Result<()> {
-    if reply.as_object().is_some() && !reply.as_object().unwrap().is_empty() {
-        //let args = params.as_array();
-        let nodes = reply.as_object().unwrap().get("nodes").unwrap();
+        if reply.as_object().is_some() && !reply.as_object().unwrap().is_empty() {
+            let nodes = reply.as_object().unwrap().get("nodes").unwrap();
 
-        let node1 = &nodes[0];
-        let node2 = &nodes[1];
-        let _node3 = &nodes[2];
+            let node1 = &nodes[0];
+            let node2 = &nodes[1];
+            let node3 = &nodes[2];
 
-        let infos = vec![
-            NodeInfo {
+            let _infos = vec![NodeInfo {
                 id: node1["id"].to_string(),
                 connections: node1["connections"].as_u64().unwrap() as usize,
                 is_active: node2["is_active"].as_bool().unwrap(),
-                last_message: "message".to_string(),
-            },
-            //NodeInfo {
-            //    id: node2["id"].to_string(),
-            //    connections: node2["connections"].as_u64().unwrap() as usize,
-            //    is_active: node2["is_active"].as_bool().unwrap(),
-            //    last_message: node2["message"].to_string(),
-            //},
-            //NodeInfo {
-            //    id: node3["id"].to_string(),
-            //    connections: node3["connections"].as_u64().unwrap() as usize,
-            //    is_active: node3["is_active"].as_bool().unwrap(),
-            //    last_message: node3["message"].to_string(),
-            //},
-        ];
-
-        //app.node_info(
-        //let node_info = NodeInfoView::new(infos.clone());
-
-        //let ids = vec![node1["id"].to_string(), node2["id"].to_string(), node3["id"].to_string()];
-
-        // mutex
-        app.update(infos).await;
-        //let node_list = NodeIdList::new(ids);
-        //println!("{}", test);
-        // do something
-    } else {
-        // TODO: error handling
-        println!("Reply is an error");
-    }
+                last_message: node3["message"].to_string(),
+            }];
 
-    Ok(())
+            //model.lock().await.update(infos).await;
+        } else {
+            // TODO: error handling
+            println!("Reply is an error");
+        }
+
+        async_util::sleep(1).await;
+    }
 }
 
-async fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
+async fn render<B: Backend>(terminal: &mut Terminal<B>, model: Arc<Model>) -> io::Result<()> {
     let mut asi = async_stdin();
 
     terminal.clear()?;
 
-    app.node_list.state.select(Some(0));
+    let mut info_vec = Vec::new();
 
-    app.node_info.index = 0;
+    for info in model.info_list.infos.lock().await.clone() {
+        info_vec.push(info)
+    }
 
-    loop {
-        terminal.draw(|f| ui::ui(f, &mut app))?;
+    let mut id_vec = Vec::new();
+
+    for id in model.id_list.node_id.lock().await.clone() {
+        id_vec.push(id)
+    }
+
+    let id_list = IdListView::new(id_vec);
 
+    let info_list = InfoListView::new(info_vec);
+
+    let mut view = View::new(id_list, info_list);
+
+    view.id_list.state.select(Some(0));
+
+    view.info_list.index = 0;
+
+    loop {
+        terminal.draw(|f| {
+            ui::ui(f, view.clone());
+        })?;
         for k in asi.by_ref().keys() {
             match k.unwrap() {
                 Key::Char('q') => {
                     terminal.clear()?;
-                    return Ok(())
+                    return Ok(());
                 }
                 Key::Char('j') => {
-                    app.node_list.next();
-                    app.node_info.next();
+                    view.id_list.next();
+                    view.info_list.next().await;
                 }
                 Key::Char('k') => {
-                    app.node_list.previous();
-                    app.node_info.previous();
+                    view.id_list.previous();
+                    view.info_list.previous().await;
                 }
                 _ => (),
             }

+ 81 - 0
bin/map/src/model.rs

@@ -0,0 +1,81 @@
+use async_std::sync::Mutex;
+use tui::widgets::ListState;
+
+pub struct Model {
+    pub id_list: IdList,
+    pub info_list: InfoList,
+}
+
+impl Model {
+    pub fn new(id_list: IdList, info_list: InfoList) -> Model {
+        Model { id_list, info_list }
+    }
+
+    pub async fn update(self, node_vec: Vec<NodeInfo>) -> Model {
+        let ids = vec![node_vec[0].id.clone()];
+
+        for id in ids {
+            self.id_list.node_id.lock().await.push(id);
+        }
+
+        let id_list = self.id_list;
+
+        for info in node_vec {
+            self.info_list.infos.lock().await.push(info);
+        }
+        let info_list = self.info_list;
+
+        Model { id_list, info_list }
+    }
+}
+
+pub struct IdList {
+    pub state: Mutex<ListState>,
+    pub node_id: Mutex<Vec<String>>,
+}
+
+impl IdList {
+    pub fn new(node_id: Vec<String>) -> IdList {
+        let node_id = Mutex::new(node_id);
+        IdList { state: Mutex::new(ListState::default()), node_id }
+    }
+}
+
+pub struct InfoList {
+    pub index: Mutex<usize>,
+    pub infos: Mutex<Vec<NodeInfo>>,
+}
+
+impl InfoList {
+    pub fn new(infos: Vec<NodeInfo>) -> InfoList {
+        let index = 0;
+        let index = Mutex::new(index);
+        let infos = Mutex::new(infos);
+
+        InfoList { index, infos }
+    }
+}
+
+pub type NodeId = u32;
+
+#[derive(Clone)]
+pub struct NodeInfo {
+    pub id: String,
+    pub connections: usize,
+    pub is_active: bool,
+    pub last_message: String,
+}
+
+impl NodeInfo {
+    pub fn new() -> NodeInfo {
+        let connections = 0;
+        let is_active = false;
+        NodeInfo { id: String::new(), connections, is_active, last_message: String::new() }
+    }
+}
+
+impl Default for NodeInfo {
+    fn default() -> Self {
+        Self::new()
+    }
+}

+ 0 - 51
bin/map/src/node_info.rs

@@ -1,51 +0,0 @@
-#[derive(Clone)]
-pub struct NodeInfoView {
-    pub index: usize,
-    pub infos: Vec<NodeInfo>,
-}
-
-impl NodeInfoView {
-    pub fn new(infos: Vec<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;
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct NodeInfo {
-    pub id: String,
-    pub connections: usize,
-    pub is_active: bool,
-    pub last_message: String,
-}
-
-impl NodeInfo {
-    pub fn new() -> NodeInfo {
-        let connections = 0;
-        let is_active = false;
-        NodeInfo { id: String::new(), connections, is_active, last_message: String::new() }
-    }
-}
-
-impl Default for NodeInfo {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-//pub async fn add_seen(&self, id: u32) {
-//    self.privmsg_ids.lock().await.insert(id);
-//}

+ 0 - 56
bin/map/src/notes/list.rs

@@ -1,56 +0,0 @@
-use crate::{
-    info::{NodeId, NodeInfo},
-    ui::render_selected,
-};
-use std::collections::HashMap;
-use tui::widgets::ListState;
-
-// TODO: make this just a list
-// hashmaps are owned by App
-#[derive(Clone)]
-pub struct StatefulList {
-    pub state: ListState,
-    pub nodes: NodeId,
-    //pub nodes: HashMap<NodeId, NodeInfo>,
-    //pub node_info: NodeInfo,
-    //pub index: HashMap<usize, NodeInfo>,
-    //pub node_info: InfoScreen,
-}
-
-impl StatefulList {
-    pub fn new(nodes: NodeId) -> StatefulList {
-        StatefulList { state: ListState::default(), nodes }
-    }
-
-    pub fn next(&mut self) {
-        let i = match self.state.selected() {
-            Some(i) => {
-                if i >= self.nodes.id.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.nodes.id.len() - 1
-                } else {
-                    i - 1
-                }
-            }
-            None => 0,
-        };
-        self.state.select(Some(i));
-    }
-
-    pub fn unselect(&mut self) {
-        self.state.select(None);
-    }
-}

+ 0 - 2
bin/map/src/types.rs

@@ -1,2 +0,0 @@
-//pub type NodeId = String;
-//pub type NodeInfo = String;

+ 13 - 12
bin/map/src/ui.rs

@@ -1,4 +1,4 @@
-use crate::app::App;
+use crate::view::View;
 use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
@@ -8,15 +8,15 @@ use tui::{
     Frame,
 };
 
-pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
+pub fn ui<B: Backend>(f: &mut Frame<'_, B>, mut view: View) {
     let slice = Layout::default()
         .direction(Direction::Horizontal)
         .margin(2)
         .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
         .split(f.size());
 
-    let nodes: Vec<ListItem> = app
-        .node_list
+    let nodes: Vec<ListItem> = view
+        .id_list
         .node_id
         .iter()
         .map(|id| {
@@ -29,18 +29,19 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
         .block(Block::default().borders(Borders::ALL))
         .highlight_style(Style::default().fg(Color::LightCyan).add_modifier(Modifier::BOLD));
 
-    f.render_stateful_widget(nodes, slice[0], &mut app.node_list.state);
+    f.render_stateful_widget(nodes, slice[0], &mut view.id_list.state);
 
-    let index = app.node_info.index;
+    let index = view.info_list.index;
 
-    render_info(app, f, index, slice);
+    render_info(view, f, index, slice);
 }
 
-fn render_info<B: Backend>(app: &mut App, f: &mut Frame<B>, index: usize, slice: Vec<Rect>) {
-    let id = &app.node_info.infos[index].id;
-    let connections = app.node_info.infos[index].connections;
-    let is_active = app.node_info.infos[index].is_active;
-    let message = &app.node_info.infos[index].last_message;
+fn render_info<B: Backend>(view: View, f: &mut Frame<'_, B>, index: usize, slice: Vec<Rect>) {
+    let info = &view.info_list.infos;
+    let id = &info[index].id;
+    let connections = info[index].connections;
+    let is_active = info[index].is_active;
+    let message = &info[index].last_message;
     let span = vec![
         Spans::from(format!("NodeId: {}", id)),
         Spans::from(format!("Number of connections: {}", connections)),

+ 1 - 1
book/src/tutorial.md

@@ -107,7 +107,7 @@ tutorial](https://bitzuma.com/posts/a-beginners-guide-to-the-electrum-bitcoin-wa
 for more details.
 
 For Solana, you can either install the Solana command-line suite or use
-[sollet](sollet.io).
+[sollet](https://www.sollet.io).
 
 Follow [this tutorial](https://docs.solana.com/cli) for the Solana
 command-line. For sollet.io, switch the network to testnet and click

+ 10 - 4
proof/burn.rs

@@ -15,16 +15,22 @@ use halo2_gadgets::primitives::{
     poseidon::{ConstantLength, P128Pow5T3},
 };
 use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Tree};
+use log::info;
 use pasta_curves::{
     arithmetic::{CurveAffine, Field},
     group::Curve,
     pallas,
 };
 use rand::rngs::OsRng;
-use simplelog::{ColorChoice::Auto, Config, LevelFilter::Debug, TermLogger, TerminalMode::Mixed};
+use simplelog::{ColorChoice::Auto, Config, LevelFilter, TermLogger, TerminalMode::Mixed};
 
 fn main() -> Result<()> {
-    TermLogger::init(Debug, Config::default(), Mixed, Auto)?;
+    let loglevel = match option_env!("RUST_LOG") {
+        Some("debug") => LevelFilter::Debug,
+        Some("trace") => LevelFilter::Trace,
+        Some(_) | None => LevelFilter::Info,
+    };
+    TermLogger::init(loglevel, Config::default(), Mixed, Auto)?;
 
     /* ANCHOR: main */
     let bincode = include_bytes!("burn.zk.bin");
@@ -114,7 +120,7 @@ fn main() -> Result<()> {
     // Create the circuit
     let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
 
-    // Build the proving key and create the zero-knowledge proof
+    info!(target: "PROVER", "Building proving key and creating the zero-knowledge proof");
     let proving_key = ProvingKey::build(11, &circuit);
     let proof = Proof::create(&proving_key, &[circuit], &public_inputs)?;
 
@@ -139,7 +145,7 @@ fn main() -> Result<()> {
     // Create the circuit
     let circuit = ZkCircuit::new(verifier_witnesses, zkbin);
 
-    // Build the verifying key and verify the zero-knowledge proof
+    info!(target: "VERIFIER", "Building verifying key and verifying the zero-knowledge proof");
     let verifying_key = VerifyingKey::build(11, &circuit);
     proof.verify(&verifying_key, &public_inputs)?;
     /* ANCHOR_END: main */

+ 10 - 4
proof/mint.rs

@@ -13,16 +13,22 @@ use halo2_gadgets::primitives::{
     poseidon,
     poseidon::{ConstantLength, P128Pow5T3},
 };
+use log::info;
 use pasta_curves::{
     arithmetic::{CurveAffine, Field},
     group::Curve,
     pallas,
 };
 use rand::rngs::OsRng;
-use simplelog::{ColorChoice::Auto, Config, LevelFilter::Debug, TermLogger, TerminalMode::Mixed};
+use simplelog::{ColorChoice::Auto, Config, LevelFilter, TermLogger, TerminalMode::Mixed};
 
 fn main() -> Result<()> {
-    TermLogger::init(Debug, Config::default(), Mixed, Auto)?;
+    let loglevel = match option_env!("RUST_LOG") {
+        Some("debug") => LevelFilter::Debug,
+        Some("trace") => LevelFilter::Trace,
+        Some(_) | None => LevelFilter::Info,
+    };
+    TermLogger::init(loglevel, Config::default(), Mixed, Auto)?;
 
     /* ANCHOR: main */
     let bincode = include_bytes!("mint.zk.bin");
@@ -69,7 +75,7 @@ fn main() -> Result<()> {
     // Create the circuit
     let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
 
-    // Build the proving key and create the zero-knowledge proof
+    info!(target: "PROVER", "Building proving key and creating the zero-knowledge proof");
     let proving_key = ProvingKey::build(11, &circuit);
     let proof = Proof::create(&proving_key, &[circuit], &public_inputs)?;
 
@@ -92,7 +98,7 @@ fn main() -> Result<()> {
     // Create the circuit
     let circuit = ZkCircuit::new(verifier_witnesses, zkbin);
 
-    // Build the verifying key and verify the zero-knowledge proof
+    info!(target: "VERIFIER", "Building verifying key and verifying the zero-knowledge proof");
     let verifying_key = VerifyingKey::build(11, &circuit);
     proof.verify(&verifying_key, &public_inputs)?;
     /* ANCHOR_END: main */

+ 14 - 13
src/net/channel.rs

@@ -16,8 +16,9 @@ use std::sync::{
 use crate::{
     error::{Error, Result},
     net::{
+        message,
         message_subscriber::{MessageSubscription, MessageSubsystem},
-        messages,
+        protocol::{ProtocolBase, ProtocolBasePtr},
     },
     system::{StoppableTask, StoppableTaskPtr, Subscriber, SubscriberPtr, Subscription},
 };
@@ -107,7 +108,7 @@ impl Channel {
     /// Sends a message across a channel. Calls function 'send_message' that
     /// creates a new payload and sends it over the TCP connection as a
     /// packet. Returns an error if something goes wrong.
-    pub async fn send<M: messages::Message>(&self, message: M) -> Result<()> {
+    pub async fn send<M: message::Message>(&self, message: M) -> Result<()> {
         debug!(target: "net",
             "Channel::send() [START, command={:?}, address={}]",
             M::name(),
@@ -138,17 +139,17 @@ impl Channel {
     /// it. Then creates a message packet- the base type of the network- and
     /// copies the payload into it. Then we send the packet over the TCP
     /// stream.
-    async fn send_message<M: messages::Message>(&self, message: M) -> Result<()> {
+    async fn send_message<M: message::Message>(&self, message: M) -> Result<()> {
         let mut payload = Vec::new();
         message.encode(&mut payload)?;
-        let packet = messages::Packet { command: String::from(M::name()), payload };
+        let packet = message::Packet { command: String::from(M::name()), payload };
 
         let stream = &mut *self.writer.lock().await;
-        messages::send_packet(stream, packet).await
+        message::send_packet(stream, packet).await
     }
 
     /// Subscribe to a messages on the message subsystem.
-    pub async fn subscribe_msg<M: messages::Message>(&self) -> Result<MessageSubscription<M>> {
+    pub async fn subscribe_msg<M: message::Message>(&self) -> Result<MessageSubscription<M>> {
         debug!(target: "net",
             "Channel::subscribe_msg() [START, command={:?}, address={}]",
             M::name(),
@@ -178,12 +179,12 @@ impl Channel {
 
     /// Perform network handshake for message subsystem dispatchers.
     async fn setup_dispatchers(message_subsystem: &MessageSubsystem) {
-        message_subsystem.add_dispatch::<messages::VersionMessage>().await;
-        message_subsystem.add_dispatch::<messages::VerackMessage>().await;
-        message_subsystem.add_dispatch::<messages::PingMessage>().await;
-        message_subsystem.add_dispatch::<messages::PongMessage>().await;
-        message_subsystem.add_dispatch::<messages::GetAddrsMessage>().await;
-        message_subsystem.add_dispatch::<messages::AddrsMessage>().await;
+        message_subsystem.add_dispatch::<message::VersionMessage>().await;
+        message_subsystem.add_dispatch::<message::VerackMessage>().await;
+        message_subsystem.add_dispatch::<message::PingMessage>().await;
+        message_subsystem.add_dispatch::<message::PongMessage>().await;
+        message_subsystem.add_dispatch::<message::GetAddrsMessage>().await;
+        message_subsystem.add_dispatch::<message::AddrsMessage>().await;
     }
 
     /// Convenience function that returns the Message Subsystem.
@@ -202,7 +203,7 @@ impl Channel {
         let reader = &mut *self.reader.lock().await;
 
         loop {
-            let packet = match messages::read_packet(reader).await {
+            let packet = match message::read_packet(reader).await {
                 Ok(packet) => packet,
                 Err(err) => {
                     if Self::is_eof_error(err.clone()) {

+ 2 - 2
src/net/messages.rs → src/net/message.rs

@@ -1,5 +1,5 @@
-use futures::prelude::*;
-use log::*;
+use futures::{AsyncRead, AsyncWrite, AsyncReadExt, AsyncWriteExt};
+use log::debug;
 use std::{io, net::SocketAddr};
 
 use crate::{

+ 4 - 2
src/net/message_subscriber.rs

@@ -1,11 +1,11 @@
 use async_std::sync::Mutex;
 use async_trait::async_trait;
-use log::*;
+use log::{debug, error, warn};
 use rand::Rng;
 use std::{any::Any, collections::HashMap, io, io::Cursor, sync::Arc};
 
 use crate::{
-    net::messages::Message,
+    net::message::Message,
     util::serial::{Decodable, Encodable},
     Error, Result,
 };
@@ -87,6 +87,7 @@ impl<M: Message> MessageDispatcher<M> {
     /// channels.
     async fn trigger_all(&self, message: MessageResult<M>) {
         debug!(
+            target: "net",
             "MessageDispatcher<M={}>::trigger_all({}) [START, subs={}]",
             M::name(),
             if message.is_ok() { "msg" } else { "err" },
@@ -109,6 +110,7 @@ impl<M: Message> MessageDispatcher<M> {
         self.collect_garbage(garbage_ids).await;
 
         debug!(
+            target: "net",
             "MessageDispatcher<M={}>::trigger_all({}) [END, subs={}]",
             M::name(),
             if message.is_ok() { "msg" } else { "err" },

+ 6 - 5
src/net/mod.rs

@@ -46,7 +46,7 @@ pub mod message_subscriber;
 ///
 /// Implements a type called Packet which is the base message type. Packets are
 /// converted into messages and passed to an event loop.
-pub mod messages;
+pub mod message;
 
 /// P2P provides all core functionality to interact with the peer-to-peer
 /// network.
@@ -72,7 +72,7 @@ pub mod p2p;
 ///
 /// Protocol submodule also implements a jobs manager than handles the
 /// asynchronous execution of the protocols.
-pub mod protocols;
+pub mod protocol;
 
 /// Defines the interaction between nodes during a connection. Consists of an
 /// inbound session, which describes how to set up an incoming connection, and
@@ -80,7 +80,7 @@ pub mod protocols;
 /// describes the seed session, which is the type of connection used when a node
 /// connects to the network for the first time. Implements the session trait
 /// which describes the common functions across all sessions.
-pub mod sessions;
+pub mod session;
 
 /// Network configuration settings.
 pub mod settings;
@@ -89,8 +89,9 @@ pub use acceptor::{Acceptor, AcceptorPtr};
 pub use channel::{Channel, ChannelPtr};
 pub use connector::Connector;
 pub use hosts::{Hosts, HostsPtr};
+pub use message::Message;
 pub use message_subscriber::MessageSubscription;
-pub use messages::Message;
 pub use p2p::{P2p, P2pPtr};
-pub use protocols::{ProtocolJobsManager, ProtocolJobsManagerPtr};
+pub use protocol::{ProtocolJobsManager, ProtocolJobsManagerPtr, ProtocolBasePtr, ProtocolBase};
+pub use session::{SESSION_ALL, SESSION_INBOUND, SESSION_MANUAL, SESSION_OUTBOUND, SESSION_SEED};
 pub use settings::{Settings, SettingsPtr};

+ 18 - 6
src/net/p2p.rs

@@ -1,6 +1,6 @@
 use async_executor::Executor;
 use async_std::sync::Mutex;
-use log::*;
+use log::debug;
 use std::{
     collections::{HashMap, HashSet},
     net::SocketAddr,
@@ -10,8 +10,9 @@ use std::{
 use crate::{
     error::{Error, Result},
     net::{
-        messages::Message,
-        sessions::{InboundSession, ManualSession, OutboundSession, SeedSession},
+        message::Message,
+        protocol::{register_default_protocols, ProtocolRegistry},
+        session::{InboundSession, ManualSession, OutboundSession, SeedSession},
         Channel, ChannelPtr, Hosts, HostsPtr, Settings, SettingsPtr,
     },
     system::{Subscriber, SubscriberPtr, Subscription},
@@ -32,21 +33,28 @@ pub struct P2p {
     // Used both internally and externally
     stop_subscriber: SubscriberPtr<Error>,
     hosts: HostsPtr,
+    protocol_registry: ProtocolRegistry,
     settings: SettingsPtr,
 }
 
 impl P2p {
     /// Create a new p2p network.
-    pub fn new(settings: Settings) -> Arc<Self> {
+    pub async fn new(settings: Settings) -> Arc<Self> {
         let settings = Arc::new(settings);
-        Arc::new(Self {
+
+        let self_ = Arc::new(Self {
             pending: Mutex::new(HashSet::new()),
             channels: Mutex::new(HashMap::new()),
             channel_subscriber: Subscriber::new(),
             stop_subscriber: Subscriber::new(),
             hosts: Hosts::new(),
+            protocol_registry: ProtocolRegistry::new(),
             settings,
-        })
+        });
+
+        register_default_protocols(self_.clone()).await;
+
+        self_
     }
 
     /// Invoke startup and seeding sequence. Call from constructing thread.
@@ -140,6 +148,10 @@ impl P2p {
         self.hosts.clone()
     }
 
+    pub fn protocol_registry(&self) -> &ProtocolRegistry {
+        &self.protocol_registry
+    }
+
     /// Subscribe to a channel.
     pub async fn subscribe_channel(&self) -> Subscription<Result<ChannelPtr>> {
         self.channel_subscriber.clone().subscribe().await

+ 18 - 0
src/net/protocols/mod.rs → src/net/protocol/mod.rs

@@ -46,8 +46,26 @@ pub mod protocol_seed;
 /// other node and sending the version acknowledgement.
 pub mod protocol_version;
 
+pub mod protocol_base;
+pub mod protocol_registry;
+
 pub use protocol_address::ProtocolAddress;
 pub use protocol_jobs_manager::{ProtocolJobsManager, ProtocolJobsManagerPtr};
 pub use protocol_ping::ProtocolPing;
 pub use protocol_seed::ProtocolSeed;
 pub use protocol_version::ProtocolVersion;
+
+pub use protocol_base::{ProtocolBase, ProtocolBasePtr};
+pub use protocol_registry::ProtocolRegistry;
+
+use crate::net::{
+    session::{SESSION_ALL, SESSION_SEED},
+    P2pPtr,
+};
+
+pub async fn register_default_protocols(p2p: P2pPtr) {
+    let registry = p2p.protocol_registry();
+    registry.register(SESSION_ALL, ProtocolPing::new).await;
+    registry.register(!SESSION_SEED, ProtocolAddress::new).await;
+    registry.register(SESSION_SEED, ProtocolSeed::new).await;
+}

+ 36 - 25
src/net/protocols/protocol_address.rs → src/net/protocol/protocol_address.rs

@@ -1,22 +1,23 @@
-use log::*;
+use async_trait::async_trait;
+use log::{debug, error};
 use smol::Executor;
 use std::sync::Arc;
 
 use crate::{
     error::Result,
     net::{
+        message,
         message_subscriber::MessageSubscription,
-        messages,
-        protocols::{ProtocolJobsManager, ProtocolJobsManagerPtr},
-        ChannelPtr, HostsPtr,
+        protocol::{ProtocolBase, ProtocolBasePtr, ProtocolJobsManager, ProtocolJobsManagerPtr},
+        ChannelPtr, HostsPtr, P2pPtr,
     },
 };
 
 /// Defines address and get-address messages.
 pub struct ProtocolAddress {
     channel: ChannelPtr,
-    addrs_sub: MessageSubscription<messages::AddrsMessage>,
-    get_addrs_sub: MessageSubscription<messages::GetAddrsMessage>,
+    addrs_sub: MessageSubscription<message::AddrsMessage>,
+    get_addrs_sub: MessageSubscription<message::GetAddrsMessage>,
     hosts: HostsPtr,
     jobsman: ProtocolJobsManagerPtr,
 }
@@ -24,18 +25,20 @@ pub struct ProtocolAddress {
 impl ProtocolAddress {
     /// Create a new address protocol. Makes an address and get-address
     /// subscription and adds them to the address protocol instance.
-    pub async fn new(channel: ChannelPtr, hosts: HostsPtr) -> Arc<Self> {
+    pub async fn new(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr {
+        let hosts = p2p.hosts();
+
         // Creates a subscription to address message.
         let addrs_sub = channel
             .clone()
-            .subscribe_msg::<messages::AddrsMessage>()
+            .subscribe_msg::<message::AddrsMessage>()
             .await
             .expect("Missing addrs dispatcher!");
 
         // Creates a subscription to get-address message.
         let get_addrs_sub = channel
             .clone()
-            .subscribe_msg::<messages::GetAddrsMessage>()
+            .subscribe_msg::<message::GetAddrsMessage>()
             .await
             .expect("Missing getaddrs dispatcher!");
 
@@ -48,21 +51,6 @@ impl ProtocolAddress {
         })
     }
 
-    /// Starts the address protocol. Runs receive address and get address
-    /// protocols on the protocol task manager. Then sends get-address
-    /// message.
-    pub async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) {
-        debug!(target: "net", "ProtocolAddress::start() [START]");
-        self.jobsman.clone().start(executor.clone());
-        self.jobsman.clone().spawn(self.clone().handle_receive_addrs(), executor.clone()).await;
-        self.jobsman.clone().spawn(self.clone().handle_receive_get_addrs(), executor).await;
-
-        // Send get_address message.
-        let get_addrs = messages::GetAddrsMessage {};
-        let _ = self.channel.clone().send(get_addrs).await;
-        debug!(target: "net", "ProtocolAddress::start() [END]");
-    }
-
     /// Handles receiving the address message. Loops to continually recieve
     /// address messages on the address subsciption. Adds the recieved
     /// addresses to the list of hosts.
@@ -101,9 +89,32 @@ impl ProtocolAddress {
                 addrs.len()
             );
             // Creates an address messages containing host address.
-            let addrs_msg = messages::AddrsMessage { addrs };
+            let addrs_msg = message::AddrsMessage { addrs };
             // Sends the address message across the channel.
             self.channel.clone().send(addrs_msg).await?;
         }
     }
 }
+
+#[async_trait]
+impl ProtocolBase for ProtocolAddress {
+    /// Starts the address protocol. Runs receive address and get address
+    /// protocols on the protocol task manager. Then sends get-address
+    /// message.
+    async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
+        debug!(target: "net", "ProtocolAddress::start() [START]");
+        self.jobsman.clone().start(executor.clone());
+        self.jobsman.clone().spawn(self.clone().handle_receive_addrs(), executor.clone()).await;
+        self.jobsman.clone().spawn(self.clone().handle_receive_get_addrs(), executor).await;
+
+        // Send get_address message.
+        let get_addrs = message::GetAddrsMessage {};
+        let _ = self.channel.clone().send(get_addrs).await;
+        debug!(target: "net", "ProtocolAddress::start() [END]");
+        Ok(())
+    }
+
+    fn name(&self) -> &'static str {
+        "ProtocolAddress"
+    }
+}

+ 14 - 0
src/net/protocol/protocol_base.rs

@@ -0,0 +1,14 @@
+use async_trait::async_trait;
+use smol::Executor;
+use std::sync::Arc;
+
+use crate::error::Result;
+
+pub type ProtocolBasePtr = Arc<dyn ProtocolBase + Send + Sync>;
+
+#[async_trait]
+pub trait ProtocolBase {
+    async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()>;
+
+    fn name(&self) -> &'static str;
+}

+ 0 - 0
src/net/protocols/protocol_jobs_manager.rs → src/net/protocol/protocol_jobs_manager.rs


+ 50 - 36
src/net/protocols/protocol_ping.rs → src/net/protocol/protocol_ping.rs

@@ -1,4 +1,5 @@
-use log::*;
+use async_trait::async_trait;
+use log::{debug, error};
 use rand::Rng;
 use smol::Executor;
 use std::{sync::Arc, time::Instant};
@@ -6,9 +7,10 @@ use std::{sync::Arc, time::Instant};
 use crate::{
     error::{Error, Result},
     net::{
-        messages,
-        protocols::{ProtocolJobsManager, ProtocolJobsManagerPtr},
-        ChannelPtr, SettingsPtr,
+        message,
+        message_subscriber::MessageSubscription,
+        protocol::{ProtocolBase, ProtocolBasePtr, ProtocolJobsManager, ProtocolJobsManagerPtr},
+        ChannelPtr, P2pPtr, SettingsPtr,
     },
     util::sleep,
 };
@@ -16,45 +18,46 @@ use crate::{
 /// Defines ping and pong messages.
 pub struct ProtocolPing {
     channel: ChannelPtr,
+    ping_sub: MessageSubscription<message::PingMessage>,
+    pong_sub: MessageSubscription<message::PongMessage>,
     settings: SettingsPtr,
     jobsman: ProtocolJobsManagerPtr,
 }
 
 impl ProtocolPing {
     /// Create a new ping-pong protocol.
-    pub fn new(channel: ChannelPtr, settings: SettingsPtr) -> Arc<Self> {
+    pub async fn new(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr {
+        let settings = p2p.settings();
+
+        // Creates a subscription to ping message.
+        let ping_sub = channel
+            .clone()
+            .subscribe_msg::<message::PingMessage>()
+            .await
+            .expect("Missing ping dispatcher!");
+
+        // Creates a subscription to pong message.
+        let pong_sub = channel
+            .clone()
+            .subscribe_msg::<message::PongMessage>()
+            .await
+            .expect("Missing pong dispatcher!");
+
         Arc::new(Self {
             channel: channel.clone(),
+            ping_sub,
+            pong_sub,
             settings,
             jobsman: ProtocolJobsManager::new("ProtocolPing", channel),
         })
     }
 
-    /// Starts ping-pong keep-alive messages exchange. Runs ping-pong in the
-    /// protocol task manager, then queues the reply. Sends out a ping and
-    /// waits for pong reply. Waits for ping and replies with a pong.
-    pub async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) {
-        debug!(target: "net", "ProtocolPing::start() [START]");
-        self.jobsman.clone().start(executor.clone());
-        self.jobsman.clone().spawn(self.clone().run_ping_pong(), executor.clone()).await;
-        self.jobsman.clone().spawn(self.reply_to_ping(), executor).await;
-        debug!(target: "net", "ProtocolPing::start() [END]");
-    }
-
     /// Runs ping-pong protocol. Creates a subscription to pong, then starts a
     /// loop. Loop sleeps for the duration of the channel heartbeat, then
     /// sends a ping message with a random nonce. Loop starts a timer, waits
     /// for the pong reply and insures the nonce is the same.
     async fn run_ping_pong(self: Arc<Self>) -> Result<()> {
         debug!(target: "net", "ProtocolPing::run_ping_pong() [START]");
-        // Creates a subscription to pong message.
-        let pong_sub = self
-            .channel
-            .clone()
-            .subscribe_msg::<messages::PongMessage>()
-            .await
-            .expect("Missing pong dispatcher!");
-
         loop {
             // Wait channel_heartbeat amount of time.
             sleep(self.settings.channel_heartbeat_seconds).await;
@@ -63,14 +66,14 @@ impl ProtocolPing {
             let nonce = Self::random_nonce();
 
             // Send ping message.
-            let ping = messages::PingMessage { nonce };
+            let ping = message::PingMessage { nonce };
             self.channel.clone().send(ping).await?;
             debug!(target: "net", "ProtocolPing::run_ping_pong() send Ping message");
             // Start the timer for ping timer.
             let start = Instant::now();
 
             // Wait for pong, check nonce matches.
-            let pong_msg = pong_sub.receive().await?;
+            let pong_msg = self.pong_sub.receive().await?;
             if pong_msg.nonce != nonce {
                 error!("Wrong nonce for ping reply. Disconnecting from channel.");
                 self.channel.stop().await;
@@ -85,21 +88,13 @@ impl ProtocolPing {
     /// pong reply.
     async fn reply_to_ping(self: Arc<Self>) -> Result<()> {
         debug!(target: "net", "ProtocolPing::reply_to_ping() [START]");
-        // Creates a subscription to ping message.
-        let ping_sub = self
-            .channel
-            .clone()
-            .subscribe_msg::<messages::PingMessage>()
-            .await
-            .expect("Missing ping dispatcher!");
-
         loop {
             // Wait for ping, reply with pong that has a matching nonce.
-            let ping = ping_sub.receive().await?;
+            let ping = self.ping_sub.receive().await?;
             debug!(target: "net", "ProtocolPing::reply_to_ping() received Ping message");
 
             // Send pong message.
-            let pong = messages::PongMessage { nonce: ping.nonce };
+            let pong = message::PongMessage { nonce: ping.nonce };
             self.channel.clone().send(pong).await?;
             debug!(target: "net", "ProtocolPing::reply_to_ping() sent Pong reply");
         }
@@ -110,3 +105,22 @@ impl ProtocolPing {
         rng.gen()
     }
 }
+
+#[async_trait]
+impl ProtocolBase for ProtocolPing {
+    /// Starts ping-pong keep-alive messages exchange. Runs ping-pong in the
+    /// protocol task manager, then queues the reply. Sends out a ping and
+    /// waits for pong reply. Waits for ping and replies with a pong.
+    async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
+        debug!(target: "net", "ProtocolPing::start() [START]");
+        self.jobsman.clone().start(executor.clone());
+        self.jobsman.clone().spawn(self.clone().run_ping_pong(), executor.clone()).await;
+        self.jobsman.clone().spawn(self.reply_to_ping(), executor).await;
+        debug!(target: "net", "ProtocolPing::start() [END]");
+        Ok(())
+    }
+
+    fn name(&self) -> &'static str {
+        "ProtocolPing"
+    }
+}

+ 56 - 0
src/net/protocol/protocol_registry.rs

@@ -0,0 +1,56 @@
+use async_std::sync::Mutex;
+use futures::future::BoxFuture;
+use std::future::Future;
+use log::debug;
+
+use crate::net::{session::SessionBitflag, ChannelPtr, P2pPtr, protocol::ProtocolBasePtr};
+
+type Constructor = Box<
+    dyn Fn(ChannelPtr, P2pPtr) -> BoxFuture<'static, ProtocolBasePtr>
+        + Send
+        + Sync,
+>;
+
+pub struct ProtocolRegistry {
+    protocol_constructors: Mutex<Vec<(SessionBitflag, Constructor)>>,
+}
+
+impl ProtocolRegistry {
+    pub fn new() -> Self {
+        Self { protocol_constructors: Mutex::new(Vec::new()) }
+    }
+
+    // add_protocol()?
+    pub async fn register<C, F>(&self, session_flags: SessionBitflag, constructor: C)
+    where
+        C: 'static + Fn(ChannelPtr, P2pPtr) -> F + Send + Sync,
+        F: 'static + Future<Output = ProtocolBasePtr> + Send,
+    {
+        let constructor = move |channel, p2p| {
+            Box::pin(constructor(channel, p2p))
+                as BoxFuture<'static, ProtocolBasePtr>
+        };
+        self.protocol_constructors.lock().await.push((session_flags, Box::new(constructor)));
+    }
+
+    pub async fn attach(
+        &self,
+        selector_id: SessionBitflag,
+        channel: ChannelPtr,
+        p2p: P2pPtr,
+    ) -> Vec<ProtocolBasePtr> {
+        let mut protocols: Vec<ProtocolBasePtr> = Vec::new();
+        for (session_flags, construct) in self.protocol_constructors.lock().await.iter() {
+            // Skip protocols that are not registered for this session
+            if selector_id & session_flags == 0 {
+                continue
+            }
+
+            let protocol: ProtocolBasePtr =
+                construct(channel.clone(), p2p.clone()).await;
+            debug!(target: "net", "Attached {}", protocol.name());
+            protocols.push(protocol)
+        }
+        protocols
+    }
+}

+ 34 - 19
src/net/protocols/protocol_seed.rs → src/net/protocol/protocol_seed.rs

@@ -1,10 +1,15 @@
-use log::*;
+use async_trait::async_trait;
+use log::debug;
 use smol::Executor;
 use std::sync::Arc;
 
 use crate::{
     error::Result,
-    net::{messages, ChannelPtr, HostsPtr, SettingsPtr},
+    net::{
+        message,
+        protocol::{ProtocolBase, ProtocolBasePtr},
+        ChannelPtr, HostsPtr, P2pPtr, SettingsPtr,
+    },
 };
 
 /// Implements the seed protocol.
@@ -16,20 +21,41 @@ pub struct ProtocolSeed {
 
 impl ProtocolSeed {
     /// Create a new seed protocol.
-    pub fn new(channel: ChannelPtr, hosts: HostsPtr, settings: SettingsPtr) -> Arc<Self> {
+    pub async fn new(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr {
+        let hosts = p2p.hosts();
+        let settings = p2p.settings();
+
         Arc::new(Self { channel, hosts, settings })
     }
 
+    /// Sends own external address over a channel. Imports own external address
+    /// from settings, then adds that address to an address message and
+    /// sends it out over the channel.
+    pub async fn send_self_address(&self) -> Result<()> {
+        match self.settings.external_addr {
+            Some(addr) => {
+                debug!(target: "net", "ProtocolSeed::send_own_address() addr={}", addr);
+                let addr = message::AddrsMessage { addrs: vec![addr] };
+                Ok(self.channel.clone().send(addr).await?)
+            }
+            // Do nothing if external address is not configured
+            None => Ok(()),
+        }
+    }
+}
+
+#[async_trait]
+impl ProtocolBase for ProtocolSeed {
     /// Starts the seed protocol. Creates a subscription to the address message,
     /// then sends our address to the seed server. Sends a get-address
     /// message and receives an address message.
-    pub async fn start(self: Arc<Self>, _executor: Arc<Executor<'_>>) -> Result<()> {
+    async fn start(self: Arc<Self>, _executor: Arc<Executor<'_>>) -> Result<()> {
         debug!(target: "net", "ProtocolSeed::start() [START]");
         // Create a subscription to address message.
         let addr_sub = self
             .channel
             .clone()
-            .subscribe_msg::<messages::AddrsMessage>()
+            .subscribe_msg::<message::AddrsMessage>()
             .await
             .expect("Missing addrs dispatcher!");
 
@@ -37,7 +63,7 @@ impl ProtocolSeed {
         self.send_self_address().await?;
 
         // Send get address message.
-        let get_addr = messages::GetAddrsMessage {};
+        let get_addr = message::GetAddrsMessage {};
         self.channel.clone().send(get_addr).await?;
 
         // Receive addresses.
@@ -49,18 +75,7 @@ impl ProtocolSeed {
         Ok(())
     }
 
-    /// Sends own external address over a channel. Imports own external address
-    /// from settings, then adds that address to an address message and
-    /// sends it out over the channel.
-    pub async fn send_self_address(&self) -> Result<()> {
-        match self.settings.external_addr {
-            Some(addr) => {
-                debug!(target: "net", "ProtocolSeed::send_own_address() addr={}", addr);
-                let addr = messages::AddrsMessage { addrs: vec![addr] };
-                Ok(self.channel.clone().send(addr).await?)
-            }
-            // Do nothing if external address is not configured
-            None => Ok(()),
-        }
+    fn name(&self) -> &'static str {
+        "ProtocolSeed"
     }
 }

+ 7 - 7
src/net/protocols/protocol_version.rs → src/net/protocol/protocol_version.rs

@@ -5,7 +5,7 @@ use std::sync::Arc;
 
 use crate::{
     error::{Error, Result},
-    net::{message_subscriber::MessageSubscription, messages, ChannelPtr, SettingsPtr},
+    net::{message, message_subscriber::MessageSubscription, ChannelPtr, SettingsPtr},
     util::sleep,
 };
 
@@ -13,8 +13,8 @@ use crate::{
 /// of a connection.
 pub struct ProtocolVersion {
     channel: ChannelPtr,
-    version_sub: MessageSubscription<messages::VersionMessage>,
-    verack_sub: MessageSubscription<messages::VerackMessage>,
+    version_sub: MessageSubscription<message::VersionMessage>,
+    verack_sub: MessageSubscription<message::VerackMessage>,
     settings: SettingsPtr,
 }
 
@@ -26,14 +26,14 @@ impl ProtocolVersion {
         // Creates a version subscription.
         let version_sub = channel
             .clone()
-            .subscribe_msg::<messages::VersionMessage>()
+            .subscribe_msg::<message::VersionMessage>()
             .await
             .expect("Missing version dispatcher!");
 
         // Creates a version acknowledgement subscription.
         let verack_sub = channel
             .clone()
-            .subscribe_msg::<messages::VerackMessage>()
+            .subscribe_msg::<message::VerackMessage>()
             .await
             .expect("Missing verack dispatcher!");
 
@@ -71,7 +71,7 @@ impl ProtocolVersion {
     /// Send version info and wait for version acknowledgement.
     async fn send_version(self: Arc<Self>) -> Result<()> {
         debug!(target: "net", "ProtocolVersion::send_version() [START]");
-        let version = messages::VersionMessage {};
+        let version = message::VersionMessage {};
         self.channel.clone().send(version).await?;
 
         // Wait for version acknowledgement
@@ -90,7 +90,7 @@ impl ProtocolVersion {
         // Check the message is OK
 
         // Send version acknowledgement
-        let verack = messages::VerackMessage {};
+        let verack = message::VerackMessage {};
         self.channel.clone().send(verack).await?;
 
         debug!(target: "net", "ProtocolVersion::recv_version() [END]");

+ 12 - 8
src/net/sessions/inbound_session.rs → src/net/session/inbound_session.rs

@@ -8,8 +8,8 @@ use std::{
 use crate::{
     error::{Error, Result},
     net::{
-        protocols::{ProtocolAddress, ProtocolPing},
-        sessions::Session,
+        protocol::{ProtocolAddress, ProtocolBase, ProtocolPing},
+        session::{Session, SessionBitflag, SESSION_INBOUND},
         Acceptor, AcceptorPtr, ChannelPtr, P2p,
     },
     system::{StoppableTask, StoppableTaskPtr},
@@ -96,30 +96,34 @@ impl InboundSession {
 
         self.clone().register_channel(channel.clone(), executor.clone()).await?;
 
-        self.attach_protocols(channel, executor).await
+        //self.attach_protocols(channel, executor).await
+        Ok(())
     }
 
-    /// Starts sending keep-alive and address messages across the channels.
-    async fn attach_protocols(
+    // Starts sending keep-alive and address messages across the channels.
+    /*async fn attach_protocols(
         self: Arc<Self>,
         channel: ChannelPtr,
         executor: Arc<Executor<'_>>,
     ) -> Result<()> {
-        let settings = self.p2p().settings().clone();
         let hosts = self.p2p().hosts();
 
-        let protocol_ping = ProtocolPing::new(channel.clone(), settings.clone());
+        let protocol_ping = ProtocolPing::new(channel.clone(), self.p2p());
         let protocol_addr = ProtocolAddress::new(channel, hosts).await;
 
         protocol_ping.start(executor.clone()).await;
         protocol_addr.start(executor).await;
 
         Ok(())
-    }
+    }*/
 }
 
 impl Session for InboundSession {
     fn p2p(&self) -> Arc<P2p> {
         self.p2p.upgrade().unwrap()
     }
+
+    fn selector_id(&self) -> SessionBitflag {
+        SESSION_INBOUND
+    }
 }

+ 11 - 8
src/net/sessions/manual_session.rs → src/net/session/manual_session.rs

@@ -9,8 +9,8 @@ use std::{
 use crate::{
     error::{Error, Result},
     net::{
-        protocols::{ProtocolAddress, ProtocolPing},
-        sessions::Session,
+        protocol::{ProtocolAddress, ProtocolBase, ProtocolPing},
+        session::{Session, SessionBitflag, SESSION_MANUAL},
         ChannelPtr, Connector, P2p,
     },
     system::{StoppableTask, StoppableTaskPtr},
@@ -89,7 +89,7 @@ impl ManualSession {
                     // Remove pending lock since register_channel will add the channel to p2p
                     self.p2p().remove_pending(&addr).await;
 
-                    self.clone().attach_protocols(channel, executor.clone()).await?;
+                    //self.clone().attach_protocols(channel, executor.clone()).await?;
 
                     // Wait for channel to close
                     stop_sub.receive().await;
@@ -112,27 +112,30 @@ impl ManualSession {
         Ok(())
     }
 
-    /// Starts sending keep-alive and address messages across the channels.
-    async fn attach_protocols(
+    // Starts sending keep-alive and address messages across the channels.
+    /*async fn attach_protocols(
         self: Arc<Self>,
         channel: ChannelPtr,
         executor: Arc<Executor<'_>>,
     ) -> Result<()> {
-        let settings = self.p2p().settings().clone();
         let hosts = self.p2p().hosts();
 
-        let protocol_ping = ProtocolPing::new(channel.clone(), settings.clone());
+        let protocol_ping = ProtocolPing::new(channel.clone(), self.p2p());
         let protocol_addr = ProtocolAddress::new(channel, hosts).await;
 
         protocol_ping.start(executor.clone()).await;
         protocol_addr.start(executor).await;
 
         Ok(())
-    }
+    }*/
 }
 
 impl Session for ManualSession {
     fn p2p(&self) -> Arc<P2p> {
         self.p2p.upgrade().unwrap()
     }
+
+    fn selector_id(&self) -> SessionBitflag {
+        SESSION_MANUAL
+    }
 }

+ 8 - 0
src/net/sessions/mod.rs → src/net/session/mod.rs

@@ -27,6 +27,14 @@ pub mod outbound_session;
 /// channel and initializing the channel by performing a network handshake.
 pub mod session;
 
+// bitwise selectors for the protocol_registry
+pub type SessionBitflag = u32;
+pub const SESSION_INBOUND: SessionBitflag = 0b0001;
+pub const SESSION_OUTBOUND: SessionBitflag = 0b0010;
+pub const SESSION_MANUAL: SessionBitflag = 0b0100;
+pub const SESSION_SEED: SessionBitflag = 0b1000;
+pub const SESSION_ALL: SessionBitflag = 0b1111;
+
 pub use inbound_session::InboundSession;
 pub use manual_session::ManualSession;
 pub use outbound_session::OutboundSession;

+ 11 - 8
src/net/sessions/outbound_session.rs → src/net/session/outbound_session.rs

@@ -9,8 +9,8 @@ use std::{
 use crate::{
     error::{Error, Result},
     net::{
-        protocols::{ProtocolAddress, ProtocolPing},
-        sessions::Session,
+        protocol::{ProtocolAddress, ProtocolBase, ProtocolPing},
+        session::{Session, SessionBitflag, SESSION_OUTBOUND},
         ChannelPtr, Connector, P2p,
     },
     system::{StoppableTask, StoppableTaskPtr},
@@ -91,7 +91,7 @@ impl OutboundSession {
                     // Remove pending lock since register_channel will add the channel to p2p
                     self.p2p().remove_pending(&addr).await;
 
-                    self.clone().attach_protocols(channel, executor.clone()).await?;
+                    //self.clone().attach_protocols(channel, executor.clone()).await?;
 
                     // Wait for channel to close
                     stop_sub.receive().await;
@@ -151,27 +151,30 @@ impl OutboundSession {
         }
     }
 
-    /// Starts sending keep-alive and address messages across the channels.
-    async fn attach_protocols(
+    // Starts sending keep-alive and address messages across the channels.
+    /*async fn attach_protocols(
         self: Arc<Self>,
         channel: ChannelPtr,
         executor: Arc<Executor<'_>>,
     ) -> Result<()> {
-        let settings = self.p2p().settings().clone();
         let hosts = self.p2p().hosts();
 
-        let protocol_ping = ProtocolPing::new(channel.clone(), settings.clone());
+        let protocol_ping = ProtocolPing::new(channel.clone(), self.p2p());
         let protocol_addr = ProtocolAddress::new(channel, hosts).await;
 
         protocol_ping.start(executor.clone()).await;
         protocol_addr.start(executor).await;
 
         Ok(())
-    }
+    }*/
 }
 
 impl Session for OutboundSession {
     fn p2p(&self) -> Arc<P2p> {
         self.p2p.upgrade().unwrap()
     }
+
+    fn selector_id(&self) -> SessionBitflag {
+        SESSION_OUTBOUND
+    }
 }

+ 11 - 7
src/net/sessions/seed_session.rs → src/net/session/seed_session.rs

@@ -9,8 +9,8 @@ use std::{
 use crate::{
     error::{Error, Result},
     net::{
-        protocols::{ProtocolPing, ProtocolSeed},
-        sessions::Session,
+        protocol::{ProtocolBase, ProtocolPing, ProtocolSeed},
+        session::{Session, SessionBitflag, SESSION_SEED},
         ChannelPtr, Connector, HostsPtr, P2p, SettingsPtr,
     },
     util::sleep,
@@ -100,7 +100,7 @@ impl SeedSession {
 
                 self.clone().register_channel(channel.clone(), executor.clone()).await?;
 
-                self.attach_protocols(channel, hosts, settings, executor).await?;
+                //self.attach_protocols(channel, hosts, settings, executor).await?;
 
                 debug!(target: "net", "SeedSession::start_seed(i={}) [END]", seed_index);
                 Ok(())
@@ -112,15 +112,15 @@ impl SeedSession {
         }
     }
 
-    /// Starts keep-alive messages and seed protocol.
-    async fn attach_protocols(
+    // Starts keep-alive messages and seed protocol.
+    /*async fn attach_protocols(
         self: Arc<Self>,
         channel: ChannelPtr,
         hosts: HostsPtr,
         settings: SettingsPtr,
         executor: Arc<Executor<'_>>,
     ) -> Result<()> {
-        let protocol_ping = ProtocolPing::new(channel.clone(), settings.clone());
+        let protocol_ping = ProtocolPing::new(channel.clone(), self.p2p());
         protocol_ping.start(executor.clone()).await;
 
         let protocol_seed = ProtocolSeed::new(channel.clone(), hosts, settings.clone());
@@ -130,11 +130,15 @@ impl SeedSession {
         channel.stop().await;
 
         Ok(())
-    }
+    }*/
 }
 
 impl Session for SeedSession {
     fn p2p(&self) -> Arc<P2p> {
         self.p2p.upgrade().unwrap()
     }
+
+    fn selector_id(&self) -> SessionBitflag {
+        SESSION_SEED
+    }
 }

+ 27 - 4
src/net/sessions/session.rs → src/net/session/session.rs

@@ -1,11 +1,11 @@
 use async_trait::async_trait;
-use log::*;
+use log::debug;
 use smol::Executor;
 use std::sync::Arc;
 
 use crate::{
     error::Result,
-    net::{p2p::P2pPtr, protocols::ProtocolVersion, ChannelPtr},
+    net::{p2p::P2pPtr, protocol::ProtocolVersion, ChannelPtr},
 };
 
 /// Removes channel from the list of connected channels when a stop signal is
@@ -37,15 +37,36 @@ pub trait Session: Sync {
     ) -> Result<()> {
         debug!(target: "net", "Session::register_channel() [START]");
 
+        // Protocols should all be initialized but not started
+        // We do this so that the protocols can begin receiving and buffering messages
+        // while the handshake protocol is ongoing.
+        // They are currently in sleep mode.
+        let p2p = self.p2p();
+        let protocols =
+            p2p.protocol_registry().attach(self.selector_id(), channel.clone(), p2p.clone()).await;
+
+        // Perform the handshake protocol
         let protocol_version = ProtocolVersion::new(channel.clone(), self.p2p().settings()).await;
         let handshake_task =
             self.perform_handshake_protocols(protocol_version, channel.clone(), executor.clone());
 
-        // start channel
-        channel.start(executor);
+        // Switch on the channel
+        channel.start(executor.clone());
 
+        // Wait for handshake to finish.
         handshake_task.await?;
 
+        // Now the channel is ready
+        debug!(target: "net", "Session handshake complete. Activating remaining protocols");
+
+        // Now start all the protocols
+        // They are responsible for managing their own lifetimes and
+        // correctly self destructing when the channel ends.
+        for protocol in protocols {
+            // Activate protocol
+            protocol.start(executor.clone()).await?;
+        }
+
         debug!(target: "net", "Session::register_channel() [END]");
         Ok(())
     }
@@ -76,4 +97,6 @@ pub trait Session: Sync {
 
     /// Returns a pointer to the p2p network interface.
     fn p2p(&self) -> P2pPtr;
+
+    fn selector_id(&self) -> u32;
 }

+ 1 - 4
src/zk/mod.rs

@@ -1,7 +1,4 @@
 pub mod circuit;
-
-#[cfg(feature = "zkvm")]
+/// Halo2 zkas virtual machine
 pub mod vm;
-
-#[cfg(feature = "zkvm")]
 mod vm_stack;

+ 1 - 1
src/zk/vm_stack.rs

@@ -1,4 +1,4 @@
-//! Stack type abstractions
+//! VM stack type abstractions
 use halo2_gadgets::{
     ecc::{chip::EccChip, FixedPoint, Point},
     utilities::CellValue,

+ 14 - 48
src/zkas/analyzer.rs

@@ -1,27 +1,23 @@
 use std::{
-    io,
     io::{stdin, stdout, Read, Write},
-    process,
     str::Chars,
 };
 
-use termion::{color, style};
-
 use super::{
     ast::{
         Constant, Constants, StatementType, Statements, Var, Variable, Variables, Witness,
         Witnesses,
     },
+    error::ErrorEmitter,
     types::Type,
 };
 
 pub struct Analyzer {
-    file: String,
-    lines: Vec<String>,
     pub constants: Constants,
     pub witnesses: Witnesses,
     pub statements: Statements,
     pub stack: Variables,
+    error: ErrorEmitter,
 }
 
 impl Analyzer {
@@ -34,15 +30,10 @@ impl Analyzer {
     ) -> Self {
         // For nice error reporting, we'll load everything into a string
         // vector so we have references to lines.
-        let lines = source.as_str().lines().map(|x| x.to_string()).collect();
-        Analyzer {
-            file: filename.to_string(),
-            lines,
-            constants,
-            witnesses,
-            statements,
-            stack: vec![],
-        }
+        let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
+        let error = ErrorEmitter::new("Semantic", filename, lines.clone());
+
+        Analyzer { constants, witnesses, statements, stack: vec![], error }
     }
 
     pub fn analyze_types(&mut self) {
@@ -62,7 +53,7 @@ impl Analyzer {
             // It's kinda ugly.
             if arg_types[0] == Type::BaseArray || arg_types[0] == Type::ScalarArray {
                 if statement.args.is_empty() {
-                    self.error(
+                    self.error.emit(
                         format!(
                             "Passed no arguments to `{:?}` call. Expected at least 1.",
                             statement.opcode
@@ -81,7 +72,7 @@ impl Analyzer {
                         };
 
                         if arg_types[0] == Type::BaseArray && var_type != Type::Base {
-                            self.error(
+                            self.error.emit(
                                 format!(
                                     "Incorrect argument type. Expected `{:?}`, got `{:?}`",
                                     arg_types[0],
@@ -93,7 +84,7 @@ impl Analyzer {
                         }
 
                         if arg_types[0] == Type::ScalarArray && var_type != Type::Scalar {
-                            self.error(
+                            self.error.emit(
                                 format!(
                                     "Incorrect argument type. Expected `{:?}`, got `{:?}`",
                                     arg_types[0],
@@ -108,7 +99,7 @@ impl Analyzer {
                         arg.typ = var_type;
                         args.push(arg);
                     } else {
-                        self.error(
+                        self.error.emit(
                             format!("Unknown argument reference `{}`.", i.name),
                             i.line,
                             i.column,
@@ -117,7 +108,7 @@ impl Analyzer {
                 }
             } else {
                 if statement.args.len() != arg_types.len() {
-                    self.error(
+                    self.error.emit(
                         format!(
                             "Incorrent number of args to `{:?}` call. Expected {}, got {}",
                             statement.opcode,
@@ -138,7 +129,7 @@ impl Analyzer {
                         };
 
                         if var_type != arg_types[idx] {
-                            self.error(
+                            self.error.emit(
                                 format!(
                                     "Incorrect argument type. Expected `{:?}`, got `{:?}`",
                                     arg_types[idx], var_type,
@@ -152,7 +143,7 @@ impl Analyzer {
                         arg.typ = var_type;
                         args.push(arg);
                     } else {
-                        self.error(
+                        self.error.emit(
                             format!("Unknown argument reference `{}`.", i.name),
                             i.line,
                             i.column,
@@ -213,7 +204,7 @@ impl Analyzer {
                 if let Some(index) = stack.iter().position(|&r| r == &arg.name) {
                     println!("Found at stack index {}", index);
                 } else {
-                    self.error(
+                    self.error.emit(
                         format!("Could not find `{}` on the stack", arg.name),
                         arg.line,
                         arg.column,
@@ -283,31 +274,6 @@ impl Analyzer {
         None
     }
 
-    fn error(&self, msg: String, ln: usize, col: usize) {
-        let err_msg = format!("{} (line {}, column {})", msg, ln, col);
-        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
-        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
-        let caret = format!("{:width$}^", "", width = pad);
-        let msg = format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret);
-        Analyzer::abort(&msg);
-    }
-
-    fn abort(msg: &str) {
-        let stderr = io::stderr();
-        let mut handle = stderr.lock();
-        write!(
-            handle,
-            "{}{}Semantic error:{} {}",
-            style::Bold,
-            color::Fg(color::Red),
-            style::Reset,
-            msg,
-        )
-        .unwrap();
-        handle.flush().unwrap();
-        process::exit(1);
-    }
-
     fn pause() {
         let msg = b"[Press Enter to continue]\r";
         let mut stdout = stdout();

+ 11 - 34
src/zkas/compiler.rs

@@ -1,8 +1,9 @@
-use std::{io, io::Write, process, str::Chars};
+use std::str::Chars;
 
-use termion::{color, style};
-
-use super::ast::{Constants, StatementType, Statements, Witnesses};
+use super::{
+    ast::{Constants, StatementType, Statements, Witnesses},
+    error::ErrorEmitter,
+};
 use crate::util::serial::{serialize, VarInt};
 
 /// Version of the binary
@@ -11,12 +12,11 @@ pub const BINARY_VERSION: u8 = 1;
 pub const MAGIC_BYTES: [u8; 4] = [0x0b, 0x00, 0xb1, 0x35];
 
 pub struct Compiler {
-    file: String,
-    lines: Vec<String>,
     constants: Constants,
     witnesses: Witnesses,
     statements: Statements,
     debug_info: bool,
+    error: ErrorEmitter,
 }
 
 impl Compiler {
@@ -30,8 +30,10 @@ impl Compiler {
     ) -> Self {
         // For nice error reporting, we'll load everything into a string
         // vector so we have references to lines.
-        let lines = source.as_str().lines().map(|x| x.to_string()).collect();
-        Compiler { file: filename.to_string(), lines, constants, witnesses, statements, debug_info }
+        let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
+        let error = ErrorEmitter::new("Compiler", filename, lines.clone());
+
+        Compiler { constants, witnesses, statements, debug_info, error }
     }
 
     pub fn compile(&self) -> Vec<u8> {
@@ -77,7 +79,7 @@ impl Compiler {
                     continue
                 }
 
-                self.error(
+                self.error.emit(
                     format!("Failed finding a stack reference for `{}`", arg.name),
                     arg.line,
                     arg.column,
@@ -104,29 +106,4 @@ impl Compiler {
 
         None
     }
-
-    fn error(&self, msg: String, ln: usize, col: usize) {
-        let err_msg = format!("{} (line {}, column {})", msg, ln, col);
-        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
-        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
-        let caret = format!("{:width$}^", "", width = pad);
-        let msg = format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret);
-        Compiler::abort(&msg);
-    }
-
-    fn abort(msg: &str) {
-        let stderr = io::stderr();
-        let mut handle = stderr.lock();
-        write!(
-            handle,
-            "{}{}Compiler error:{} {}",
-            style::Bold,
-            color::Fg(color::Red),
-            style::Reset,
-            msg,
-        )
-        .unwrap();
-        handle.flush().unwrap();
-        process::exit(1);
-    }
 }

+ 41 - 0
src/zkas/error.rs

@@ -0,0 +1,41 @@
+use std::{io, io::Write, process};
+
+use termion::{color, style};
+
+pub(super) struct ErrorEmitter {
+    namespace: String,
+    file: String,
+    lines: Vec<String>,
+}
+
+impl ErrorEmitter {
+    pub fn new(namespace: &str, file: &str, lines: Vec<String>) -> Self {
+        Self { namespace: namespace.to_string(), file: file.to_string(), lines }
+    }
+
+    pub fn emit(&self, msg: String, ln: usize, col: usize) {
+        let err_msg = format!("{} (line{}, column {})", msg, ln, col);
+        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
+        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
+        let caret = format!("{:width$}^", "", width = pad);
+        let msg = format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret);
+        self.abort(&msg);
+    }
+
+    fn abort(&self, msg: &str) {
+        let stderr = io::stderr();
+        let mut handle = stderr.lock();
+        write!(
+            handle,
+            "{}{}{} error:{} {}",
+            style::Bold,
+            color::Fg(color::Red),
+            self.namespace,
+            style::Reset,
+            msg,
+        )
+        .unwrap();
+        handle.flush().unwrap();
+        process::exit(1);
+    }
+}

+ 19 - 35
src/zkas/lexer.rs

@@ -1,6 +1,6 @@
-use std::{io, io::Write, process, str::Chars};
+use std::str::Chars;
 
-use termion::{color, style};
+use super::error::ErrorEmitter;
 
 #[derive(Hash, Eq, PartialEq, Clone, Debug)]
 pub enum TokenType {
@@ -33,9 +33,8 @@ impl Token {
 }
 
 pub struct Lexer<'a> {
-    file: String,
-    lines: Vec<String>,
     source: Chars<'a>,
+    error: ErrorEmitter,
 }
 
 impl<'a> Lexer<'a> {
@@ -43,7 +42,9 @@ impl<'a> Lexer<'a> {
         // For nice error reporting, we'll load everything into a string
         // vector so we have references to lines.
         let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
-        Lexer { file: filename.to_string(), lines, source }
+        let error = ErrorEmitter::new("Lexer", filename, lines.clone());
+
+        Self { source, error }
     }
 
     pub fn lex(self) -> Vec<Token> {
@@ -78,7 +79,11 @@ impl<'a> Lexer<'a> {
 
                 if in_string {
                     // TODO: Allow newlines in strings?
-                    self.error(format!("Invalid ending in string `{}`", &strbuf), lineno, column);
+                    self.error.emit(
+                        format!("Invalid ending in string `{}`", &strbuf),
+                        lineno,
+                        column,
+                    );
                 }
 
                 in_comment = false;
@@ -141,7 +146,7 @@ impl<'a> Lexer<'a> {
 
             if c == '"' && !in_string {
                 if in_symbol {
-                    self.error(format!("Illegal char `{}` for symbol", c), lineno, column);
+                    self.error.emit(format!("Illegal char `{}` for symbol", c), lineno, column);
                 }
                 in_string = true;
                 continue
@@ -149,7 +154,11 @@ impl<'a> Lexer<'a> {
 
             if c == '"' && in_string {
                 if strbuf.is_empty() {
-                    self.error(format!("Invalid ending in string `{}`", &strbuf), lineno, column);
+                    self.error.emit(
+                        format!("Invalid ending in string `{}`", &strbuf),
+                        lineno,
+                        column,
+                    );
                 }
 
                 in_string = false;
@@ -229,41 +238,16 @@ impl<'a> Lexer<'a> {
                         tokens.push(Token::new("=".to_string(), TokenType::Assign, lineno, column));
                         continue
                     }
-                    _ => self.error(format!("Invalid token `{}`", c), lineno, column - 1),
+                    _ => self.error.emit(format!("Invalid token `{}`", c), lineno, column - 1),
                 }
                 continue
             }
 
-            self.error(format!("Invalid token `{}`", c), lineno, column - 1);
+            self.error.emit(format!("Invalid token `{}`", c), lineno, column - 1);
         }
 
         tokens
     }
-
-    fn error(&self, msg: String, ln: usize, col: usize) {
-        let err_msg = format!("{} (line {}, column {})", msg, ln, col);
-        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
-        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
-        let caret = format!("{:width$}^", "", width = pad);
-        let msg = format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret);
-        Lexer::abort(&msg);
-    }
-
-    fn abort(msg: &str) {
-        let stderr = io::stderr();
-        let mut handle = stderr.lock();
-        write!(
-            handle,
-            "{}{}Lexer error:{} {}",
-            style::Bold,
-            color::Fg(color::Red),
-            style::Reset,
-            msg,
-        )
-        .unwrap();
-        handle.flush().unwrap();
-        process::exit(1);
-    }
 }
 
 fn is_letter(ch: char) -> bool {

+ 2 - 0
src/zkas/mod.rs

@@ -6,6 +6,8 @@ pub mod ast;
 pub mod compiler;
 /// Binary decoder
 pub mod decoder;
+/// Error emitter
+mod error;
 /// Lexer module
 pub mod lexer;
 /// Language opcodes

+ 40 - 58
src/zkas/parser.rs

@@ -1,31 +1,32 @@
-use std::{io, io::Write, iter::Peekable, process, str::Chars};
+use std::{iter::Peekable, str::Chars};
 
 use indexmap::IndexMap;
 use itertools::Itertools;
-use termion::{color, style};
 
 use super::{
     ast::{
         Constant, Constants, Statement, StatementType, Statements, UnparsedConstants,
         UnparsedWitnesses, Variable, Witness, Witnesses,
     },
+    error::ErrorEmitter,
     lexer::{Token, TokenType},
     opcode::Opcode,
     types::Type,
 };
 
 pub struct Parser {
-    file: String,
-    lines: Vec<String>,
     tokens: Vec<Token>,
+    error: ErrorEmitter,
 }
 
 impl Parser {
     pub fn new(filename: &str, source: Chars, tokens: Vec<Token>) -> Self {
         // For nice error reporting, we'll load everything into a string
         // vector so we have references to lines.
-        let lines = source.as_str().lines().map(|x| x.to_string()).collect();
-        Parser { file: filename.to_string(), lines, tokens }
+        let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
+        let error = ErrorEmitter::new("Parser", filename, lines.clone());
+
+        Parser { tokens, error }
     }
 
     pub fn parse(self) -> (Constants, Witnesses, Statements) {
@@ -93,7 +94,9 @@ impl Parser {
                         }
                     }
 
-                    x => self.error(format!("Unknown `{}` proof section", x), t.line, t.column),
+                    x => {
+                        self.error.emit(format!("Unknown `{}` proof section", x), t.line, t.column)
+                    }
                 }
             }
 
@@ -114,7 +117,7 @@ impl Parser {
 
                 // TODO: Do we need this?
                 if namespace_found && namespace != constant_tokens[0].token {
-                    self.error(
+                    self.error.emit(
                         format!(
                             "Found `{}` namespace. Expected `{}`.",
                             constant_tokens[0].token, namespace
@@ -134,7 +137,7 @@ impl Parser {
 
                 while let Some((typ, name, comma)) = constants_inner.next_tuple() {
                     if comma.token_type != TokenType::Comma {
-                        self.error(
+                        self.error.emit(
                             "Separator is not a comma".to_string(),
                             comma.line,
                             comma.column,
@@ -142,7 +145,7 @@ impl Parser {
                     }
 
                     if constants_map.contains_key(name.token.as_str()) {
-                        self.error(
+                        self.error.emit(
                             format!(
                                 "Section `constant` already contains the token `{}`.",
                                 &name.token
@@ -164,7 +167,7 @@ impl Parser {
 
                 // TODO: Do we need this?
                 if namespace_found && namespace != contract_tokens[0].token {
-                    self.error(
+                    self.error.emit(
                         format!(
                             "Found `{}` namespace. Expected `{}`.",
                             contract_tokens[0].token, namespace
@@ -184,7 +187,7 @@ impl Parser {
 
                 while let Some((typ, name, comma)) = contract_inner.next_tuple() {
                     if comma.token_type != TokenType::Comma {
-                        self.error(
+                        self.error.emit(
                             "Separator is not a comma".to_string(),
                             comma.line,
                             comma.column,
@@ -192,7 +195,7 @@ impl Parser {
                     }
 
                     if contract_map.contains_key(name.token.as_str()) {
-                        self.error(
+                        self.error.emit(
                             format!(
                                 "Section `contract` already contains the token `{}`.",
                                 &name.token
@@ -213,7 +216,7 @@ impl Parser {
                 self.check_section_structure("circuit", contract_tokens.clone());
 
                 if circuit_tokens[circuit_tokens.len() - 2].token_type != TokenType::Semicolon {
-                    self.error(
+                    self.error.emit(
                         "Circuit section does not end with a semicolon. Would never finish parsing.".to_string(),
                         circuit_tokens[circuit_tokens.len()-2].line,
                         circuit_tokens[circuit_tokens.len()-2].column
@@ -222,7 +225,7 @@ impl Parser {
 
                 // TODO: Do we need this?
                 if namespace_found && namespace != circuit_tokens[0].token {
-                    self.error(
+                    self.error.emit(
                         format!(
                             "Found `{}` namespace. Expected `{}`.",
                             circuit_tokens[0].token, namespace
@@ -271,7 +274,7 @@ impl Parser {
 
     fn check_section_structure(&self, section: &str, tokens: Vec<Token>) {
         if tokens[0].token_type != TokenType::String {
-            self.error(
+            self.error.emit(
                 format!("{} section declaration must start with a naming string.", section),
                 tokens[0].line,
                 tokens[0].column,
@@ -279,7 +282,7 @@ impl Parser {
         }
 
         if tokens[1].token_type != TokenType::LeftBrace {
-            self.error(
+            self.error.emit(
                 format!(
                     "{} section opening is not correct. Must be opened with a left brace `{{`",
                     section
@@ -290,7 +293,7 @@ impl Parser {
         }
 
         if tokens[tokens.len() - 1].token_type != TokenType::RightBrace {
-            self.error(
+            self.error.emit(
                 format!(
                     "{} section closing is not correct. Must be closed with a right brace `}}`",
                     section
@@ -303,7 +306,7 @@ impl Parser {
         if (section == "constant" || section == "contract") &&
             tokens[2..tokens.len() - 1].len() % 3 != 0
         {
-            self.error(
+            self.error.emit(
                 format!(
                     "Invalid number of elements in `{}` section. Must be pairs of `type:name` separated with a comma `,`",
                     section
@@ -319,7 +322,7 @@ impl Parser {
 
         for (k, v) in ast {
             if &v.0.token != k {
-                self.error(
+                self.error.emit(
                     format!("Constant name `{}` doesn't match token `{}`.", v.0.token, k),
                     v.0.line,
                     v.0.column,
@@ -327,7 +330,7 @@ impl Parser {
             }
 
             if v.0.token_type != TokenType::Symbol {
-                self.error(
+                self.error.emit(
                     format!("Constant name `{}` is not a symbol.", v.0.token),
                     v.0.line,
                     v.0.column,
@@ -335,7 +338,7 @@ impl Parser {
             }
 
             if v.1.token_type != TokenType::Symbol {
-                self.error(
+                self.error.emit(
                     format!("Constant type `{}` is not a symbol.", v.1.token),
                     v.1.line,
                     v.1.column,
@@ -353,7 +356,7 @@ impl Parser {
                 }
 
                 x => {
-                    self.error(
+                    self.error.emit(
                         format!("`{}` is an illegal constant type", x),
                         v.1.line,
                         v.1.column,
@@ -370,7 +373,7 @@ impl Parser {
 
         for (k, v) in ast {
             if &v.0.token != k {
-                self.error(
+                self.error.emit(
                     format!("Witness name `{}` doesn't match token `{}`.", v.0.token, k),
                     v.0.line,
                     v.0.column,
@@ -378,7 +381,7 @@ impl Parser {
             }
 
             if v.0.token_type != TokenType::Symbol {
-                self.error(
+                self.error.emit(
                     format!("Witness name `{}` is not a symbol.", v.0.token),
                     v.0.line,
                     v.0.column,
@@ -386,7 +389,7 @@ impl Parser {
             }
 
             if v.1.token_type != TokenType::Symbol {
-                self.error(
+                self.error.emit(
                     format!("Witness type `{}` is not a symbol.", v.1.token),
                     v.1.line,
                     v.1.column,
@@ -440,7 +443,11 @@ impl Parser {
                 }
 
                 x => {
-                    self.error(format!("`{}` is an illegal witness type", x), v.1.line, v.1.column);
+                    self.error.emit(
+                        format!("`{}` is an illegal witness type", x),
+                        v.1.line,
+                        v.1.column,
+                    );
                 }
             }
         }
@@ -461,7 +468,7 @@ impl Parser {
                 }
             }
             if left_paren != right_paren {
-                self.error(
+                self.error.emit(
                     "Incorrect number of left and right parenthesis for statement.".to_string(),
                     statement[0].line,
                     statement[0].column,
@@ -508,7 +515,7 @@ impl Parser {
                         }
 
                         if !parsing {
-                            self.error(
+                            self.error.emit(
                                 format!("Illegal token `{}`", next_token.token),
                                 next_token.line,
                                 next_token.column,
@@ -614,7 +621,7 @@ impl Parser {
                     }
 
                     x => {
-                        self.error(
+                        self.error.emit(
                             format!("Unimplemented function call `{}`", x),
                             token.line,
                             token.column,
@@ -635,7 +642,7 @@ impl Parser {
     ) -> Vec<Variable> {
         if let Some(next_token) = iter.peek() {
             if next_token.token_type != TokenType::LeftParen {
-                self.error(
+                self.error.emit(
                     "Invalid function call opening. Must start with a `(`".to_string(),
                     next_token.line,
                     next_token.column,
@@ -644,7 +651,7 @@ impl Parser {
             // Skip the opening parenthesis
             iter.next();
         } else {
-            self.error("Premature ending of statement".to_string(), token.line, token.column);
+            self.error.emit("Premature ending of statement".to_string(), token.line, token.column);
         }
 
         // Eat up function arguments
@@ -663,7 +670,7 @@ impl Parser {
             }
 
             if sep.token_type != TokenType::Comma {
-                self.error(
+                self.error.emit(
                     "Argument separator is not a comma (`,`)".to_string(),
                     sep.line,
                     sep.column,
@@ -673,29 +680,4 @@ impl Parser {
 
         args
     }
-
-    fn error(&self, msg: String, ln: usize, col: usize) {
-        let err_msg = format!("{} (line {}, column {})", msg, ln, col);
-        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
-        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
-        let caret = format!("{:width$}^", "", width = pad);
-        let msg = format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret);
-        Parser::abort(&msg);
-    }
-
-    fn abort(msg: &str) {
-        let stderr = io::stderr();
-        let mut handle = stderr.lock();
-        write!(
-            handle,
-            "{}{}Parser error:{} {}",
-            style::Bold,
-            color::Fg(color::Red),
-            style::Reset,
-            msg,
-        )
-        .unwrap();
-        handle.flush().unwrap();
-        process::exit(1);
-    }
 }