diff --git a/Cargo.toml b/Cargo.toml index a3d777701..b5f36d50c 100644 --- a/Cargo.toml +++ b/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"] diff --git a/Makefile b/Makefile index d4fccc733..c8817c7ca 100644 --- a/Makefile +++ b/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) diff --git a/bin/ircd/src/main.rs b/bin/ircd/src/main.rs index fbc3c8dab..58ee66e3a 100644 --- a/bin/ircd/src/main.rs +++ b/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>, - seen_privmsg_ids: SeenPrivMsgIdsPtr, - executor: Arc>, -) -> 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>, options: ProgramOptions) -> Result<()> { let listener = match Async::::bind(options.irc_accept_addr) { Ok(listener) => listener, @@ -145,7 +126,28 @@ async fn start(executor: Arc>, 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>, 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>, 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), diff --git a/bin/ircd/src/protocol_privmsg.rs b/bin/ircd/src/protocol_privmsg.rs index 315042ac8..3d78ebded 100644 --- a/bin/ircd/src/protocol_privmsg.rs +++ b/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>, seen_privmsg_ids: SeenPrivMsgIdsPtr, p2p: net::P2pPtr, - ) -> Arc { + ) -> net::ProtocolBasePtr { let message_subsytem = channel.get_message_subsystem(); message_subsytem.add_dispatch::().await; - debug!("ADDED DISPATCH"); - let privmsg_sub = channel.subscribe_msg::().await.expect("Missing PrivMsg dispatcher!"); @@ -38,15 +37,8 @@ impl ProtocolPrivMsg { }) } - pub async fn start(self: Arc, executor: Arc>) { - 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) -> 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, executor: Arc>) -> 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" + } +} diff --git a/bin/map/src/app.rs b/bin/map/src/app.rs deleted file mode 100644 index c9dc34ac0..000000000 --- a/bin/map/src/app.rs +++ /dev/null @@ -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) -> 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() - } -} diff --git a/bin/map/src/lib.rs b/bin/map/src/lib.rs index d05977f03..abe92bb36 100644 --- a/bin/map/src/lib.rs +++ b/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}; diff --git a/bin/map/src/list.rs b/bin/map/src/list.rs deleted file mode 100644 index 44ab29321..000000000 --- a/bin/map/src/list.rs +++ /dev/null @@ -1,50 +0,0 @@ -//use std::collections::HashMap; -use tui::widgets::ListState; - -#[derive(Clone)] -pub struct NodeIdList { - pub state: ListState, - pub node_id: Vec, -} - -impl NodeIdList { - pub fn new(node_id: Vec) -> 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); -//} diff --git a/bin/map/src/main.rs b/bin/map/src/main.rs index aa0657c82..9840a2163 100644 --- a/bin/map/src/main.rs +++ b/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>, app: App) -> Result<()> { +async fn run_rpc(ex: Arc>, model: Arc) -> 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) -> Result<()> { loop { let reply = client.get_info().await?; - update(app.clone(), reply).await?; + + 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 _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: node3["message"].to_string(), + }]; + + //model.lock().await.update(infos).await; + } else { + // TODO: error handling + println!("Reply is an error"); + } + 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(); - - let node1 = &nodes[0]; - let node2 = &nodes[1]; - let _node3 = &nodes[2]; - - 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"); - } - - Ok(()) -} - -async fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<()> { +async fn render(terminal: &mut Terminal, model: Arc) -> 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) + } + + 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, &mut app))?; - + 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; } _ => (), } diff --git a/bin/map/src/model.rs b/bin/map/src/model.rs new file mode 100644 index 000000000..7321ab700 --- /dev/null +++ b/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) -> 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, + pub node_id: Mutex>, +} + +impl IdList { + pub fn new(node_id: Vec) -> IdList { + let node_id = Mutex::new(node_id); + IdList { state: Mutex::new(ListState::default()), node_id } + } +} + +pub struct InfoList { + pub index: Mutex, + pub infos: Mutex>, +} + +impl InfoList { + pub fn new(infos: Vec) -> 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() + } +} diff --git a/bin/map/src/node_info.rs b/bin/map/src/node_info.rs deleted file mode 100644 index f6a1f7137..000000000 --- a/bin/map/src/node_info.rs +++ /dev/null @@ -1,51 +0,0 @@ -#[derive(Clone)] -pub struct NodeInfoView { - pub index: usize, - pub infos: Vec, -} - -impl NodeInfoView { - pub fn new(infos: Vec) -> 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); -//} diff --git a/bin/map/src/notes/list.rs b/bin/map/src/notes/list.rs deleted file mode 100644 index 6b74dc506..000000000 --- a/bin/map/src/notes/list.rs +++ /dev/null @@ -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, - //pub node_info: NodeInfo, - //pub index: HashMap, - //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); - } -} diff --git a/bin/map/src/types.rs b/bin/map/src/types.rs deleted file mode 100644 index 1ad737b1b..000000000 --- a/bin/map/src/types.rs +++ /dev/null @@ -1,2 +0,0 @@ -//pub type NodeId = String; -//pub type NodeInfo = String; diff --git a/bin/map/src/ui.rs b/bin/map/src/ui.rs index 83829e9ff..ab40f4a0b 100644 --- a/bin/map/src/ui.rs +++ b/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(f: &mut Frame, app: &mut App) { +pub fn ui(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 = app - .node_list + let nodes: Vec = view + .id_list .node_id .iter() .map(|id| { @@ -29,18 +29,19 @@ pub fn ui(f: &mut Frame, 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(app: &mut App, f: &mut Frame, index: usize, slice: Vec) { - 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(view: View, f: &mut Frame<'_, B>, index: usize, slice: Vec) { + 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)), diff --git a/book/src/tutorial.md b/book/src/tutorial.md index 517b7b49e..400050145 100644 --- a/book/src/tutorial.md +++ b/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 diff --git a/proof/burn.rs b/proof/burn.rs index b164f0c18..7f6abdf45 100644 --- a/proof/burn.rs +++ b/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 */ diff --git a/proof/mint.rs b/proof/mint.rs index 05f04512b..d776a3c21 100644 --- a/proof/mint.rs +++ b/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 */ diff --git a/src/net/channel.rs b/src/net/channel.rs index 532aa9934..fbbc1eb08 100644 --- a/src/net/channel.rs +++ b/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(&self, message: M) -> Result<()> { + pub async fn send(&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(&self, message: M) -> Result<()> { + async fn send_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(&self) -> Result> { + pub async fn subscribe_msg(&self) -> Result> { 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::().await; - message_subsystem.add_dispatch::().await; - message_subsystem.add_dispatch::().await; - message_subsystem.add_dispatch::().await; - message_subsystem.add_dispatch::().await; - message_subsystem.add_dispatch::().await; + message_subsystem.add_dispatch::().await; + message_subsystem.add_dispatch::().await; + message_subsystem.add_dispatch::().await; + message_subsystem.add_dispatch::().await; + message_subsystem.add_dispatch::().await; + message_subsystem.add_dispatch::().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()) { diff --git a/src/net/messages.rs b/src/net/message.rs similarity index 98% rename from src/net/messages.rs rename to src/net/message.rs index 1c42a7abb..6ab8737f5 100644 --- a/src/net/messages.rs +++ b/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::{ diff --git a/src/net/message_subscriber.rs b/src/net/message_subscriber.rs index b14356348..b47627e1a 100644 --- a/src/net/message_subscriber.rs +++ b/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 MessageDispatcher { /// channels. async fn trigger_all(&self, message: MessageResult) { debug!( + target: "net", "MessageDispatcher::trigger_all({}) [START, subs={}]", M::name(), if message.is_ok() { "msg" } else { "err" }, @@ -109,6 +110,7 @@ impl MessageDispatcher { self.collect_garbage(garbage_ids).await; debug!( + target: "net", "MessageDispatcher::trigger_all({}) [END, subs={}]", M::name(), if message.is_ok() { "msg" } else { "err" }, diff --git a/src/net/mod.rs b/src/net/mod.rs index 20203b654..61491796f 100644 --- a/src/net/mod.rs +++ b/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}; diff --git a/src/net/p2p.rs b/src/net/p2p.rs index b8eedb733..9da49b09a 100644 --- a/src/net/p2p.rs +++ b/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, hosts: HostsPtr, + protocol_registry: ProtocolRegistry, settings: SettingsPtr, } impl P2p { /// Create a new p2p network. - pub fn new(settings: Settings) -> Arc { + pub async fn new(settings: Settings) -> Arc { 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> { self.channel_subscriber.clone().subscribe().await diff --git a/src/net/protocols/mod.rs b/src/net/protocol/mod.rs similarity index 83% rename from src/net/protocols/mod.rs rename to src/net/protocol/mod.rs index d395ff560..1d4216d20 100644 --- a/src/net/protocols/mod.rs +++ b/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; +} diff --git a/src/net/protocols/protocol_address.rs b/src/net/protocol/protocol_address.rs similarity index 79% rename from src/net/protocols/protocol_address.rs rename to src/net/protocol/protocol_address.rs index 2dc8bd386..88d692884 100644 --- a/src/net/protocols/protocol_address.rs +++ b/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, - get_addrs_sub: MessageSubscription, + addrs_sub: MessageSubscription, + get_addrs_sub: MessageSubscription, 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 { + 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::() + .subscribe_msg::() .await .expect("Missing addrs dispatcher!"); // Creates a subscription to get-address message. let get_addrs_sub = channel .clone() - .subscribe_msg::() + .subscribe_msg::() .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, executor: Arc>) { - 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, executor: Arc>) -> 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" + } +} diff --git a/src/net/protocol/protocol_base.rs b/src/net/protocol/protocol_base.rs new file mode 100644 index 000000000..54857bbb9 --- /dev/null +++ b/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; + +#[async_trait] +pub trait ProtocolBase { + async fn start(self: Arc, executor: Arc>) -> Result<()>; + + fn name(&self) -> &'static str; +} diff --git a/src/net/protocols/protocol_jobs_manager.rs b/src/net/protocol/protocol_jobs_manager.rs similarity index 100% rename from src/net/protocols/protocol_jobs_manager.rs rename to src/net/protocol/protocol_jobs_manager.rs diff --git a/src/net/protocols/protocol_ping.rs b/src/net/protocol/protocol_ping.rs similarity index 75% rename from src/net/protocols/protocol_ping.rs rename to src/net/protocol/protocol_ping.rs index aad16efbe..020ec8568 100644 --- a/src/net/protocols/protocol_ping.rs +++ b/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, + pong_sub: MessageSubscription, settings: SettingsPtr, jobsman: ProtocolJobsManagerPtr, } impl ProtocolPing { /// Create a new ping-pong protocol. - pub fn new(channel: ChannelPtr, settings: SettingsPtr) -> Arc { + 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::() + .await + .expect("Missing ping dispatcher!"); + + // Creates a subscription to pong message. + let pong_sub = channel + .clone() + .subscribe_msg::() + .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, executor: Arc>) { - 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) -> Result<()> { debug!(target: "net", "ProtocolPing::run_ping_pong() [START]"); - // Creates a subscription to pong message. - let pong_sub = self - .channel - .clone() - .subscribe_msg::() - .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) -> Result<()> { debug!(target: "net", "ProtocolPing::reply_to_ping() [START]"); - // Creates a subscription to ping message. - let ping_sub = self - .channel - .clone() - .subscribe_msg::() - .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, executor: Arc>) -> 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" + } +} diff --git a/src/net/protocol/protocol_registry.rs b/src/net/protocol/protocol_registry.rs new file mode 100644 index 000000000..8ba1a579d --- /dev/null +++ b/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>, +} + +impl ProtocolRegistry { + pub fn new() -> Self { + Self { protocol_constructors: Mutex::new(Vec::new()) } + } + + // add_protocol()? + pub async fn register(&self, session_flags: SessionBitflag, constructor: C) + where + C: 'static + Fn(ChannelPtr, P2pPtr) -> F + Send + Sync, + F: 'static + Future + 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 { + let mut protocols: Vec = 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 + } +} diff --git a/src/net/protocols/protocol_seed.rs b/src/net/protocol/protocol_seed.rs similarity index 72% rename from src/net/protocols/protocol_seed.rs rename to src/net/protocol/protocol_seed.rs index 6eca990f7..d2de384a8 100644 --- a/src/net/protocols/protocol_seed.rs +++ b/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 { + 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, _executor: Arc>) -> Result<()> { + async fn start(self: Arc, _executor: Arc>) -> Result<()> { debug!(target: "net", "ProtocolSeed::start() [START]"); // Create a subscription to address message. let addr_sub = self .channel .clone() - .subscribe_msg::() + .subscribe_msg::() .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" } } diff --git a/src/net/protocols/protocol_version.rs b/src/net/protocol/protocol_version.rs similarity index 88% rename from src/net/protocols/protocol_version.rs rename to src/net/protocol/protocol_version.rs index e63de528f..81151bf6f 100644 --- a/src/net/protocols/protocol_version.rs +++ b/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, - verack_sub: MessageSubscription, + version_sub: MessageSubscription, + verack_sub: MessageSubscription, settings: SettingsPtr, } @@ -26,14 +26,14 @@ impl ProtocolVersion { // Creates a version subscription. let version_sub = channel .clone() - .subscribe_msg::() + .subscribe_msg::() .await .expect("Missing version dispatcher!"); // Creates a version acknowledgement subscription. let verack_sub = channel .clone() - .subscribe_msg::() + .subscribe_msg::() .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) -> 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]"); diff --git a/src/net/sessions/inbound_session.rs b/src/net/session/inbound_session.rs similarity index 90% rename from src/net/sessions/inbound_session.rs rename to src/net/session/inbound_session.rs index 9f977e22a..667486e69 100644 --- a/src/net/sessions/inbound_session.rs +++ b/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, channel: ChannelPtr, executor: Arc>, ) -> 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 { self.p2p.upgrade().unwrap() } + + fn selector_id(&self) -> SessionBitflag { + SESSION_INBOUND + } } diff --git a/src/net/sessions/manual_session.rs b/src/net/session/manual_session.rs similarity index 89% rename from src/net/sessions/manual_session.rs rename to src/net/session/manual_session.rs index e625340dd..b10f9155c 100644 --- a/src/net/sessions/manual_session.rs +++ b/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, channel: ChannelPtr, executor: Arc>, ) -> 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 { self.p2p.upgrade().unwrap() } + + fn selector_id(&self) -> SessionBitflag { + SESSION_MANUAL + } } diff --git a/src/net/sessions/mod.rs b/src/net/session/mod.rs similarity index 82% rename from src/net/sessions/mod.rs rename to src/net/session/mod.rs index 9dcf93238..1cad1ffef 100644 --- a/src/net/sessions/mod.rs +++ b/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; diff --git a/src/net/sessions/outbound_session.rs b/src/net/session/outbound_session.rs similarity index 92% rename from src/net/sessions/outbound_session.rs rename to src/net/session/outbound_session.rs index c7561c5af..8c0f91716 100644 --- a/src/net/sessions/outbound_session.rs +++ b/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, channel: ChannelPtr, executor: Arc>, ) -> 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 { self.p2p.upgrade().unwrap() } + + fn selector_id(&self) -> SessionBitflag { + SESSION_OUTBOUND + } } diff --git a/src/net/sessions/seed_session.rs b/src/net/session/seed_session.rs similarity index 91% rename from src/net/sessions/seed_session.rs rename to src/net/session/seed_session.rs index f54b5153e..403d7b0f7 100644 --- a/src/net/sessions/seed_session.rs +++ b/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, channel: ChannelPtr, hosts: HostsPtr, settings: SettingsPtr, executor: Arc>, ) -> 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 { self.p2p.upgrade().unwrap() } + + fn selector_id(&self) -> SessionBitflag { + SESSION_SEED + } } diff --git a/src/net/sessions/session.rs b/src/net/session/session.rs similarity index 67% rename from src/net/sessions/session.rs rename to src/net/session/session.rs index 01ae07c14..e16fddca7 100644 --- a/src/net/sessions/session.rs +++ b/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; } diff --git a/src/zk/mod.rs b/src/zk/mod.rs index a68994c2b..dc6405089 100644 --- a/src/zk/mod.rs +++ b/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; diff --git a/src/zk/vm_stack.rs b/src/zk/vm_stack.rs index 67e0881c4..555ab5987 100644 --- a/src/zk/vm_stack.rs +++ b/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, diff --git a/src/zkas/analyzer.rs b/src/zkas/analyzer.rs index 1f823b1d0..0d11971b7 100644 --- a/src/zkas/analyzer.rs +++ b/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, 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 = 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(); diff --git a/src/zkas/compiler.rs b/src/zkas/compiler.rs index c462ef113..73180bb9c 100644 --- a/src/zkas/compiler.rs +++ b/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, 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 = 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 { @@ -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); - } } diff --git a/src/zkas/error.rs b/src/zkas/error.rs new file mode 100644 index 000000000..eed3f6947 --- /dev/null +++ b/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, +} + +impl ErrorEmitter { + pub fn new(namespace: &str, file: &str, lines: Vec) -> 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); + } +} diff --git a/src/zkas/lexer.rs b/src/zkas/lexer.rs index 50a18bfbd..1106292c8 100644 --- a/src/zkas/lexer.rs +++ b/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, 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 = 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 { @@ -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 { diff --git a/src/zkas/mod.rs b/src/zkas/mod.rs index e86d9a487..c05e4b267 100644 --- a/src/zkas/mod.rs +++ b/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 diff --git a/src/zkas/parser.rs b/src/zkas/parser.rs index ff2c13625..3ac3e455c 100644 --- a/src/zkas/parser.rs +++ b/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, tokens: Vec, + error: ErrorEmitter, } impl Parser { pub fn new(filename: &str, source: Chars, tokens: Vec) -> 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 = 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) { 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 { 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); - } }