Forráskód Böngészése

book/dchat: EDIT2- polish dchat code, use mdbook format for code + beautify

lunar-mining 4 éve
szülő
commit
074a34593b
29 módosított fájl, 472 hozzáadás és 848 törlés
  1. 8 8
      doc/src/SUMMARY.md
  2. 5 5
      doc/src/learn/dchat/creating-dchat/creating-dchat.md
  3. 12 33
      doc/src/learn/dchat/creating-dchat/message.md
  4. 28 82
      doc/src/learn/dchat/creating-dchat/protocol-dchat.md
  5. 27 27
      doc/src/learn/dchat/creating-dchat/protocols.md
  6. 39 71
      doc/src/learn/dchat/creating-dchat/register-protocol.md
  7. 10 19
      doc/src/learn/dchat/creating-dchat/sending-messages.md
  8. 15 79
      doc/src/learn/dchat/creating-dchat/ui.md
  9. 1 1
      doc/src/learn/dchat/creating-dchat/using-dchat.md
  10. 13 11
      doc/src/learn/dchat/deployment/deployment.md
  11. 20 0
      doc/src/learn/dchat/deployment/error-handling.md
  12. 21 0
      doc/src/learn/dchat/deployment/getting-started.md
  13. 2 2
      doc/src/learn/dchat/deployment/local-deployment.md
  14. 43 0
      doc/src/learn/dchat/deployment/seed-node.md
  15. 31 0
      doc/src/learn/dchat/deployment/sessions.md
  16. 40 0
      doc/src/learn/dchat/deployment/settings.md
  17. 95 0
      doc/src/learn/dchat/deployment/start-run-stop.md
  18. 28 0
      doc/src/learn/dchat/deployment/writing-a-daemon.md
  19. 0 124
      doc/src/learn/dchat/local-deployment/creating-and-running.md
  20. 0 38
      doc/src/learn/dchat/local-deployment/error-handling.md
  21. 0 43
      doc/src/learn/dchat/local-deployment/getting-started.md
  22. 0 31
      doc/src/learn/dchat/local-deployment/inbound-and-outbound.md
  23. 0 75
      doc/src/learn/dchat/local-deployment/seed-node.md
  24. 0 101
      doc/src/learn/dchat/local-deployment/settings.md
  25. 0 51
      doc/src/learn/dchat/local-deployment/writing-a-daemon.md
  26. 4 4
      example/dchat/src/dchatmsg.rs
  27. 4 3
      example/dchat/src/error.rs
  28. 20 34
      example/dchat/src/main.rs
  29. 6 6
      example/dchat/src/protocol_dchat.rs

+ 8 - 8
doc/src/SUMMARY.md

@@ -28,14 +28,14 @@
   - [ZK explainer](learn/zk_explainer.md)
   - [ZK explainer](learn/zk_explainer.md)
   - [Dchat](learn/dchat/dchat.md)
   - [Dchat](learn/dchat/dchat.md)
     - [Deployment](learn/dchat/local-deployment/local-deployment.md)
     - [Deployment](learn/dchat/local-deployment/local-deployment.md)
-      - [Getting started](learn/dchat/local-deployment/getting-started.md)
-      - [Writing a daemon](learn/dchat/local-deployment/writing-a-daemon.md)
-      - [Sessions](learn/dchat/local-deployment/sessions.md)
-      - [Settings](learn/dchat/local-deployment/settings.md)
-      - [Error handling](learn/dchat/local-deployment/error-handling.md)
-      - [Running the network](learn/dchat/local-deployment/creating-and-running.md)
-      - [Seed node](learn/dchat/local-deployment/seed-node.md)
-      - [Deployment](learn/dchat/local-deployment/deployment.md)
+      - [Getting started](learn/dchat/deployment/getting-started.md)
+      - [Writing a daemon](learn/dchat/deployment/writing-a-daemon.md)
+      - [Sessions](learn/dchat/deployment/sessions.md)
+      - [Settings](learn/dchat/deployment/settings.md)
+      - [Error handling](learn/dchat/deployment/error-handling.md)
+      - [Running the network](learn/dchat/deployment/start-run-stop.md)
+      - [Seed node](learn/dchat/deployment/seed-node.md)
+      - [Deployment](learn/dchat/deployment/deployment.md)
     - [Creating dchat](learn/dchat/creating-dchat/creating-dchat.md)
     - [Creating dchat](learn/dchat/creating-dchat/creating-dchat.md)
       - [Message](learn/dchat/creating-dchat/message.md)
       - [Message](learn/dchat/creating-dchat/message.md)
       - [Protocols](learn/dchat/creating-dchat/protocols.md)
       - [Protocols](learn/dchat/creating-dchat/protocols.md)

+ 5 - 5
doc/src/learn/dchat/creating-dchat/creating-dchat.md

@@ -6,8 +6,8 @@ send and receive messages across the network.
 
 
 This section will cover:
 This section will cover:
 
 
-* The Message type
-* Protocols and the protocol registry
-* The message subsystem
-* Message subscriptions
-* Channels
+* The `Message` type
+* `Protocols` and the `ProtocolRegistry`
+* The `MessageSubsystem`
+* `MessageSubscription`
+* `Channel`

+ 12 - 33
doc/src/learn/dchat/creating-dchat/message.md

@@ -1,45 +1,24 @@
 # Creating a Message type
 # Creating a Message type
 
 
-We'll start by creating a custom Message type called Dchatmsg. This is the
-data structure that we'll use to send messages between dchat instances.
+We'll start by creating a custom `Message` type called `DchatMsg`. This is the
+data structure that we'll use to send messages between `dchat` instances.
 
 
-Messages on the p2p network must implement the Message trait. Message is a
+Messages on the p2p network must implement the `Message` trait. `Message` is a
 generic type that standardizes all messages on DarkFi's p2p network.
 generic type that standardizes all messages on DarkFi's p2p network.
 
 
-We define a custom type called Dchatmsg that implements the Message
-trait. We also add serde's SerialEncodable and SerialDecodable to our
-struct definition so our messages can be parsed by the network.
+We define a custom type called `DchatMsg` that implements the
+`Message` trait. We also add `darkfi::util::SerialEncodable` and
+`darkfi::util::SerialDecodable` macros to our struct definition so our
+messages can be parsed by the network.
 
 
-The Message trait requires that we implement a method called name(),
-which returns a str of the struct's name.
-
-```
-use darkfi::{
-    net,
-    util::serial::{SerialDecodable, SerialEncodable},
-};
-
-impl net::Message for Dchatmsg {
-    fn name() -> &'static str {
-        "Dchatmsg"
-    }
-}
-
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
-pub struct Dchatmsg {
-    pub msg: String,
-}
-```
+`Message` requires that we implement a method called `name()`, which
+returns a str of the struct's name.
 
 
 For the purposes of our chat program, we will also define a buffer where
 For the purposes of our chat program, we will also define a buffer where
 we can write messages upon receiving them on the p2p network. We'll wrap
 we can write messages upon receiving them on the p2p network. We'll wrap
-this in a Mutex to ensure thread safety and an Arc pointer so we can
+this in a `Mutex` to ensure thread safety and an `Arc` pointer so we can
 pass it around.
 pass it around.
 
 
+```rust
+{{#include ../../../../../example/dchat/src/dchatmsg.rs::19}}
 ```
 ```
-use async_std::sync::{Arc, Mutex};
-
-pub type DchatmsgsBuffer = Arc<Mutex<Vec<Dchatmsg>>>;
-```
-
-

+ 28 - 82
doc/src/learn/dchat/creating-dchat/protocol-dchat.md

@@ -1,104 +1,50 @@
 # ProtocolDchat
 # ProtocolDchat
 
 
 Let's start tying these concepts together. We'll define a struct called
 Let's start tying these concepts together. We'll define a struct called
-ProtocolDchat that contains a MessageSubscription to Dchatmsg and a
-pointer to the ProtocolJobsManager. We'll also include the DchatmsgsBuffer
+`ProtocolDchat` that contains a `MessageSubscription` to `DchatMsg` and a
+pointer to the `ProtocolJobsManager`. We'll also include the `DchatMsgsBuffer`
 in the struct as it will come in handy later on.
 in the struct as it will come in handy later on.
 
 
+```rust
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:1:13}}
 ```
 ```
-use darkfi::net;
 
 
-use crate::dchatmsg::{Dchatmsg, DchatmsgsBuffer};
+Next we'll implement the trait `ProtocolBase`. `ProtocolBase` requires
+two functions, `start()` and `name()`. In `start()` we will start up the
+`ProtocolJobsManager`. `name()` will return a `str` of the protocol name.
 
 
-pub struct ProtocolDchat {
-    jobsman: net::ProtocolJobsManagerPtr,
-    msg_sub: net::MessageSubscription<Dchatmsg>,
-    msgs: DchatmsgsBuffer,
-}
+```rust
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:42:46}}
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:48::}}
 ```
 ```
 
 
-Next we'll implement the trait ProtocolBase. ProtocolBase requires two
-functions, start() and name(). In start() we will start up the Protocol
-Jobs Manager. name() will return a str of the protocol name.
+Once that's done, we'll need to create a `ProtocolDchat` constructor
+that we will pass to the `ProtocolRegistry` to register our protocol.
+We'll invoke the `MessageSubsystem` and add `DchatMsg` as to the list
+of dispatchers. Next, we'll create a `MessageSubscription` to `DchatMsg`
+using the method `subscribe_msg()`.
 
 
-```
-use async_executor::Executor;
-use async_std::sync::Arc;
-use async_trait::async_trait;
-use darkfi::{net, Result};
-
-use crate::dchatmsg::{Dchatmsg, DchatmsgsBuffer};
-
-#[async_trait]
-impl net::ProtocolBase for ProtocolDchat {
-    async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
-        self.jobsman.clone().start(executor.clone());
-        Ok(())
-    }
-
-    fn name(&self) -> &'static str {
-        "ProtocolDchat"
-    }
-}
-```
-
-Once that's done, we'll need to create a ProtocolDchat constructor that
-we will pass to the ProtocolRegistry to register our protocol. The
-constructor takes a pointer to channel which it uses to invoke the
-Message Subsystem and add Dchatmsg as to the list of dispatchers. Next,
-we'll create a message subscription to Dchatmsg using the method
-subscribe_msg().
-
-We'll also initialize the Protocol Jobs Manager and finally return a
+We'll also initialize the `ProtocolJobsManager` and finally return a
 pointer to the protocol.
 pointer to the protocol.
 
 
-```
-impl ProtocolDchat {
-    pub async fn init(channel: net::ChannelPtr, msgs: DchatmsgsBuffer) -> net::ProtocolBasePtr {
-        let message_subsytem = channel.get_message_subsystem();
-        message_subsytem.add_dispatch::<Dchatmsg>().await;
-
-        let msg_sub = channel
-            .subscribe_msg::<Dchatmsg>()
-            .await
-            .expect("Missing DchatMsg dispatcher!");
-
-        Arc::new(Self {
-            jobsman: net::ProtocolJobsManager::new("ProtocolDchat", channel.clone()),
-            msg_sub,
-            msgs,
-        })
-    }
-}
+```rust
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:15:29}}
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:40}}
 ```
 ```
 
 
 We're nearly there. But right now the protocol doesn't actually do
 We're nearly there. But right now the protocol doesn't actually do
-anything. Let's write a method called handle_receive_msg() which receives
-a message on our message subscription and adds it to DchatmsgsBuffer.
+anything. Let's write a method called `handle_receive_msg()` which receives
+a message on our `MessageSubscription` and adds it to `DchatMsgsBuffer`.
  
  
-Put this inside the ProtocolDchat implementation:
+Put this inside the `ProtocolDchat` implementation:
 
 
+```rust
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:31:39}}
 ```
 ```
-async fn handle_receive_msg(self: Arc<Self>) -> Result<()> {
-    while let Ok(msg) = self.msg_sub.receive().await {
-        let msg = (*msg).to_owned();
-        self.msgs.lock().await.push(msg);
-    }
 
 
-    Ok(())
-}
-```
-
-As a final step, let's add that task to the jobs manager that is invoked
-in start():
+As a final step, let's add that task to the `ProtocolJobManager` that is invoked
+in `start()`:
 
 
-```
-async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
-    self.jobsman.clone().start(executor.clone());
-    self.jobsman
-        .clone()
-        .spawn(self.clone().handle_receive_msg(), executor.clone())
-        .await;
-    Ok(())
-}
+```rust
+{{#include ../../../../../example/dchat/src/protocol_dchat.rs:42::}}
 ```
 ```

+ 27 - 27
doc/src/learn/dchat/creating-dchat/protocols.md

@@ -8,57 +8,57 @@ are automatically activated when nodes connect to eachother on the
 p2p network. Here are examples of two protocols that every node runs
 p2p network. Here are examples of two protocols that every node runs
 continuously in the background:
 continuously in the background:
 
 
-[ProtocolPing](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_ping.rs):
-sends ping, receives pong
-[ProtocolAddress](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_address.rs):
-receives a get_address message, sends an address message
+* [ProtocolPing](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_ping.rs):
+sends `ping`, receives `pong`
+* [ProtocolAddress](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_address.rs):
+receives a `get_address` message, sends an `address` message
 
 
 Under the hood, these protocols have a few similarities:
 Under the hood, these protocols have a few similarities:
 
 
-1. They create a subscription to a message type, such as Ping and Pong.
-2. They implement [ProtocolBase](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_base.rs),
+* They create a subscription to a message type, such as `ping` and `pong`.
+* They implement [ProtocolBase](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_base.rs),
 DarkFi's generic protocol trait.
 DarkFi's generic protocol trait.
-3. They run asynchronously using the
+* They run asynchronously using the
 [ProtocolJobsManager](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_jobs_manager.rs).
 [ProtocolJobsManager](https://github.com/darkrenaissance/darkfi/blob/master/src/net/protocol/protocol_jobs_manager.rs).
-4. They hold a pointer to [Channel](https://github.com/darkrenaissance/darkfi/blob/master/src/net/channel.rs) which
+* They hold a pointer to [Channel](https://github.com/darkrenaissance/darkfi/blob/master/src/net/channel.rs) which
 invokes the [MessageSubsystem](https://github.com/darkrenaissance/darkfi/blob/master/src/net/message_subscriber.rs#L170).
 invokes the [MessageSubsystem](https://github.com/darkrenaissance/darkfi/blob/master/src/net/message_subscriber.rs#L170).
 
 
 This introduces several generic interfaces that we must use to build
 This introduces several generic interfaces that we must use to build
 our custom protocol. In particular:
 our custom protocol. In particular:
 
 
-1. The Message Subsystem
+**The Message Subsystem**
 
 
-MessageSubsystem is a generic publish/subscribe class that contains
-a list of Message dispatchers. A new dispatcher is created for every
-Message type. These Message-specific dispatchers maintain a list of
-susbscribers that are subscribed to a particular message.
+`MessageSubsystem` is a generic publish/subscribe class that contains
+a list of `Message` dispatchers. A new dispatcher is created for every
+`Message` type. These `Message` specific dispatchers maintain a list of
+susbscribers that are subscribed to a particular `Message`.
 
 
-2. Message Subscription
+**Message Subscription**
 
 
-A subscription to a specific Message type. Handles receiving messages
+A subscription to a specific `Message` type. Handles receiving messages
 on a subscription.
 on a subscription.
 
 
-3. Channel
+**Channel**
 
 
-A channel is an async connection for communication between nodes. It is
-also a powerful interface that exposes methods to the Message Subsystem
-and implements message subscriptions.
+`Channel` is an async connection for communication between nodes. It is
+also a powerful interface that exposes methods to the `MessageSubsystem`
+and implements `MessageSubscription`.
 
 
-4. The Protocol Registry 
+**The Protocol Registry**
 
 
-ProtocolRegistry is a registry of all protocols. We use it through the
-method register() which passes a protocol constructor and a session
-bitflag. The bitflag specifies which sessions the protocol is created
-for. The ProtocolRegistry then spawns new protocols for different channels
+`ProtocolRegistry` is a registry of all protocols. We use it through the
+method `register()` which passes a protocol constructor and a session
+`bitflag`. The `bitflag` specifies which sessions the protocol is created
+for. The `ProtocolRegistry` then spawns new protocols for different channels
 depending on the session.
 depending on the session.
 
 
-5. ProtocolJobsManager
+**ProtocolJobsManager**
 
 
 An asynchronous job manager that spawns and stops tasks. Its main
 An asynchronous job manager that spawns and stops tasks. Its main
 purpose is so a protocol can cleanly close all started jobs, through
 purpose is so a protocol can cleanly close all started jobs, through
-the function close_all_tasks().  This way if the connection between
+the function `close_all_tasks()`.  This way if the connection between
 nodes is dropped and the channel closes, all protocols are also shutdown.
 nodes is dropped and the channel closes, all protocols are also shutdown.
 
 
-6. ProtocolBase
+**ProtocolBase**
 
 
 A generic protocol trait that all protocols must implement.
 A generic protocol trait that all protocols must implement.

+ 39 - 71
doc/src/learn/dchat/creating-dchat/register-protocol.md

@@ -1,40 +1,26 @@
 # Registering a protocol
 # Registering a protocol
 
 
 We've now successfully created a custom protocol. The next step is the
 We've now successfully created a custom protocol. The next step is the
-register the protocol with the protocol registry.
+register the protocol with the `ProtocolRegistry`.
 
 
-We'll define a new function inside the Dchat implementation called
-register_protocol(). It will invoke the protocol_registry using the
-handle to the p2p network contained in the Dchat struct. It will then
-call register() on the registry and pass the ProtocolDchat constructor.
+We'll define a new function inside the `Dchat` implementation called
+`register_protocol()`. It will invoke the `ProtocolRegistry` using the
+handle to the p2p network contained in the `Dchat` struct. It will then
+call `register()` on the registry and pass the `ProtocolDchat` constructor.
 
 
-```
-use crate::{dchatmsg::DchatmsgsBuffer, protocol_dchat::ProtocolDchat};
-
-pub mod dchatmsg;
-pub mod protocol_dchat;
-
-async fn register_protocol(&self, msgs: DchatmsgsBuffer) -> Result<()> {
-    let registry = self.p2p.protocol_registry();
-    registry
-        .register(net::SESSION_ALL, move |channel, _p2p| {
-            let msgs2 = msgs.clone();
-            async move { ProtocolDchat::init(channel, msgs2).await }
-        })
-        .await;
-    Ok(())
-}
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:84:95}}
 ```
 ```
 
 
-There's a lot going on here. register() takes a closure with two
+There's a lot going on here. `register()` takes a closure with two
 arguments, `channel` and `p2p`. We use `move` to capture these values. We
 arguments, `channel` and `p2p`. We use `move` to capture these values. We
 then create an async closure that captures these values and the value
 then create an async closure that captures these values and the value
-`msgs` and use them to call ProtocolDchat::init() in the async block.
+`msgs` and use them to call `ProtocolDchat::init()` in the async block.
 
 
 The code would be expressed more simply as:
 The code would be expressed more simply as:
 
 
-```
-registry.register(net::SESSION_ALL, async move |channel, _p2p| {
+```rust
+registry.register(!net::SESSION_SEED, async move |channel, _p2p| {
         ProtocolDchat::init(channel, msgs).await
         ProtocolDchat::init(channel, msgs).await
     })
     })
     .await;
     .await;
@@ -42,71 +28,53 @@ registry.register(net::SESSION_ALL, async move |channel, _p2p| {
 
 
 However we cannot do this due to limitation with async closures. So
 However we cannot do this due to limitation with async closures. So
 instead we wrap the `async move` in a `move` in order to capture the
 instead we wrap the `async move` in a `move` in order to capture the
-variables needed by ProtocolDchat::init().
-
-Notice the use of a bitflag. We use SESSION_ALL to specify that this
-protocol should be performed by every session. 
+variables needed by `ProtocolDchat::init()`.
 
 
-Also notice that register_protocol() requires a DchatmsgsBuffer that we send
-to the ProtocolDchat constructor. We'll create the DchatmsgsBuffer in
-main() and pass it to Dchat::new(). Let's add DchatmsgsBuffer to the
-Dchat struct definition first.
+Notice the use of a `bitflag`. We use `!SESSION_SEED` to specify that
+this protocol should be performed by every session, not including the
+seed session.
 
 
-```
-struct Dchat {
-    p2p: net::P2pPtr,
-    recv_msgs: DchatmsgsBuffer,
-}
-
-impl Dchat {
-    fn new(p2p: net::P2pPtr, recv_msgs: DchatmsgsBuffer) -> Self {
-        Self { p2p, recv_msgs }
-    }
-}
-```
+Also notice that `register_protocol()` requires a `DchatMsgsBuffer` that we
+send to the `ProtocolDchat` constructor. We'll create the `DchatMsgsBuffer`
+in `main()` and pass it to `Dchat::new()`. Let's add `DchatMsgsBuffer` to the
+`Dchat` struct definition first.
 
 
-And initialize it, adding Mutex and Dchatmsg to our imports:
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:13:17}}
 
 
+{{#include ../../../../../example/dchat/src/main.rs:26:34}}
+{{#include ../../../../../example/dchat/src/main.rs:119}}
 ```
 ```
-use async_std::sync::Mutex;
-use crate::dchatmsg::Dchatmsg;
-
 
 
-async fn main() -> Result<()> {
-    // ...
-
-    let msgs: DchatmsgsBuffer = Arc::new(Mutex::new(vec![Dchatmsg { msg: String::new() }]));
-    let dchat = Dchat::new(p2p, msgs);
-
-    //... 
-
-    }
+And initialize it:
 
 
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:163:164}}
+    //...
+{{#include ../../../../../example/dchat/src/main.rs:182:184}}
+    //...
+{{#include ../../../../../example/dchat/src/main.rs:197}}
 ```
 ```
 
 
-Finally, call register_protocol() in dchat::start():
-
-```
-async fn start(&self, ex: Arc<Executor<'_>>) -> Result<()> {
-    self.register_protocol(self.recv_msgs.clone()).await?;
+Finally, call `register_protocol()` in `dchat::start()`:
 
 
-    self.p2p.clone().start(ex.clone()).await?;
-    self.p2p.clone().run(ex.clone()).await?;
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:97:103}}
+        self.p2p.clone().run(ex.clone()).await?;
 
 
-    Ok(())
-}
+{{#include ../../../../../example/dchat/src/main.rs:110:112}}
 ```
 ```
 Now try running Alice and Bob and seeing what debug output you get. Keep
 Now try running Alice and Bob and seeing what debug output you get. Keep
 an eye out for the following:
 an eye out for the following:
 
 
 ```
 ```
-[DEBUG] (1) net: Channel::subscribe_msg() [START, command="Dchatmsg", address=tcp://127.0.0.1:55555]
-[DEBUG] (1) net: Channel::subscribe_msg() [END, command="Dchatmsg", address=tcp://127.0.0.1:55555]
+[DEBUG] (1) net: Channel::subscribe_msg() [START, command="DchatMsg", address=tcp://127.0.0.1:55555]
+[DEBUG] (1) net: Channel::subscribe_msg() [END, command="DchatMsg", address=tcp://127.0.0.1:55555]
 [DEBUG] (1) net: Attached ProtocolDchat
 [DEBUG] (1) net: Attached ProtocolDchat
 ```
 ```
 
 
 If you see that, we have successfully:
 If you see that, we have successfully:
 
 
-* Implemented a custom message type and created a message subscription.
-* Implemented a custom protocol and registered it with the protocol registry.
+* Implemented a custom `Message` and created a `MessageSubscription`.
+* Implemented a custom `Protocol` and registered it with the `ProtocolRegistry`.
 
 

+ 10 - 19
doc/src/learn/dchat/creating-dchat/sending-messages.md

@@ -1,36 +1,27 @@
 # Sending messages
 # Sending messages
 
 
 The core of our application has been built. All that's left is to add a UI
 The core of our application has been built. All that's left is to add a UI
-that takes user input, creates a Dchatmsg and sends it over the network.
+that takes user input, creates a `DchatMsg` and sends it over the network.
 
 
-Let's start by creating a send() function inside Dchat. This will
+Let's start by creating a `send()` function inside `Dchat`. This will
 introduce us to a new p2p method that is essential to our chat app:
 introduce us to a new p2p method that is essential to our chat app:
-p2p.broadcast().
+`p2p.broadcast()`.
 
 
 ```
 ```
-async fn send(&self, msg: String) -> Result<()> {
-    let dchatmsg = Dchatmsg { msg };
-    self.p2p.broadcast(dchatmsg).await?;
-    Ok(())
-}
+{{#include ../../../../../example/dchat/src/main.rs:114:118}}
 ```
 ```
 
 
-We pass a String called msg that will be taken from user input. We use
-this input to initialize a message of the type Dchatmsg that the network
-can now support. Finally, we pass the message into p2p.broadcast().
+We pass a `String` called msg that will be taken from user input. We use
+this input to initialize a message of the type `DchatMsg` that the network
+can now support. Finally, we pass the message into `p2p.broadcast()`.
   
   
 Here's what happens under the hood:
 Here's what happens under the hood:
 
 
-```
-pub async fn broadcast<M: Message + Clone>(&self, message: M) -> Result<()> {
-    for channel in self.channels.lock().await.values() {
-        channel.send(message.clone()).await?;
-    }
-    Ok(())
-}
+```rust
+{{#include ../../../../../src/net/p2p.rs:191:196}}
 ```
 ```
 
 
-This is pretty straightforward: broadcast() takes a generic Message type
+This is pretty straightforward: `broadcast()` takes a generic `Message` type
 and sends it across all the channels that our node has access to.
 and sends it across all the channels that our node has access to.
 
 
 All that's left to do is to create a UI.
 All that's left to do is to create a UI.

+ 15 - 79
doc/src/learn/dchat/creating-dchat/ui.md

@@ -1,101 +1,37 @@
 # Slap on a UI
 # Slap on a UI
 
 
-We'll create a new method called menu() inside the Dchat
+We'll create a new method called `menu()` inside the `Dchat`
 implementation. It implements a highly simple UI that allows a user to
 implementation. It implements a highly simple UI that allows a user to
 send messages and see received messages inside the inbox. Our inbox
 send messages and see received messages inside the inbox. Our inbox
-simply displays the messages that ProtocolDchat has saved in the
-DchatmsgBuffer.
+simply displays the messages that `ProtocolDchat` has saved in the
+`DchatMsgBuffer`.
 
 
 Here's what is should look like:
 Here's what is should look like:
 
 
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:36:82}}
 ```
 ```
 
 
-use std::io::stdin;
+We'll call `menu()` inside of `dchat::start()` along with our other methods, like so:
 
 
-async fn menu(&self) -> Result<()> {
-    let mut buffer = String::new();
-    let stdin = stdin();
-    loop {
-        println!(
-            "Welcome to dchat.
-s: send message
-i: inbox
-q: quit "
-        );
-        stdin.read_line(&mut buffer)?;
-        // Remove trailing \n
-        buffer.pop();
-        match buffer.as_str() {
-            "q" => return Ok(()),
-            "s" => {
-                // Remove trailing s
-                buffer.pop();
-                stdin.read_line(&mut buffer)?;
-                match self.send(buffer.clone()).await {
-                    Ok(_) => {
-                        println!("you sent: {}", buffer);
-                    }
-                    Err(e) => {
-                        println!("send failed for reason: {}", e);
-                    }
-                }
-                buffer.clear();
-            }
-            "i" => {
-                let msgs = self.recv_msgs.lock().await;
-                if msgs.is_empty() {
-                    println!("inbox is empty")
-                } else {
-                    println!("received:");
-                    for i in msgs.iter() {
-                        if !i.msg.is_empty() {
-                            println!("{}", i.msg);
-                        }
-                    }
-                }
-                buffer.clear();
-            }
-            _ => {}
-        }
-    }
-}
-```
-
-We'll call menu() inside of dchat::start() along with our other methods, like so:
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:97:98}}
 
 
-```
-async fn start(&self, ex: Arc<Executor<'_>>) -> Result<()> {
-    self.register_protocol(self.recv_msgs.clone()).await?;
+{{#include ../../../../../example/dchat/src/main.rs:103}}
 
 
-    self.p2p.clone().start(ex.clone()).await?;
-    self.p2p.clone().run(ex.clone()).await?;
+        self.p2p.clone().run(ex.clone()).await?;
 
 
-    self.menu().await?;
-
-    Ok(())
-}
+{{#include ../../../../../example/dchat/src/main.rs:108:112}}
 ```
 ```
 
 
 But wait- if you try running this code, you'll notice that the menu never
 But wait- if you try running this code, you'll notice that the menu never
-gets displayed. That's because we call .await on the previous function
-call, p2p.run(). p2p.run() is a loop that runs until we exit the program,
+gets displayed. That's because we call `.await` on the previous function
+call, `p2p.run()`. `p2p.run()` is a loop that runs until we exit the program,
 so in order for it to not block other threads from executing we'll need
 so in order for it to not block other threads from executing we'll need
 to detach it in the background.
 to detach it in the background.
 
 
 The complete implementaion looks like this:
 The complete implementaion looks like this:
 
 
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:97:112}}
 ```
 ```
-async fn start(&mut self, ex: Arc<Executor<'_>>) -> Result<()> {
-    let ex2 = ex.clone();
-
-    self.register_protocol(self.recv_msgs.clone()).await?;
-    self.p2p.clone().start(ex.clone()).await?;
-    ex2.spawn(self.p2p.clone().run(ex.clone())).detach();
-
-    self.menu().await?;
-
-    Ok(())
-}
-```
-
-

+ 1 - 1
doc/src/learn/dchat/creating-dchat/using-dchat.md

@@ -2,7 +2,7 @@
 
 
 We are finally ready to test our program. Spin up 5 different terminals.
 We are finally ready to test our program. Spin up 5 different terminals.
 
 
-In terminal 1, run lilith.
+In terminal 1, run `lilith`.
 
 
 ```
 ```
 cargo run --dchat
 cargo run --dchat

+ 13 - 11
doc/src/learn/dchat/local-deployment/deployment.md → doc/src/learn/dchat/deployment/deployment.md

@@ -2,9 +2,9 @@
 
 
 Get ready to spin up a bunch of different terminals. We are going to
 Get ready to spin up a bunch of different terminals. We are going to
 run 3 nodes: Alice and Bob and our seed node. To run the seed node,
 run 3 nodes: Alice and Bob and our seed node. To run the seed node,
-go to the lilith directory and run it by passing dchat as an argument:
+go to the `lilith` directory and run it by passing `dchat` as an argument:
 
 
-```
+```bash
 cargo run -- --dchat
 cargo run -- --dchat
 ```
 ```
 
 
@@ -23,14 +23,16 @@ Here's what the debug output should look like:
 
 
 Next we'll run Alice.
 Next we'll run Alice.
 
 
-```
+```bash
 cargo run a
 cargo run a
 ```
 ```
 
 
 You can `cat` or `tail` the log file created in /tmp/. I recommend using
 You can `cat` or `tail` the log file created in /tmp/. I recommend using
 multitail for colored debug output, like so:
 multitail for colored debug output, like so:
 
 
-`multitail -c /tmp/alice.log`
+```bash
+multitail -c /tmp/alice.log
+```
 
 
 Check out that debug output! Keep an eye out for this line:
 Check out that debug output! Keep an eye out for this line:
 
 
@@ -42,10 +44,10 @@ That shows Alice has connected to the seed node. Here's some more
 interesting output:
 interesting output:
 
 
 ```
 ```
-08:54:59 [DEBUG] (1) net: Attached ProtocolPing
-08:54:59 [DEBUG] (1) net: Attached ProtocolSeed
-08:54:59 [DEBUG] (1) net: ProtocolVersion::run() [START]
-08:54:59 [DEBUG] (1) net: ProtocolVersion::exchange_versions() [START]
+[DEBUG] (1) net: Attached ProtocolPing
+[DEBUG] (1) net: Attached ProtocolSeed
+[DEBUG] (1) net: ProtocolVersion::run() [START]
+[DEBUG] (1) net: ProtocolVersion::exchange_versions() [START]
 ```
 ```
 
 
 This raises an interesting question- what are these protocols? We'll deal
 This raises an interesting question- what are these protocols? We'll deal
@@ -55,18 +57,18 @@ when it connects to another node.
 
 
 Keep Alice and the seed node running. Now let's run Bob.
 Keep Alice and the seed node running. Now let's run Bob.
 
 
-```
+```bash
 cargo run b
 cargo run b
 ```
 ```
 
 
 And track his debug output:
 And track his debug output:
 
 
-```
+```bash
 multitail -c /tmp/bob.log
 multitail -c /tmp/bob.log
 ```
 ```
 
 
 Success! All going well, Alice and Bob are now connected to each
 Success! All going well, Alice and Bob are now connected to each
-other. We should be able to watch Ping and Pong messages being sent
+other. We should be able to watch `ping` and `pong` messages being sent
 across by tracking their debug output.
 across by tracking their debug output.
 
 
 We have created a local deployment of the p2p network.
 We have created a local deployment of the p2p network.

+ 20 - 0
doc/src/learn/dchat/deployment/error-handling.md

@@ -0,0 +1,20 @@
+# Error handling 
+
+Before we continue, we need to quickly add some error handling to handle
+the case where a user forgets to add the command-line flag.
+
+```rust
+{{#include ../../../../../example/dchat/src/dchat_error.rs:1:12}}
+```
+
+Finally we can read the flag from the command-line by adding the following lines to main():
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:13:14}}
+{{#include ../../../../../example/dchat/src/main.rs:17}}
+
+{{#include ../../../../../example/dchat/src/main.rs:163:172}}
+...
+{{#include ../../../../../example/dchat/src/main.rs:197}}
+```
+

+ 21 - 0
doc/src/learn/dchat/deployment/getting-started.md

@@ -0,0 +1,21 @@
+# Getting started
+
+We'll create a new cargo directory and add DarkFi to our `Cargo.toml`,
+like so:
+
+```
+{{#include ../../../../../example/dchat/Cargo.toml::8}}
+```
+
+Be sure to replace the path to DarkFi with the correct path for your
+setup.
+
+Once that's done we can access DarkFi's net methods inside of
+dchat. We'll need a few more external libraries too, so add these
+dependencies:
+
+```
+{{#include ../../../../../example/dchat/Cargo.toml:10:26}}
+```
+
+

+ 2 - 2
doc/src/learn/dchat/local-deployment/local-deployment.md → doc/src/learn/dchat/deployment/local-deployment.md

@@ -5,5 +5,5 @@ introduce a number of key concepts:
 
 
 * p2p daemons
 * p2p daemons
 * Inbound, outbound, manual and seed nodes
 * Inbound, outbound, manual and seed nodes
-* Understanding Sessions
-* p2p.start() and p2p.run()
+* Understanding `Sessions`
+* `p2p.start()` and `p2p.run()`

+ 43 - 0
doc/src/learn/dchat/deployment/seed-node.md

@@ -0,0 +1,43 @@
+# The seed node
+
+Let's create an instance of dchat inside our main function and pass the
+p2p network into it.  Then we'll add `dchat::start()` to our async loop
+in the main function. 
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:163:197}}
+```
+
+Now try to run the program, don't forget to add a specifier `a` or `b`
+to define the type of node.
+
+It should output the following error: 
+
+```
+Error: NetworkOperationFailed
+```
+
+That's because there is no seed node online for our nodes to connect to. A
+seed node is used when connecting to the network: it is a special kind
+of inbound node that gets connected to, sends over a list of addresses
+and disconnects again.  This behavior is defined in the `ProtocolSeed`.
+
+Everytime we run `p2p.start()` we attempt to connect to a seed using a
+`SeedSyncSession`.  If the `SeedSyncSession` fails, `p2p.start()` will fail,
+so without a seed node, our inbound and outbound nodes cannot establish
+a connection to the network. Let's remedy that.
+
+We have two options here. First, we could implement our own seed node.
+Alternatively, DarkFi maintains a master seed node called `lilith` that
+can act as the seed for many different protocols at the same time. For
+the purpose of this tutorial let's use `lilith`.
+
+What `lilith` does in the background is very simple. Just like any p2p
+daemon, a seed node defines its networks settings into a type called
+`Settings` and creates a new instance of the p2p network. It then runs
+`p2p::start()` and `p2p::run()`. The difference is in the settings: a seed
+node just specifies an inbound address which other nodes will connect to.
+
+Crucially, this inbound address must match the seed address we specified
+earlier in Alice and Bob's settings.
+

+ 31 - 0
doc/src/learn/dchat/deployment/sessions.md

@@ -0,0 +1,31 @@
+# Sessions
+
+To deploy the p2p network, we need to configure two types of nodes:
+inbound and outbound. These nodes perform different roles on the p2p
+network. An inbound node receives connections. An outbound node makes
+connections.
+
+The behavior of these nodes is defined in what is called a
+[Session](https://github.com/darkrenaissance/darkfi/blob/master/src/net/session/mod.rs#L93).
+There are four types of sessions: `Manual`, `Inbound`, `Outbound` and `SeedSync`.
+
+There behavior is as follows: 
+
+**Inbound**: Uses an `Acceptor` to accept connections on the inbound connect
+address configured in settings.
+
+**Outbound**: Starts a connect loop for every connect slot configured in
+settings. Establishes a connection using `Connector.connect()`: a method
+that takes an address returns a `Channel`.
+
+**Manual**: Uses a `Connector` to connect to a single address that is passed
+to `ManualSession::connect()`. Used to create an explicit connection to
+an address.
+
+**SeedSync**: Creates a connection to the seed nodes specified in settings.
+Loops through all the configured seeds and tries to connect to them
+using a `Connector`. Either connects successfully, fails with an error or
+times out.
+
+To create an inbound and outbound node, we will need to configure them
+using a type called `net::Settings`.

+ 40 - 0
doc/src/learn/dchat/deployment/settings.md

@@ -0,0 +1,40 @@
+# Settings
+
+On production-ready software, you would usually configure your node
+using a config file or command line inputs. On `dchat` we are keeping
+things ultra simple. We pass a command line flag that is either `a` or
+`b`. If we pass `a` we will initialize an inbound node. If we pass `b`
+we will initialize an outbound node.
+
+Here's how that works. We define two methods called `alice()` and
+`bob()`. `alice()` returns the `Settings` that will create an inbound
+node. bob() return the `Settings` for an outbound node.
+
+We also implement logging that outputs to `/tmp/alice.log` and `/tmp/bob.log`
+so we can access the debug output of our nodes. We store this info in a
+log file because we don't want it interfering with our terminal UI when
+we eventually build it.
+
+This is a function that returns the settings to create Alice, an
+inbound node:
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:121:141}}
+```
+
+This is a function that returns the settings to create Bob, an
+outbound node:
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:143:161}}
+```
+
+Both outbound and inbound nodes specify a seed address to connect to. The
+inbound node also specifies an external address and an inbound address:
+this is where it will receive connections. The outbound node specifies
+the number of outbound connection slots, which is the number of outbound
+connections the node will try to make.
+
+These are the only settings we need to think about. For the rest, we
+use the network defaults.
+

+ 95 - 0
doc/src/learn/dchat/deployment/start-run-stop.md

@@ -0,0 +1,95 @@
+# Start, run, stop
+
+## Creating the p2p network
+
+Now that we have initialized the network settings we can create an
+instance of the p2p network.
+
+Add the following to `main()`:
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:174}}
+```
+
+## Running the p2p network
+
+We will next create a Dchat struct that will store all the data required
+by dchat. For now, it will just hold a pointer to the p2p network.
+
+```rust
+struct Dchat {
+    p2p: net::P2pPtr,
+}
+
+impl Dchat {
+    fn new(p2p: net::P2pPtr) -> Self {
+        Self { p2p }
+    }
+}
+```
+
+Now let's add a `start()` function to the `Dchat` implementation. `start()`
+takes an executor and runs three p2p methods, `p2p::start()`, `p2p::run()`,
+and `p2p::stop()`.
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs:97:98}}
+
+{{#include ../../../../../example/dchat/src/main.rs:103}}
+
+        self.p2p.clone().run(ex.clone()).await?;
+
+{{#include ../../../../../example/dchat/src/main.rs:108:112}}
+```
+
+## Start
+
+Let's take a quick look at the underlying p2p methods we're using here.
+
+This is [start()](https://github.com/darkrenaissance/darkfi/blob/master/src/net/p2p.rs#L129):
+
+```rust
+{{#include ../../../../../src/net/p2p.rs:129:143}}
+```
+
+`start()` changes the `P2pState` to `P2pState::Start` and runs a [seed
+session](https://github.com/darkrenaissance/darkfi/blob/master/src/net/session/seed_session.rs).
+
+This loops through the seed addresses specified in our `Settings` and
+tries to connect to them. The seed session either connects successfully,
+fails with an error or times out.
+
+If a seed node connects successfully, it runs a version exchange protocol,
+stores the channel in the p2p list of channels, and disconnects, removing
+the channel from the channel list.
+
+## Run
+
+This is [run()](https://github.com/darkrenaissance/darkfi/blob/master/src/net/p2p.rs#L157):
+
+```rust
+{{#include ../../../../../src/net/p2p.rs:157:184}}
+```
+
+`run()` changes the P2pState to `P2pState::Run`. It then calls `start()`
+on manual, inbound and outbound sessions that are contained with the
+`P2p` struct. The outcome of `start()` will depend on how your node is
+configured. `start()` will try to run each kind of session, but if the
+configuration doesn't match attemping to start a session will simply
+return without doing anything. For example, if you are an outbound node,
+`inbound.start()` will return with the following message:
+
+```
+info!(target: "net", "Not configured for accepting incoming connections.");
+```
+
+`run()` then waits for a stop signal and shuts down the sessions when it
+is received.
+
+## Stop
+
+To send this shutdown signal, we'll need to manually call
+[stop()](https://github.com/darkrenaissance/darkfi/blob/master/src/net/p2p.rs#L186).
+`stop()` transmits a shutdown signal to all channels subscribed to the
+stop signal and safely shuts down the network.
+

+ 28 - 0
doc/src/learn/dchat/deployment/writing-a-daemon.md

@@ -0,0 +1,28 @@
+# Writing a daemon
+
+DarkFi consists of many seperate daemons communicating with each other. To
+run the p2p network, we'll need to implement our own daemon.  So we'll
+start building `dchat` by configuring our main function into a daemon that
+can run the p2p network.
+
+```rust
+{{#include ../../../../../example/dchat/src/main.rs::9}}
+
+{{#include ../../../../../example/dchat/src/main.rs:23:24}}
+
+{{#include ../../../../../example/dchat/src/main.rs:163:164}}
+{{#include ../../../../../example/dchat/src/main.rs:176:179}}
+
+{{#include ../../../../../example/dchat/src/main.rs:186:189}}
+{{#include ../../../../../example/dchat/src/main.rs:191:197}}
+```
+
+We get the number of cpu cores using `num_cpus::get()` and spin up a
+bunch of threads in parallel using `easy_parallel`. Right now it doesn't
+do anything, but soon we'll run dchat inside this block.
+
+**Note**: DarkFi includes a macro called `async_daemonize` that is used by
+DarkFi binaries to minimize boilerplate in the repo.  To keep things
+simple we will ignore this macro for the purpose of this tutorial.  But
+check it out if you are curious: [util/cli.rs](https://github.com/darkrenaissance/darkfi/blob/master/src/util/cli.rs#L154).
+

+ 0 - 124
doc/src/learn/dchat/local-deployment/creating-and-running.md

@@ -1,124 +0,0 @@
-# Creating the p2p network
-
-Now that we have initialized the network settings we can create an
-instance of the p2p network.
-
-Add the following to main():
-
-```
-let p2p = net::P2p::new(settings?.into()).await;
-```
-
-# Running the p2p network
-
-We will next create a Dchat struct that will store all the data required
-by dchat. For now, it will just hold a pointer to the p2p network.
-
-```
-use darkfi::net;
-
-struct Dchat {
-    p2p: net::P2pPtr,
-}
-
-impl Dchat {
-    fn new(p2p: net::P2pPtr) -> Self {
-        Self { p2p }
-    }
-}
-```
-
-Now let's add a start() function to the Dchat implementation. start()
-takes an executor and runs two p2p methods, p2p::start() and p2p::run().
-
-```
-async fn start(&self, ex: Arc<Executor<'_>>) -> Result<()> {
-
-    self.p2p.clone().start(ex.clone()).await?;
-    self.p2p.clone().run(ex.clone()).await?;
-
-    Ok(())
-}
-```
-
-Let's take a quick look at the underlying p2p methods we're using here.
-
-This is [start()](https://github.com/darkrenaissance/darkfi/blob/master/src/net/p2p.rs#L129):
-
-```
-pub async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
-    debug!(target: "net", "P2p::start() [BEGIN]");
-
-    *self.state.lock().await = P2pState::Start;
-
-    // Start seed session
-    let seed = SeedSession::new(Arc::downgrade(&self));
-    // This will block until all seed queries have finished
-    seed.start(executor.clone()).await?;
-
-    *self.state.lock().await = P2pState::Started;
-
-    debug!(target: "net", "P2p::start() [END]");
-    Ok(())
-}
-```
-
-start() changes the P2pState to P2pState::Start and runs a [seed
-session](https://github.com/darkrenaissance/darkfi/blob/master/src/net/session/seed_session.rs).
-
-This loops through the seed addresses specified in our Settings and
-tries to connect to them. The seed session either connects successfully,
-fails with an error or times out.
-
-If a seed node connects successfully, it runs a version exchange protocol,
-stores the channel in the p2p list of channels, and disconnects, removing
-the channel from the channel list.
-
-This is [run()](https://github.com/darkrenaissance/darkfi/blob/master/src/net/p2p.rs#L157):
-
-```
-pub async fn run(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
-    debug!(target: "net", "P2p::run() [BEGIN]");
-
-    *self.state.lock().await = P2pState::Run;
-
-    let manual = self.session_manual().await;
-    for peer in &self.settings.peers {
-        manual.clone().connect(peer, executor.clone()).await;
-    }
-
-    let inbound = self.session_inbound().await;
-    inbound.clone().start(executor.clone()).await?;
-
-    let outbound = self.session_outbound().await;
-    outbound.clone().start(executor.clone()).await?;
-
-    let stop_sub = self.subscribe_stop().await;
-    // Wait for stop signal
-    stop_sub.receive().await;
-
-    // Stop the sessions
-    manual.stop().await;
-    inbound.stop().await;
-    outbound.stop().await;
-
-    debug!(target: "net", "P2p::run() [END]");
-    Ok(())
-}
-```
-
-run() changes the P2pState to P2pState::Run. It then calls start()
-on manual, inbound and outbound sessions that are contained with the
-P2p struct. The outcome of start() will depend on how your node is
-configured. start() will try to run each kind of session, but if the
-configuration doesn't match attemping to start a session will simply
-return without doing anything. For example, if you are an outbound node,
-inbound.start() will return with the following message:
-
-```
-info!(target: "net", "Not configured for accepting incoming connections.");
-```
-
-run() then waits for a stop signal and shuts down the sessions when it
-is received.
-

+ 0 - 38
doc/src/learn/dchat/local-deployment/error-handling.md

@@ -1,38 +0,0 @@
-# Error handling 
-
-Before we continue, we need to quickly add some error handling to handle
-the case where a user forgets to add the command-line flag. We'll use a
-Box<dyn error::Error> to implement that. Because we are now defining our own
-Result type, we will need to remove `use darkfi::Result` from main.rs.
-
-```
-use std::{error, fmt};
-
-pub type Error = Box<dyn error::Error>;
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Debug, Clone)]
-pub struct MissingSpecifier;
-
-impl fmt::Display for MissingSpecifier {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "missing node specifier. you must specify either a or b")
-    }
-}
-
-impl error::Error for MissingSpecifier {}
-```
-
-Finally we can read the flag from the command-line by adding the following lines to main():
-
-```
-let settings: Result<Settings> = match std::env::args().nth(1) {
-    Some(id) => match id.as_str() {
-        "a" => alice(),
-        "b" => bob(),
-        _ => Err(MissingSpecifier.into()),
-    },
-    None => Err(MissingSpecifier.into()),
-};
-```
-

+ 0 - 43
doc/src/learn/dchat/local-deployment/getting-started.md

@@ -1,43 +0,0 @@
-# Getting started
-
-We'll create a new cargo directory and add DarkFi to our Cargo.toml,
-like so:
-
-```
-[package]
-name = "dchat"
-version = "0.1.0"
-edition = "2021"
-description = "Demo chat to document darkfi net code"
-
-[dependencies]
-darkfi = {path = "../../", features = ["net"]}
-```
-
-Be sure to replace the path to DarkFi with the correct path for your
-setup.
-
-Once that's done we can access DarkFi's net methods inside of
-dchat. We'll need a few more external libraries too, so add these
-dependencies:
-
-```
-# Async
-async-std = "1"
-async-trait = "0.1.56"
-async-executor = "1.4.1"
-async-channel = "1.6.1"
-easy-parallel = "3.2.0"
-smol = "1.2.5"
-num_cpus = "1.13.1"
-
-# Misc
-simplelog = "0.12.0"
-url = "2.2.2"
-
-# Encoding and parsing
-serde = {version = "1.0.138", features = ["derive"]}
-toml = "0.4.2"
-```
-
-

+ 0 - 31
doc/src/learn/dchat/local-deployment/inbound-and-outbound.md

@@ -1,31 +0,0 @@
-# Sessions
-
-To deploy the p2p network, we need to configure two types of nodes:
-inbound and outbound. These nodes perform different roles on the p2p
-network. An inbound node receives connections. An outbound node makes
-connections.
-
-The behavior of these nodes is defined in what is called a
-[Session](https://github.com/darkrenaissance/darkfi/blob/master/src/net/session/mod.rs#L93).
-There are four types of sessions: Manual, Inbound, Outbound and SeedSync.
-
-There behavior is as follows: 
-
-1. Inbound: Uses an Acceptor to accept connections on the inbound connect
-address configured in settings.
-
-2. Outbound: Starts a connect loop for every connect slot configured in
-settings. Establishes a connection using Connector.connect(): a method
-that takes an address returns a Channel.
-
-3. Manual: Uses a Connector to connect to a single address that is passed
-to ManualSession::connect(). Used to create an explicit connection to
-an address.
-
-4. SeedSync: Creates a connection to the seed nodes specified in settings.
-Loops through all the configured seeds and tries to connect to them
-using a Connector. Either connects successfully, fails with an error or
-times out.
-
-To create an inbound and outbound node, we will need to configure them
-using a type called net::Settings.

+ 0 - 75
doc/src/learn/dchat/local-deployment/seed-node.md

@@ -1,75 +0,0 @@
-# The seed node
-
-Let's create an instance of dchat inside our main function and pass the
-p2p network into it.  Then we'll add dchat::start() to our async loop
-in the main function. 
-
-```
-#[async_std::main]
-async fn main() -> Result<()> {
-    let settings: Result<Settings> = match std::env::args().nth(1) {
-        Some(id) => match id.as_str() {
-            "a" => alice(),
-            "b" => bob(),
-            _ => Err(MissingSpecifier.into()),
-        },
-        None => Err(MissingSpecifier.into()),
-    };
-
-    let p2p = net::P2p::new(settings?.into()).await;
-    let dchat = Dchat::new(p2p);
-
-    let nthreads = num_cpus::get();
-    let (signal, shutdown) = async_channel::unbounded::<()>();
-
-    let ex = Arc::new(Executor::new());
-    let ex2 = ex.clone();
-
-    let (_, result) = Parallel::new()
-        .each(0..nthreads, |_| {
-            smol::future::block_on(ex.run(shutdown.recv()))
-        })
-        .finish(|| {
-            smol::future::block_on(async move {
-                dchat.start(ex2).await?;
-                drop(signal);
-                Ok(())
-            })
-        });
-
-    result
-}
-```
-Now try to run the program, don't forget to add a specifier `a` or `b`
-to define the type of node.
-
-It should output the following error: 
-
-```
-Error: NetworkOperationFailed
-```
-
-That's because there is no seed node online for our nodes to connect to. A
-seed node is used when connecting to the network: it is a special kind
-of inbound node that gets connected to, sends over a list of addresses
-and disconnects again.  This behavior is defined in the ProtocolSeed.
-
-Everytime we run `p2p.start()` we attempt to connect to a seed using a
-SeedSyncSession.  If the SeedSyncSession fails, p2p.start() will fail,
-so without a seed node, our inbound and outbound nodes cannot establish
-a connection to the network. Let's remedy that.
-
-We have two options here. First, we could implement our own seed node.
-Alternatively, DarkFi maintains a master seed node called lilith that
-can act as the seed for many different protocols at the same time. For
-the purpose of this tutorial let's use lilith.
-
-What lilith does in the background is very simple. Just like any p2p
-daemon, a seed node defines its networks settings into a type called
-Settings and creates a new instance of the p2p network. It then runs
-p2p::start() and p2p::run(). The difference is in the settings: a seed
-node just specifies an inbound address which other nodes will connect to.
-
-Crucially, this inbound address must match the seed address we specified
-earlier in Alice and Bob's settings.
-

+ 0 - 101
doc/src/learn/dchat/local-deployment/settings.md

@@ -1,101 +0,0 @@
-# Settings
-
-On production-ready software, you would usually configure your node
-using a config file or command line inputs. On dchat we are keeping
-things ultra simple. We pass a command line flag that is either `a` or
-`b`. If we pass `a` we will initialize an inbound node. If we pass `b`
-we will initialize an outbound node.
-
-Here's how that works. We define two methods called alice() and
-bob(). alice() returns the Settings that will create an inbound
-node. bob() return the Settings for an outbound node.
-
-We also implement logging that outputs to /tmp/alice.log and /tmp/bob.log
-so we can access the debug output of our nodes. We store this info in a
-log file because we don't want it interfering with our terminal UI when
-we eventually build it.
-
-This is a function that returns the settings to create Alice, an
-inbound node:
-
-```
-use simplelog::WriteLogger;
-use std::fs::File;
-
-use darkfi::{net::Settings, Result};
-use url::Url;
-
-fn alice() -> Result<Settings> {
-    let log_level = simplelog::LevelFilter::Debug;
-    let log_config = simplelog::Config::default();
-
-    let log_path = "/tmp/alice.log";
-    let file = File::create(log_path).unwrap();
-    WriteLogger::init(log_level, log_config, file)?;
-
-    let seed = Url::parse("tcp://127.0.0.1:55555").unwrap();
-    let inbound = Url::parse("tcp://127.0.0.1:55554").unwrap();
-    let ext_addr = Url::parse("tcp://127.0.0.1:55554").unwrap();
-
-    let settings = Settings {
-        inbound: Some(inbound),
-        outbound_connections: 0,
-        manual_attempt_limit: 0,
-        seed_query_timeout_seconds: 8,
-        connect_timeout_seconds: 10,
-        channel_handshake_seconds: 4,
-        channel_heartbeat_seconds: 10,
-        outbound_retry_seconds: 1200,
-        external_addr: Some(ext_addr),
-        peers: Vec::new(),
-        seeds: vec![seed],
-        node_id: String::new(),
-    };
-
-    Ok(settings)
-}
-
-```
-
-This is a function that returns the settings to create Bob, an
-outbound node:
-
-```
-fn bob() -> Result<Settings> {
-    let log_level = simplelog::LevelFilter::Debug;
-    let log_config = simplelog::Config::default();
-
-    let log_path = "/tmp/bob.log";
-    let file = File::create(log_path).unwrap();
-    WriteLogger::init(log_level, log_config, file)?;
-    let seed = Url::parse("tcp://127.0.0.1:55555").unwrap();
-    let oc = 5;
-
-    let settings = Settings {
-        inbound: None,
-        outbound_connections: oc,
-        manual_attempt_limit: 0,
-        seed_query_timeout_seconds: 8,
-        connect_timeout_seconds: 10,
-        channel_handshake_seconds: 4,
-        channel_heartbeat_seconds: 10,
-        outbound_retry_seconds: 1200,
-        external_addr: None,
-        peers: Vec::new(),
-        seeds: vec![seed],
-        node_id: String::new(),
-    };
-
-    Ok(settings)
-}
-```
-
-Both outbound and inbound nodes specify a seed address to connect to. The
-inbound node also specifies an external address and an inbound address:
-this is where it will receive connections. The outbound node specifies
-the number of outbound connection slots, which is the number of outbound
-connections the node will try to make.
-
-These are the only settings we need to think about. For the rest, we
-use the network defaults.
-

+ 0 - 51
doc/src/learn/dchat/local-deployment/writing-a-daemon.md

@@ -1,51 +0,0 @@
-# Writing a daemon
-
-DarkFi consists of many seperate daemons communicating with each other. To
-run the p2p network, we'll need to implement our own daemon.  So we'll
-start building dchat by configuring our main function into a daemon that
-can run the p2p network.
-
-```
-use async_executor::Executor;
-use async_std::sync::Arc;
-use easy_parallel::Parallel;
-
-use std::fs::File;
-use simplelog::WriteLogger;
-
-use darkfi::Result;
-
-#[async_std::main]
-async fn main() -> Result<()> {
-    let nthreads = num_cpus::get();
-    let (signal, shutdown) = async_channel::unbounded::<()>();
-
-    let ex = Arc::new(Executor::new());
-    //let ex2 = ex.clone();
-
-    let (_, result) = Parallel::new()
-        .each(0..nthreads, |_| {
-            smol::future::block_on(ex.run(shutdown.recv()))
-        })
-        .finish(|| {
-            smol::future::block_on(async move {
-                // TODO
-                // dchat.start(ex2).await?;
-                drop(signal);
-                Ok(())
-            })
-        });
-
-    result
-}
-```
-
-We get the number of cpu cores using num_cpus::get() and spin up a bunch
-of threads in parallel using easy_parallel. For now it's commented out,
-but soon we'll run dchat inside this block.
-
-Note: DarkFi includes a macro called async_daemonize that is used by
-DarkFi binaries to minimize boilerplate in the repo.  To keep things
-simple we will ignore this macro for the purpose of this tutorial.  But
-check it out if you are curious: [util/cli.rs](https://github.com/darkrenaissance/darkfi/blob/master/src/util/cli.rs#L154).
-

+ 4 - 4
example/dchat/src/dchatmsg.rs

@@ -5,15 +5,15 @@ use darkfi::{
     util::serial::{SerialDecodable, SerialEncodable},
     util::serial::{SerialDecodable, SerialEncodable},
 };
 };
 
 
-pub type DchatmsgsBuffer = Arc<Mutex<Vec<Dchatmsg>>>;
+pub type DchatMsgsBuffer = Arc<Mutex<Vec<DchatMsg>>>;
 
 
-impl net::Message for Dchatmsg {
+impl net::Message for DchatMsg {
     fn name() -> &'static str {
     fn name() -> &'static str {
-        "Dchatmsg"
+        "DchatMsg"
     }
     }
 }
 }
 
 
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
-pub struct Dchatmsg {
+pub struct DchatMsg {
     pub msg: String,
     pub msg: String,
 }
 }

+ 4 - 3
example/dchat/src/error.rs

@@ -4,12 +4,13 @@ pub type Error = Box<dyn error::Error>;
 pub type Result<T> = std::result::Result<T, Error>;
 pub type Result<T> = std::result::Result<T, Error>;
 
 
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
-pub struct MissingSpecifier;
+pub struct ErrorMissingSpecifier;
 
 
-impl fmt::Display for MissingSpecifier {
+impl fmt::Display for ErrorMissingSpecifier {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "missing node specifier. you must specify either a or b")
         write!(f, "missing node specifier. you must specify either a or b")
     }
     }
 }
 }
 
 
-impl error::Error for MissingSpecifier {}
+impl error::Error for ErrorMissingSpecifier {}
+

+ 20 - 34
example/dchat/src/main.rs

@@ -2,7 +2,7 @@ use async_executor::Executor;
 use async_std::sync::{Arc, Mutex};
 use async_std::sync::{Arc, Mutex};
 use easy_parallel::Parallel;
 use easy_parallel::Parallel;
 
 
-use std::{fs::File, io::stdin};
+use std::{error, fs::File, io::stdin};
 
 
 use log::debug;
 use log::debug;
 use simplelog::WriteLogger;
 use simplelog::WriteLogger;
@@ -11,22 +11,25 @@ use url::Url;
 use darkfi::{net, net::Settings};
 use darkfi::{net, net::Settings};
 
 
 use crate::{
 use crate::{
-    dchatmsg::{Dchatmsg, DchatmsgsBuffer},
-    error::{MissingSpecifier, Result},
+    dchat_error::ErrorMissingSpecifier,
+    dchatmsg::{DchatMsg, DchatMsgsBuffer},
     protocol_dchat::ProtocolDchat,
     protocol_dchat::ProtocolDchat,
 };
 };
 
 
+pub mod dchat_error;
 pub mod dchatmsg;
 pub mod dchatmsg;
-pub mod error;
 pub mod protocol_dchat;
 pub mod protocol_dchat;
 
 
+pub type Error = Box<dyn error::Error>;
+pub type Result<T> = std::result::Result<T, Error>;
+
 struct Dchat {
 struct Dchat {
     p2p: net::P2pPtr,
     p2p: net::P2pPtr,
-    recv_msgs: DchatmsgsBuffer,
+    recv_msgs: DchatMsgsBuffer,
 }
 }
 
 
 impl Dchat {
 impl Dchat {
-    fn new(p2p: net::P2pPtr, recv_msgs: DchatmsgsBuffer) -> Self {
+    fn new(p2p: net::P2pPtr, recv_msgs: DchatMsgsBuffer) -> Self {
         Self { p2p, recv_msgs }
         Self { p2p, recv_msgs }
     }
     }
 
 
@@ -78,11 +81,11 @@ impl Dchat {
         }
         }
     }
     }
 
 
-    async fn register_protocol(&self, msgs: DchatmsgsBuffer) -> Result<()> {
+    async fn register_protocol(&self, msgs: DchatMsgsBuffer) -> Result<()> {
         debug!(target: "dchat", "Dchat::register_protocol() [START]");
         debug!(target: "dchat", "Dchat::register_protocol() [START]");
         let registry = self.p2p.protocol_registry();
         let registry = self.p2p.protocol_registry();
         registry
         registry
-            .register(net::SESSION_ALL, move |channel, _p2p| {
+            .register(!net::SESSION_SEED, move |channel, _p2p| {
                 let msgs2 = msgs.clone();
                 let msgs2 = msgs.clone();
                 async move { ProtocolDchat::init(channel, msgs2).await }
                 async move { ProtocolDchat::init(channel, msgs2).await }
             })
             })
@@ -102,18 +105,19 @@ impl Dchat {
 
 
         self.menu().await?;
         self.menu().await?;
 
 
+        self.p2p.stop().await;
+
         debug!(target: "dchat", "Dchat::start() [STOP]");
         debug!(target: "dchat", "Dchat::start() [STOP]");
         Ok(())
         Ok(())
     }
     }
 
 
     async fn send(&self, msg: String) -> Result<()> {
     async fn send(&self, msg: String) -> Result<()> {
-        let dchatmsg = Dchatmsg { msg };
+        let dchatmsg = DchatMsg { msg };
         self.p2p.broadcast(dchatmsg).await?;
         self.p2p.broadcast(dchatmsg).await?;
         Ok(())
         Ok(())
     }
     }
 }
 }
 
 
-// inbound
 fn alice() -> Result<Settings> {
 fn alice() -> Result<Settings> {
     let log_level = simplelog::LevelFilter::Debug;
     let log_level = simplelog::LevelFilter::Debug;
     let log_config = simplelog::Config::default();
     let log_config = simplelog::Config::default();
@@ -128,23 +132,14 @@ fn alice() -> Result<Settings> {
 
 
     let settings = Settings {
     let settings = Settings {
         inbound: Some(inbound),
         inbound: Some(inbound),
-        outbound_connections: 0,
-        manual_attempt_limit: 0,
-        seed_query_timeout_seconds: 8,
-        connect_timeout_seconds: 10,
-        channel_handshake_seconds: 4,
-        channel_heartbeat_seconds: 10,
-        outbound_retry_seconds: 1200,
         external_addr: Some(ext_addr),
         external_addr: Some(ext_addr),
-        peers: Vec::new(),
         seeds: vec![seed],
         seeds: vec![seed],
-        node_id: String::new(),
+        ..Default::default()
     };
     };
 
 
     Ok(settings)
     Ok(settings)
 }
 }
 
 
-// outbound
 fn bob() -> Result<Settings> {
 fn bob() -> Result<Settings> {
     let log_level = simplelog::LevelFilter::Debug;
     let log_level = simplelog::LevelFilter::Debug;
     let log_config = simplelog::Config::default();
     let log_config = simplelog::Config::default();
@@ -154,21 +149,12 @@ fn bob() -> Result<Settings> {
     WriteLogger::init(log_level, log_config, file)?;
     WriteLogger::init(log_level, log_config, file)?;
 
 
     let seed = Url::parse("tcp://127.0.0.1:55555").unwrap();
     let seed = Url::parse("tcp://127.0.0.1:55555").unwrap();
-    let oc = 5;
 
 
     let settings = Settings {
     let settings = Settings {
         inbound: None,
         inbound: None,
-        outbound_connections: oc,
-        manual_attempt_limit: 0,
-        seed_query_timeout_seconds: 8,
-        connect_timeout_seconds: 10,
-        channel_handshake_seconds: 4,
-        channel_heartbeat_seconds: 10,
-        outbound_retry_seconds: 1200,
-        external_addr: None,
-        peers: Vec::new(),
+        outbound_connections: 5,
         seeds: vec![seed],
         seeds: vec![seed],
-        node_id: String::new(),
+        ..Default::default()
     };
     };
 
 
     Ok(settings)
     Ok(settings)
@@ -180,9 +166,9 @@ async fn main() -> Result<()> {
         Some(id) => match id.as_str() {
         Some(id) => match id.as_str() {
             "a" => alice(),
             "a" => alice(),
             "b" => bob(),
             "b" => bob(),
-            _ => Err(MissingSpecifier.into()),
+            _ => Err(ErrorMissingSpecifier.into()),
         },
         },
-        None => Err(MissingSpecifier.into()),
+        None => Err(ErrorMissingSpecifier.into()),
     };
     };
 
 
     let p2p = net::P2p::new(settings?.into()).await;
     let p2p = net::P2p::new(settings?.into()).await;
@@ -193,7 +179,7 @@ async fn main() -> Result<()> {
     let ex = Arc::new(Executor::new());
     let ex = Arc::new(Executor::new());
     let ex2 = ex.clone();
     let ex2 = ex.clone();
 
 
-    let msgs: DchatmsgsBuffer = Arc::new(Mutex::new(vec![Dchatmsg { msg: String::new() }]));
+    let msgs: DchatMsgsBuffer = Arc::new(Mutex::new(vec![DchatMsg { msg: String::new() }]));
 
 
     let mut dchat = Dchat::new(p2p, msgs);
     let mut dchat = Dchat::new(p2p, msgs);
 
 

+ 6 - 6
example/dchat/src/protocol_dchat.rs

@@ -4,22 +4,22 @@ use async_trait::async_trait;
 use darkfi::{net, Result};
 use darkfi::{net, Result};
 use log::debug;
 use log::debug;
 
 
-use crate::dchatmsg::{Dchatmsg, DchatmsgsBuffer};
+use crate::dchatmsg::{DchatMsg, DchatMsgsBuffer};
 
 
 pub struct ProtocolDchat {
 pub struct ProtocolDchat {
     jobsman: net::ProtocolJobsManagerPtr,
     jobsman: net::ProtocolJobsManagerPtr,
-    msg_sub: net::MessageSubscription<Dchatmsg>,
-    msgs: DchatmsgsBuffer,
+    msg_sub: net::MessageSubscription<DchatMsg>,
+    msgs: DchatMsgsBuffer,
 }
 }
 
 
 impl ProtocolDchat {
 impl ProtocolDchat {
-    pub async fn init(channel: net::ChannelPtr, msgs: DchatmsgsBuffer) -> net::ProtocolBasePtr {
+    pub async fn init(channel: net::ChannelPtr, msgs: DchatMsgsBuffer) -> net::ProtocolBasePtr {
         debug!(target: "dchat", "ProtocolDchat::init() [START]");
         debug!(target: "dchat", "ProtocolDchat::init() [START]");
         let message_subsytem = channel.get_message_subsystem();
         let message_subsytem = channel.get_message_subsystem();
-        message_subsytem.add_dispatch::<Dchatmsg>().await;
+        message_subsytem.add_dispatch::<DchatMsg>().await;
 
 
         let msg_sub =
         let msg_sub =
-            channel.subscribe_msg::<Dchatmsg>().await.expect("Missing DchatMsg dispatcher!");
+            channel.subscribe_msg::<DchatMsg>().await.expect("Missing DchatMsg dispatcher!");
 
 
         Arc::new(Self {
         Arc::new(Self {
             jobsman: net::ProtocolJobsManager::new("ProtocolDchat", channel.clone()),
             jobsman: net::ProtocolJobsManager::new("ProtocolDchat", channel.clone()),