From 4f2d6b5403be3e8af1b8389c7f535fd54e60d058 Mon Sep 17 00:00:00 2001
From: librelois <elois@ifee.fr>
Date: Mon, 10 Jun 2019 20:15:55 +0200
Subject: [PATCH] [ref] documents+network-documents: create DomainName type

---
 Cargo.lock                                    | 53 +++++------
 lib/modules/ws2p/ws2p-messages/lib.rs         |  4 +-
 lib/tools/documents/Cargo.toml                |  2 +-
 .../documents/src/documents/certification.rs  | 11 ++-
 lib/tools/documents/src/documents/identity.rs | 31 +++----
 .../documents/src/documents/membership.rs     | 33 +++----
 lib/tools/documents/src/documents/mod.rs      | 36 ++++----
 .../documents/src/documents/revocation.rs     | 30 +++----
 .../documents/src/documents/transaction.rs    | 31 +++----
 lib/tools/documents/src/lib.rs                | 49 +++++++++--
 lib/tools/network-documents/Cargo.toml        |  7 +-
 lib/tools/network-documents/src/host.rs       | 84 ++++++++++++++++++
 lib/tools/network-documents/src/lib.rs        | 28 +++---
 .../src/network_documents.pest                | 14 +--
 .../network-documents/src/network_endpoint.rs | 87 +++++++++----------
 .../network-documents/src/network_head_v3.rs  | 17 ++--
 .../network-documents/src/network_peer.rs     | 21 ++---
 17 files changed, 318 insertions(+), 220 deletions(-)
 create mode 100644 lib/tools/network-documents/src/host.rs

diff --git a/Cargo.lock b/Cargo.lock
index 89d050f6..468faed2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -291,7 +291,7 @@ dependencies = [
 
 [[package]]
 name = "dubp-documents"
-version = "0.12.0"
+version = "0.13.0"
 dependencies = [
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
@@ -312,7 +312,7 @@ dependencies = [
 name = "dubp-documents-tests-tools"
 version = "0.1.0"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-crypto-tests-tools 0.1.0",
  "dup-currency-params 0.1.0",
@@ -372,7 +372,7 @@ name = "durs-blockchain"
 version = "0.2.0-a"
 dependencies = [
  "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dubp-documents-tests-tools 0.1.0",
  "dup-crypto 0.6.0",
  "dup-crypto-tests-tools 0.1.0",
@@ -383,7 +383,7 @@ dependencies = [
  "durs-message 0.2.0-a",
  "durs-module 0.2.0-a",
  "durs-network 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "durs-wot 0.8.0-a0.9",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "json-pest-parser 0.1.0",
@@ -401,7 +401,7 @@ dependencies = [
 name = "durs-blockchain-dal"
 version = "0.2.0-a"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dubp-documents-tests-tools 0.1.0",
  "dup-crypto 0.6.0",
  "dup-crypto-tests-tools 0.1.0",
@@ -441,7 +441,7 @@ name = "durs-conf"
 version = "0.2.0-a"
 dependencies = [
  "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
@@ -468,7 +468,7 @@ dependencies = [
  "durs-message 0.2.0-a",
  "durs-module 0.2.0-a",
  "durs-network 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -484,13 +484,13 @@ dependencies = [
 name = "durs-message"
 version = "0.2.0-a"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-blockchain-dal 0.2.0-a",
  "durs-module 0.2.0-a",
  "durs-network 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -500,11 +500,11 @@ dependencies = [
 name = "durs-module"
 version = "0.2.0-a"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -517,11 +517,11 @@ dependencies = [
 name = "durs-network"
 version = "0.2.0-a"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "durs-common-tools 0.1.0",
  "durs-module 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -531,14 +531,15 @@ dependencies = [
 
 [[package]]
 name = "durs-network-documents"
-version = "0.3.1"
+version = "0.4.0"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
+ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -553,7 +554,7 @@ dependencies = [
 name = "durs-skeleton"
 version = "0.1.0"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
@@ -573,7 +574,7 @@ dependencies = [
 name = "durs-tui"
 version = "0.2.0-a"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
@@ -581,7 +582,7 @@ dependencies = [
  "durs-message 0.2.0-a",
  "durs-module 0.2.0-a",
  "durs-network 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -609,7 +610,7 @@ name = "durs-ws2p"
 version = "0.2.0-a"
 dependencies = [
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tests-tools 0.1.0",
@@ -618,7 +619,7 @@ dependencies = [
  "durs-message 0.2.0-a",
  "durs-module 0.2.0-a",
  "durs-network 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "durs-ws2p-messages 0.2.0-a",
  "durs-ws2p-protocol 0.2.0-a",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -637,11 +638,11 @@ version = "0.2.0-a"
 dependencies = [
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -653,12 +654,12 @@ dependencies = [
 name = "durs-ws2p-protocol"
 version = "0.2.0-a"
 dependencies = [
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
  "durs-module 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "durs-ws2p-messages 0.2.0-a",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -674,7 +675,7 @@ version = "0.2.0-a"
 dependencies = [
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "dubp-documents 0.12.0",
+ "dubp-documents 0.13.0",
  "dup-crypto 0.6.0",
  "dup-currency-params 0.1.0",
  "durs-common-tools 0.1.0",
@@ -682,7 +683,7 @@ dependencies = [
  "durs-message 0.2.0-a",
  "durs-module 0.2.0-a",
  "durs-network 0.2.0-a",
- "durs-network-documents 0.3.1",
+ "durs-network-documents 0.4.0",
  "durs-wot 0.8.0-a0.9",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/lib/modules/ws2p/ws2p-messages/lib.rs b/lib/modules/ws2p/ws2p-messages/lib.rs
index 0daad343..b10880e1 100644
--- a/lib/modules/ws2p/ws2p-messages/lib.rs
+++ b/lib/modules/ws2p/ws2p-messages/lib.rs
@@ -198,7 +198,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: None,
-            host: Some(String::from("g1.durs.ifee.fr")),
+            domain: Some(String::from("g1.durs.ifee.fr")),
             port: 443u16,
             path: Some(String::from("ws2p")),
         }
@@ -211,7 +211,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
             ip_v6: None,
-            host: None,
+            domain: None,
             port: 443u16,
             path: Some(String::from("ws2p")),
         }
diff --git a/lib/tools/documents/Cargo.toml b/lib/tools/documents/Cargo.toml
index 3e9ca1d8..a4a86ad5 100644
--- a/lib/tools/documents/Cargo.toml
+++ b/lib/tools/documents/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "dubp-documents"
-version = "0.12.0"
+version = "0.13.0"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@ifee.fr>"]
 description = "Handles DUBP documents (DUBP: DUniter Blockhain Protocol)"
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
diff --git a/lib/tools/documents/src/documents/certification.rs b/lib/tools/documents/src/documents/certification.rs
index e93219a6..b853ade4 100644
--- a/lib/tools/documents/src/documents/certification.rs
+++ b/lib/tools/documents/src/documents/certification.rs
@@ -296,7 +296,9 @@ impl TextDocumentParser<Rule> for CertificationDocumentParser {
                 let cert_vx_pair = cert_pair.into_inner().next().unwrap(); // get and unwrap the `cert_vX` rule; never fails
 
                 match cert_vx_pair.as_rule() {
-                    Rule::cert_v10 => Ok(CertificationDocumentParser::from_pest_pair(cert_vx_pair)),
+                    Rule::cert_v10 => {
+                        Ok(CertificationDocumentParser::from_pest_pair(cert_vx_pair)?)
+                    }
                     _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
                         "{:#?}",
                         cert_vx_pair.as_rule()
@@ -306,7 +308,7 @@ impl TextDocumentParser<Rule> for CertificationDocumentParser {
             Err(pest_error) => fatal_error!("{}", pest_error), //Err(TextDocumentParseError::PestError()),
         }
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
         let doc = pair.as_str();
         let mut currency = "";
         let mut pubkeys = Vec::with_capacity(2);
@@ -341,7 +343,8 @@ impl TextDocumentParser<Rule> for CertificationDocumentParser {
                 _ => fatal_error!("unexpected rule"), // Grammar ensures that we never reach this line
             }
         }
-        CertificationDocument {
+
+        Ok(CertificationDocument {
             text: doc.to_owned(),
             issuers: vec![pubkeys[0]],
             currency: currency.to_owned(),
@@ -351,7 +354,7 @@ impl TextDocumentParser<Rule> for CertificationDocumentParser {
             identity_sig: sigs[0],
             blockstamp: blockstamps[1],
             signatures: vec![sigs[1]],
-        }
+        })
     }
 }
 
diff --git a/lib/tools/documents/src/documents/identity.rs b/lib/tools/documents/src/documents/identity.rs
index 0922e7cd..15cb0135 100644
--- a/lib/tools/documents/src/documents/identity.rs
+++ b/lib/tools/documents/src/documents/identity.rs
@@ -233,23 +233,19 @@ impl TextDocumentParser<Rule> for IdentityDocumentParser {
     type DocumentType = IdentityDocument;
 
     fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
-        match DocumentsParser::parse(Rule::idty, doc) {
-            Ok(mut doc_pairs) => {
-                let idty_pair = doc_pairs.next().unwrap(); // get and unwrap the `idty` rule; never fails
-                let idty_vx_pair = idty_pair.into_inner().next().unwrap(); // get and unwrap the `idty_vx` rule; never fails
-
-                match idty_vx_pair.as_rule() {
-                    Rule::idty_v10 => Ok(IdentityDocumentParser::from_pest_pair(idty_vx_pair)),
-                    _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
-                        "{:#?}",
-                        idty_vx_pair.as_rule()
-                    ))),
-                }
-            }
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        let mut doc_pairs = DocumentsParser::parse(Rule::idty, doc)?;
+        let idty_pair = doc_pairs.next().unwrap(); // get and unwrap the `idty` rule; never fails
+        let idty_vx_pair = idty_pair.into_inner().next().unwrap(); // get and unwrap the `idty_vx` rule; never fails
+
+        match idty_vx_pair.as_rule() {
+            Rule::idty_v10 => Ok(IdentityDocumentParser::from_pest_pair(idty_vx_pair)?),
+            _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                "{:#?}",
+                idty_vx_pair.as_rule()
+            ))),
         }
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
         let doc = pair.as_str();
         let mut currency = "";
         let mut pubkey_str = "";
@@ -276,7 +272,8 @@ impl TextDocumentParser<Rule> for IdentityDocumentParser {
                 _ => fatal_error!("unexpected rule"), // Grammar ensures that we never reach this line
             }
         }
-        IdentityDocument {
+
+        Ok(IdentityDocument {
             text: Some(doc.to_owned()),
             currency: currency.to_owned(),
             username: uid.to_owned(),
@@ -287,7 +284,7 @@ impl TextDocumentParser<Rule> for IdentityDocumentParser {
             signatures: vec![Sig::Ed25519(
                 ed25519::Signature::from_base64(sig_str).unwrap(),
             )], // Grammar ensures that we have a base64 string.
-        }
+        })
     }
 }
 
diff --git a/lib/tools/documents/src/documents/membership.rs b/lib/tools/documents/src/documents/membership.rs
index facdd9b7..528772f5 100644
--- a/lib/tools/documents/src/documents/membership.rs
+++ b/lib/tools/documents/src/documents/membership.rs
@@ -291,25 +291,19 @@ impl TextDocumentParser<Rule> for MembershipDocumentParser {
     type DocumentType = MembershipDocument;
 
     fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
-        match DocumentsParser::parse(Rule::membership, doc) {
-            Ok(mut ms_pairs) => {
-                let ms_pair = ms_pairs.next().unwrap(); // get and unwrap the `membership` rule; never fails
-                let ms_vx_pair = ms_pair.into_inner().next().unwrap(); // get and unwrap the `membership_vX` rule; never fails
-
-                match ms_vx_pair.as_rule() {
-                    Rule::membership_v10 => {
-                        Ok(MembershipDocumentParser::from_pest_pair(ms_vx_pair))
-                    }
-                    _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
-                        "{:#?}",
-                        ms_vx_pair.as_rule()
-                    ))),
-                }
-            }
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        let mut ms_pairs = DocumentsParser::parse(Rule::membership, doc)?;
+        let ms_pair = ms_pairs.next().unwrap(); // get and unwrap the `membership` rule; never fails
+        let ms_vx_pair = ms_pair.into_inner().next().unwrap(); // get and unwrap the `membership_vX` rule; never fails
+
+        match ms_vx_pair.as_rule() {
+            Rule::membership_v10 => Ok(MembershipDocumentParser::from_pest_pair(ms_vx_pair)?),
+            _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                "{:#?}",
+                ms_vx_pair.as_rule()
+            ))),
         }
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
         let doc = pair.as_str();
         let mut currency = "";
         let mut pubkey_str = "";
@@ -339,7 +333,8 @@ impl TextDocumentParser<Rule> for MembershipDocumentParser {
                 _ => fatal_error!("unexpected rule"), // Grammar ensures that we never reach this line
             }
         }
-        MembershipDocument {
+
+        Ok(MembershipDocument {
             text: Some(doc.to_owned()),
             issuers: vec![PubKey::Ed25519(
                 ed25519::PublicKey::from_base58(pubkey_str).unwrap(),
@@ -352,7 +347,7 @@ impl TextDocumentParser<Rule> for MembershipDocumentParser {
             signatures: vec![Sig::Ed25519(
                 ed25519::Signature::from_base64(sig_str).unwrap(),
             )], // Grammar ensures that we have a base64 string.
-        }
+        })
     }
 }
 
diff --git a/lib/tools/documents/src/documents/mod.rs b/lib/tools/documents/src/documents/mod.rs
index 4df0213c..0234ceef 100644
--- a/lib/tools/documents/src/documents/mod.rs
+++ b/lib/tools/documents/src/documents/mod.rs
@@ -107,40 +107,40 @@ impl TextDocumentParser<Rule> for DUBPDocument {
 
     fn parse(doc: &str) -> Result<DUBPDocument, TextDocumentParseError> {
         match DocumentsParser::parse(Rule::document, doc) {
-            Ok(mut doc_pairs) => Ok(DUBPDocument::from_pest_pair(doc_pairs.next().unwrap())), // get and unwrap the `document` rule; never fails
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+            Ok(mut doc_pairs) => Ok(DUBPDocument::from_pest_pair(doc_pairs.next().unwrap())?), // get and unwrap the `document` rule; never fails
+            Err(pest_error) => Err(pest_error.into()),
         }
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
         let doc_vx_pair = pair.into_inner().next().unwrap(); // get and unwrap the `document_vX` rule; never fails
 
         match doc_vx_pair.as_rule() {
-            Rule::document_v10 => DUBPDocument::from_pest_pair_v10(doc_vx_pair),
+            Rule::document_v10 => Ok(DUBPDocument::from_pest_pair_v10(doc_vx_pair)?),
             _ => fatal_error!("unexpected rule: {:?}", doc_vx_pair.as_rule()), // Grammar ensures that we never reach this line
         }
     }
 }
 
 impl DUBPDocument {
-    pub fn from_pest_pair_v10(pair: Pair<Rule>) -> DUBPDocument {
+    pub fn from_pest_pair_v10(pair: Pair<Rule>) -> Result<DUBPDocument, TextDocumentParseError> {
         let doc_type_v10_pair = pair.into_inner().next().unwrap(); // get and unwrap the `{DOC_TYPE}_v10` rule; never fails
 
         match doc_type_v10_pair.as_rule() {
-            Rule::idty_v10 => DUBPDocument::Identity(
-                identity::IdentityDocumentParser::from_pest_pair(doc_type_v10_pair),
-            ),
-            Rule::membership_v10 => DUBPDocument::Membership(
-                membership::MembershipDocumentParser::from_pest_pair(doc_type_v10_pair),
-            ),
-            Rule::cert_v10 => DUBPDocument::Certification(Box::new(
-                certification::CertificationDocumentParser::from_pest_pair(doc_type_v10_pair),
+            Rule::idty_v10 => Ok(DUBPDocument::Identity(
+                identity::IdentityDocumentParser::from_pest_pair(doc_type_v10_pair)?,
             )),
-            Rule::revoc_v10 => DUBPDocument::Revocation(Box::new(
-                revocation::RevocationDocumentParser::from_pest_pair(doc_type_v10_pair),
-            )),
-            Rule::tx_v10 => DUBPDocument::Transaction(Box::new(
-                transaction::TransactionDocumentParser::from_pest_pair(doc_type_v10_pair),
+            Rule::membership_v10 => Ok(DUBPDocument::Membership(
+                membership::MembershipDocumentParser::from_pest_pair(doc_type_v10_pair)?,
             )),
+            Rule::cert_v10 => Ok(DUBPDocument::Certification(Box::new(
+                certification::CertificationDocumentParser::from_pest_pair(doc_type_v10_pair)?,
+            ))),
+            Rule::revoc_v10 => Ok(DUBPDocument::Revocation(Box::new(
+                revocation::RevocationDocumentParser::from_pest_pair(doc_type_v10_pair)?,
+            ))),
+            Rule::tx_v10 => Ok(DUBPDocument::Transaction(Box::new(
+                transaction::TransactionDocumentParser::from_pest_pair(doc_type_v10_pair)?,
+            ))),
             _ => fatal_error!("unexpected rule: {:?}", doc_type_v10_pair.as_rule()), // Grammar ensures that we never reach this line
         }
     }
diff --git a/lib/tools/documents/src/documents/revocation.rs b/lib/tools/documents/src/documents/revocation.rs
index 749aee4b..37ee248f 100644
--- a/lib/tools/documents/src/documents/revocation.rs
+++ b/lib/tools/documents/src/documents/revocation.rs
@@ -246,23 +246,19 @@ impl TextDocumentParser<Rule> for RevocationDocumentParser {
     type DocumentType = RevocationDocument;
 
     fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
-        match DocumentsParser::parse(Rule::revoc, doc) {
-            Ok(mut revoc_pairs) => {
-                let revoc_pair = revoc_pairs.next().unwrap(); // get and unwrap the `revoc` rule; never fails
-                let revoc_vx_pair = revoc_pair.into_inner().next().unwrap(); // get and unwrap the `revoc_vX` rule; never fails
-
-                match revoc_vx_pair.as_rule() {
-                    Rule::revoc_v10 => Ok(RevocationDocumentParser::from_pest_pair(revoc_vx_pair)),
-                    _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
-                        "{:#?}",
-                        revoc_vx_pair.as_rule()
-                    ))),
-                }
-            }
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        let mut revoc_pairs = DocumentsParser::parse(Rule::revoc, doc)?;
+        let revoc_pair = revoc_pairs.next().unwrap(); // get and unwrap the `revoc` rule; never fails
+        let revoc_vx_pair = revoc_pair.into_inner().next().unwrap(); // get and unwrap the `revoc_vX` rule; never fails
+
+        match revoc_vx_pair.as_rule() {
+            Rule::revoc_v10 => Ok(RevocationDocumentParser::from_pest_pair(revoc_vx_pair)?),
+            _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                "{:#?}",
+                revoc_vx_pair.as_rule()
+            ))),
         }
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
         let doc = pair.as_str();
         let mut currency = "";
         let mut pubkeys = Vec::with_capacity(1);
@@ -297,7 +293,7 @@ impl TextDocumentParser<Rule> for RevocationDocumentParser {
                 _ => fatal_error!("unexpected rule"), // Grammar ensures that we never reach this line
             }
         }
-        RevocationDocument {
+        Ok(RevocationDocument {
             text: doc.to_owned(),
             issuers: vec![pubkeys[0]],
             currency: currency.to_owned(),
@@ -305,7 +301,7 @@ impl TextDocumentParser<Rule> for RevocationDocumentParser {
             identity_blockstamp: blockstamps[0],
             identity_sig: sigs[0],
             signatures: vec![sigs[1]],
-        }
+        })
     }
 }
 
diff --git a/lib/tools/documents/src/documents/transaction.rs b/lib/tools/documents/src/documents/transaction.rs
index 388f5077..3f237bed 100644
--- a/lib/tools/documents/src/documents/transaction.rs
+++ b/lib/tools/documents/src/documents/transaction.rs
@@ -855,23 +855,19 @@ impl TextDocumentParser<Rule> for TransactionDocumentParser {
     type DocumentType = TransactionDocument;
 
     fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
-        match DocumentsParser::parse(Rule::tx, doc) {
-            Ok(mut tx_pairs) => {
-                let tx_pair = tx_pairs.next().unwrap(); // get and unwrap the `tx` rule; never fails
-                let tx_vx_pair = tx_pair.into_inner().next().unwrap(); // get and unwrap the `tx_vX` rule; never fails
-
-                match tx_vx_pair.as_rule() {
-                    Rule::tx_v10 => Ok(TransactionDocumentParser::from_pest_pair(tx_vx_pair)),
-                    _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
-                        "{:#?}",
-                        tx_vx_pair.as_rule()
-                    ))),
-                }
-            }
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        let mut tx_pairs = DocumentsParser::parse(Rule::tx, doc)?;
+        let tx_pair = tx_pairs.next().unwrap(); // get and unwrap the `tx` rule; never fails
+        let tx_vx_pair = tx_pair.into_inner().next().unwrap(); // get and unwrap the `tx_vX` rule; never fails
+
+        match tx_vx_pair.as_rule() {
+            Rule::tx_v10 => Ok(TransactionDocumentParser::from_pest_pair(tx_vx_pair)?),
+            _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                "{:#?}",
+                tx_vx_pair.as_rule()
+            ))),
         }
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
         let doc = pair.as_str();
         let mut currency = "";
         let mut blockstamp = Blockstamp::default();
@@ -917,7 +913,8 @@ impl TextDocumentParser<Rule> for TransactionDocumentParser {
                 _ => fatal_error!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
             }
         }
-        TransactionDocument {
+
+        Ok(TransactionDocument {
             text: Some(doc.to_owned()),
             currency: currency.to_owned(),
             blockstamp,
@@ -929,7 +926,7 @@ impl TextDocumentParser<Rule> for TransactionDocumentParser {
             comment: comment.to_owned(),
             signatures: sigs,
             hash: None,
-        }
+        })
     }
 }
 
diff --git a/lib/tools/documents/src/lib.rs b/lib/tools/documents/src/lib.rs
index 2f805603..282c50f6 100644
--- a/lib/tools/documents/src/lib.rs
+++ b/lib/tools/documents/src/lib.rs
@@ -49,6 +49,7 @@ use pest::RuleType;
 use serde::Serialize;
 use std::cmp::Ordering;
 use std::fmt::{Debug, Display, Error, Formatter};
+use std::net::AddrParseError;
 
 pub use crate::blockstamp::{Blockstamp, PreviousBlockstamp};
 
@@ -64,26 +65,58 @@ pub trait TextDocumentParser<R: RuleType> {
     /// Parse text document from raw format
     fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError>;
     /// Parse text document from pest pairs
-    fn from_pest_pair(pairs: Pair<R>) -> Self::DocumentType;
+    fn from_pest_pair(pairs: Pair<R>) -> Result<Self::DocumentType, TextDocumentParseError>;
 }
 
-/// List of possible errors while parsing.
-#[derive(Debug, Clone, Fail)]
+/// Error with pest parser (grammar)
+#[derive(Debug, Clone, Eq, Fail, PartialEq)]
+#[fail(display = "Grammar error: {}", _0)]
+pub struct PestError(pub String);
+
+impl<T: pest::RuleType> From<pest::error::Error<T>> for PestError {
+    fn from(e: pest::error::Error<T>) -> Self {
+        PestError(format!("{}", e))
+    }
+}
+
+/// List of possible errors while parsing a text document.
+#[derive(Debug, Clone, Eq, Fail, PartialEq)]
 pub enum TextDocumentParseError {
     /// The given source don't have a valid specific document format (document type).
     #[fail(display = "TextDocumentParseError: Invalid inner format: {}", _0)]
     InvalidInnerFormat(String),
+    /// Ip address parse error
+    #[fail(display = "TextDocumentParseError: invalid ip: {}", _0)]
+    IpAddrError(AddrParseError),
     /// Error with pest parser
-    #[fail(display = "TextDocumentParseError: PestError.")]
-    PestError(String),
-    #[fail(display = "TextDocumentParseError: UnexpectedVersion.")]
-    /// UnexpectedVersion
+    #[fail(display = "TextDocumentParseError: {}", _0)]
+    PestError(PestError),
+    /// Unexpected version
+    #[fail(display = "TextDocumentParseError: UnexpectedVersion: {}", _0)]
     UnexpectedVersion(String),
-    #[fail(display = "TextDocumentParseError: UnknownType.")]
     /// Unknown type
+    #[fail(display = "TextDocumentParseError: UnknownType.")]
     UnknownType,
 }
 
+impl From<AddrParseError> for TextDocumentParseError {
+    fn from(e: AddrParseError) -> Self {
+        TextDocumentParseError::IpAddrError(e)
+    }
+}
+
+impl From<PestError> for TextDocumentParseError {
+    fn from(e: PestError) -> Self {
+        TextDocumentParseError::PestError(e)
+    }
+}
+
+impl<T: pest::RuleType> From<pest::error::Error<T>> for TextDocumentParseError {
+    fn from(e: pest::error::Error<T>) -> Self {
+        TextDocumentParseError::PestError(e.into())
+    }
+}
+
 /// A block Id.
 #[derive(Copy, Clone, Debug, Deserialize, Ord, PartialEq, PartialOrd, Eq, Hash, Serialize)]
 pub struct BlockNumber(pub u32);
diff --git a/lib/tools/network-documents/Cargo.toml b/lib/tools/network-documents/Cargo.toml
index 6dca76c5..281135fd 100644
--- a/lib/tools/network-documents/Cargo.toml
+++ b/lib/tools/network-documents/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "durs-network-documents"
-version = "0.3.1"
+version = "0.4.0"
 authors = ["librelois <elois@duniter.org>"]
 description = "Network documents for DUNP (DUniter Network Protocol)"
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
@@ -12,13 +12,14 @@ edition = "2018"
 path = "src/lib.rs"
 
 [dependencies]
-log = "0.4.*"
-durs-common-tools = { path = "../common-tools" }
 base58 = "0.1.*"
+durs-common-tools = { path = "../common-tools" }
 dup-crypto = { path = "../crypto" }
 dup-currency-params = { path = "../../tools/currency-params" }
 dubp-documents= { path = "../documents" }
+failure = "0.1.5"
 hex = "0.3.*"
+log = "0.4.*"
 pest = "2.1.0"
 pest_derive = "2.1.0"
 serde = { version = "1.0.*", features = ["derive"] }
diff --git a/lib/tools/network-documents/src/host.rs b/lib/tools/network-documents/src/host.rs
new file mode 100644
index 00000000..f660c729
--- /dev/null
+++ b/lib/tools/network-documents/src/host.rs
@@ -0,0 +1,84 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Define host type
+
+use crate::*;
+use dubp_documents::PestError;
+use failure::Fail;
+use pest::Parser;
+use std::net::{AddrParseError, IpAddr};
+use std::str::FromStr;
+
+#[derive(Clone, Debug, Hash)]
+/// Domain name
+pub struct DomainName(String);
+
+impl FromStr for DomainName {
+    type Err = PestError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match NetworkDocsParser::parse(Rule::domain_name_inner, s) {
+            Ok(pairs) => Ok(DomainName(String::from(pairs.as_str()))),
+            Err(pest_error) => Err(PestError(format!("{}", pest_error))),
+        }
+    }
+}
+
+impl AsRef<str> for DomainName {
+    fn as_ref(&self) -> &str {
+        &self.0
+    }
+}
+
+impl ToString for DomainName {
+    fn to_string(&self) -> String {
+        self.0.clone()
+    }
+}
+
+#[derive(Clone, Debug, Hash)]
+/// Host
+pub enum Host {
+    /// Domain name
+    DomainName(DomainName),
+    // Ip address
+    Ip(IpAddr),
+}
+
+#[derive(Clone, Debug, Fail)]
+#[fail(
+    display = "Fail to parse host: It's neither a valid ip address nor a valid domain name. IP error: {}. Domain name error: {}.",
+    ip_err, domain_err
+)]
+/// Host parse error
+pub struct HostParseError {
+    ip_err: AddrParseError,
+    domain_err: PestError,
+}
+
+impl FromStr for Host {
+    type Err = HostParseError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match IpAddr::from_str(s) {
+            Ok(ip_addr) => Ok(Host::Ip(ip_addr)),
+            Err(ip_err) => match DomainName::from_str(s) {
+                Ok(domain_name) => Ok(Host::DomainName(domain_name)),
+                Err(domain_err) => Err(HostParseError { ip_err, domain_err }),
+            },
+        }
+    }
+}
diff --git a/lib/tools/network-documents/src/lib.rs b/lib/tools/network-documents/src/lib.rs
index 19d933a5..93c82e38 100644
--- a/lib/tools/network-documents/src/lib.rs
+++ b/lib/tools/network-documents/src/lib.rs
@@ -33,6 +33,7 @@ extern crate pretty_assertions;
 #[macro_use]
 extern crate log;
 
+pub mod host;
 pub mod network_endpoint;
 pub mod network_head;
 pub mod network_head_v2;
@@ -51,6 +52,7 @@ use pest::iterators::Pair;
 use pest::Parser;
 use serde::{Deserialize, Serialize};
 use std::fmt::{Display, Error, Formatter};
+use std::net::AddrParseError;
 
 #[derive(Parser)]
 #[grammar = "network_documents.pest"]
@@ -70,32 +72,24 @@ impl TextDocumentParser<Rule> for NetworkDocument {
     type DocumentType = NetworkDocument;
 
     fn parse(doc: &str) -> Result<NetworkDocument, TextDocumentParseError> {
-        match NetworkDocsParser::parse(Rule::network_document, doc) {
-            Ok(mut net_doc_pairs) => Ok(NetworkDocument::from_pest_pair(
-                net_doc_pairs.next().unwrap().into_inner().next().unwrap(),
-            )), // get and unwrap the `network_document` rule; never fails
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
-        }
+        let mut net_doc_pairs = NetworkDocsParser::parse(Rule::network_document, doc)?;
+        Ok(NetworkDocument::from_pest_pair(
+            net_doc_pairs.next().unwrap().into_inner().next().unwrap(), // get and unwrap the `network_document` rule; never fails
+        )?)
     }
-    fn from_pest_pair(pair: Pair<Rule>) -> NetworkDocument {
-        match pair.as_rule() {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<NetworkDocument, TextDocumentParseError> {
+        Ok(match pair.as_rule() {
             Rule::peer_v11 => {
-                NetworkDocument::Peer(Box::new(PeerCard::V11(PeerCardV11::from_pest_pair(pair))))
+                NetworkDocument::Peer(Box::new(PeerCard::V11(PeerCardV11::from_pest_pair(pair)?)))
             }
             Rule::head_v3 => NetworkDocument::Head(NetworkHead::V3(Box::new(
-                NetworkHeadV3::from_pest_pair(pair),
+                NetworkHeadV3::from_pest_pair(pair)?,
             ))),
             _ => fatal_error!("unexpected rule: {:?}", pair.as_rule()), // Grammar ensures that we never reach this line
-        }
+        })
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
-/// ParseError
-pub enum ParseError {
-    /// Pest grammar error
-    PestError(String),
-}
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Random identifier with which several Duniter nodes with the same network keypair can be differentiated
 pub struct NodeId(pub u32);
diff --git a/lib/tools/network-documents/src/network_documents.pest b/lib/tools/network-documents/src/network_documents.pest
index 0ed33e2c..533c0c67 100644
--- a/lib/tools/network-documents/src/network_documents.pest
+++ b/lib/tools/network-documents/src/network_documents.pest
@@ -1,4 +1,3 @@
-//r"^(?P<api>[A-Z0-9_]+) (?P<version>[1-9][0-9]*)? ?(?P<uuid>[a-f0-9]{6,8})? ?(?P<host>[a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) (?P<port>[0-9]+)(?: /?(?P<path>.+)?)? *$"
 WHITESPACE = _{ "" }
 
 // Single character rules
@@ -8,6 +7,7 @@ hexa_lower = @{ ASCII_DIGIT | 'a'..'f' }
 hexa_upper = @{ ASCII_DIGIT | 'A'..'F' }
 base58 = { !("O" | "I" | "l") ~ ASCII_ALPHANUMERIC }
 base64 = { ASCII_ALPHANUMERIC | "+" | "/" }
+alphanum_lower = { ASCII_DIGIT | ASCII_ALPHA_LOWER }
 
 // Numbers rules
 tens = @{ '1'..'9' ~ ASCII_DIGIT }
@@ -27,7 +27,7 @@ ed25519_sig = @{ base64{88} | (base64{87} ~ "=") | (base64{86} ~ "==") }
 // Endpoint v1 rules
 api_name = @{ (ASCII_ALPHA_UPPER | ASCII_DIGIT | "_")+ }
 node_id = @{ hexa_lower{1,8} }
-host = @{ ASCII_ALPHA_LOWER ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "-" | "_" | ".")* }
+host = @{ ASCII_ALPHA_LOWER ~ (alphanum_lower | "-" | "_" | ".")* }
 port = @{ u_int }
 path_inner = @{ (ASCII_ALPHANUMERIC | "-" | "_" | ".")+ }
 
@@ -66,12 +66,16 @@ tor = @{ "TOR " }
 network_features = _{ http? ~ ws? ~ tls? ~ tor? }
 api_features_inner = @{ (hexa_lower{2})+ | no_zero_hexa_lower }
 api_features = _{ "0x" ~ api_features_inner ~ " " }
-host_v2_inner = @{ ASCII_ALPHA_LOWER ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "-" | "_" | ".")* }
-host_v2 = _{ host_v2_inner ~ " " }
+domain_name_part = @{ ASCII_ALPHA_LOWER ~ (alphanum_lower | "-" | "_")* }
+domain_name_ext = @{ alphanum_lower+ }
+domain_name_parts = @{ (domain_name_part ~ ".")+ ~ domain_name_ext }
+domain_name_onion = @{ alphanum_lower{16} ~ ".onion" }
+domain_name_inner = @{ domain_name_parts | domain_name_onion | domain_name_part }
+domain_name = _{ domain_name_inner ~ " " }
 ip4_inner = { u8 ~ "." ~ u8 ~ "." ~ u8 ~ "." ~ u8 }
 ip4 = _{ ip4_inner ~ " " }
 path = _{ " " ~ path_inner }
-endpoint_v2 = ${ api_name ~ " " ~ (api_version)? ~ (network_features)? ~ (api_features)? ~ host_v2? ~ ip4? ~ ip6? ~ port ~ path? }
+endpoint_v2 = ${ api_name ~ " " ~ (api_version)? ~ (network_features)? ~ (api_features)? ~ ip4? ~ ip6? ~ domain_name? ~ port ~ path? }
 
 // Peer v11 rules
 peer_v11 = ${ "11:" ~ currency ~ ":" ~ node_id ~ ":" ~ pubkey ~ ":" ~ block_id ~ nl ~ (endpoint_v2 ~ nl)+ ~ ed25519_sig? }
diff --git a/lib/tools/network-documents/src/network_endpoint.rs b/lib/tools/network-documents/src/network_endpoint.rs
index 0e2431ba..37f213ab 100644
--- a/lib/tools/network-documents/src/network_endpoint.rs
+++ b/lib/tools/network-documents/src/network_endpoint.rs
@@ -199,16 +199,14 @@ impl EndpointV1 {
         issuer: PubKey,
         status: u32,
         last_check: u64,
-    ) -> Result<EndpointV1, ParseError> {
-        match NetworkDocsParser::parse(Rule::endpoint_v1, raw_endpoint) {
-            Ok(mut ep_v1_pairs) => Ok(EndpointV1::from_pest_pair(
-                ep_v1_pairs.next().unwrap(),
-                issuer,
-                status,
-                last_check,
-            )),
-            Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))),
-        }
+    ) -> Result<EndpointV1, TextDocumentParseError> {
+        let mut ep_v1_pairs = NetworkDocsParser::parse(Rule::endpoint_v1, raw_endpoint)?;
+        Ok(EndpointV1::from_pest_pair(
+            ep_v1_pairs.next().unwrap(),
+            issuer,
+            status,
+            last_check,
+        ))
     }
 }
 
@@ -274,7 +272,7 @@ impl EndpointV2NetworkFeatures {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-/// Endpoint v2
+/// Endpoint
 pub struct Endpoint {
     /// Endpoint content
     pub content: EndpointEnum,
@@ -295,12 +293,12 @@ pub struct EndpointV2 {
     pub network_features: EndpointV2NetworkFeatures,
     /// API features
     pub api_features: ApiFeatures,
+    /// Domain name
+    pub domain: Option<String>,
     /// IPv4
     pub ip_v4: Option<Ipv4Addr>,
     /// IPv6
     pub ip_v6: Option<Ipv6Addr>,
-    /// hostname
-    pub host: Option<String>,
     /// port number
     pub port: u16,
     /// Optional path
@@ -309,8 +307,8 @@ pub struct EndpointV2 {
 
 impl ToString for EndpointV2 {
     fn to_string(&self) -> String {
-        let host: String = if let Some(ref host) = self.host {
-            format!("{} ", host)
+        let domain: String = if let Some(ref domain) = self.domain {
+            format!("{} ", domain)
         } else {
             String::from("")
         };
@@ -330,7 +328,7 @@ impl ToString for EndpointV2 {
             "".to_owned()
         };
         format!(
-            "{api} {version}{nf}{af}{host}{ip4}{ip6}{port}{path}",
+            "{api} {version}{nf}{af}{ip4}{ip6}{domain}{port}{path}",
             api = self.api.0,
             version = if self.api_version > 0 {
                 format!("V{} ", self.api_version)
@@ -340,7 +338,7 @@ impl ToString for EndpointV2 {
             nf = self.network_features.to_string(),
             af = self.api_features.to_string(),
             port = self.port,
-            host = host,
+            domain = domain,
             ip4 = ip4,
             ip6 = ip6,
             path = path,
@@ -360,8 +358,8 @@ impl EndpointV2 {
             443 => "s",
             _ => "",
         };
-        let host = if let Some(ref host) = self.host {
-            host.clone()
+        let domain = if let Some(ref domain) = self.domain {
+            domain.clone()
         } else if supported_ip_v6 && self.ip_v6.is_some() {
             let ip_v6 = self.ip_v6.unwrap();
             format!("{}", ip_v6)
@@ -380,21 +378,21 @@ impl EndpointV2 {
         if get_protocol {
             Some(format!(
                 "{}{}://{}:{}/{}",
-                protocol, tls, host, self.port, path
+                protocol, tls, domain, self.port, path
             ))
         } else {
-            Some(format!("{}:{}/{}", host, self.port, path))
+            Some(format!("{}:{}/{}", domain, self.port, path))
         }
     }
     /// Generate from pest pair
-    pub fn from_pest_pair(pair: Pair<Rule>) -> EndpointV2 {
+    pub fn from_pest_pair(pair: Pair<Rule>) -> Result<EndpointV2, AddrParseError> {
         let mut api_str = "";
         let mut api_version = 0;
         let mut network_features = EndpointV2NetworkFeatures(vec![0u8]);
         let mut api_features = ApiFeatures(vec![]);
         let mut ip_v4 = None;
         let mut ip_v6 = None;
-        let mut host = None;
+        let mut domain = None;
         let mut port = 0;
         let mut path = None;
         for field in pair.into_inner() {
@@ -413,9 +411,9 @@ impl EndpointV2 {
                     };
                 }
                 Rule::port => port = field.as_str().parse().unwrap(),
-                Rule::host_v2_inner => host = Some(String::from(field.as_str())),
-                Rule::ip4_inner => ip_v4 = Some(Ipv4Addr::from_str(field.as_str()).unwrap()),
-                Rule::ip6_inner => ip_v6 = Some(Ipv6Addr::from_str(field.as_str()).unwrap()),
+                Rule::domain_name_inner => domain = Some(String::from(field.as_str())),
+                Rule::ip4_inner => ip_v4 = Some(Ipv4Addr::from_str(field.as_str())?),
+                Rule::ip6_inner => ip_v6 = Some(Ipv6Addr::from_str(field.as_str())?),
                 Rule::path_inner => path = Some(String::from(field.as_str())),
                 _ => fatal_error!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
             }
@@ -423,26 +421,25 @@ impl EndpointV2 {
         if network_features.is_empty() {
             network_features = EndpointV2NetworkFeatures(vec![]);
         }
-        EndpointV2 {
+
+        Ok(EndpointV2 {
             api: ApiName(String::from(api_str)),
             api_version,
             network_features,
             api_features,
+            domain,
             ip_v4,
             ip_v6,
-            host,
             port,
             path,
-        }
+        })
     }
     /// parse from raw ascii format
-    pub fn parse_from_raw(raw_endpoint: &str) -> Result<EndpointEnum, ParseError> {
-        match NetworkDocsParser::parse(Rule::endpoint_v2, raw_endpoint) {
-            Ok(mut ep_v2_pairs) => Ok(EndpointEnum::V2(EndpointV2::from_pest_pair(
-                ep_v2_pairs.next().unwrap(),
-            ))),
-            Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))),
-        }
+    pub fn parse_from_raw(raw_endpoint: &str) -> Result<EndpointEnum, TextDocumentParseError> {
+        let mut ep_v2_pairs = NetworkDocsParser::parse(Rule::endpoint_v2, raw_endpoint)?;
+        Ok(EndpointEnum::V2(EndpointV2::from_pest_pair(
+            ep_v2_pairs.next().unwrap(),
+        )?))
     }
 }
 
@@ -674,7 +671,7 @@ mod tests {
             api_features: ApiFeatures(vec![]),
             ip_v4: None,
             ip_v6: None,
-            host: None,
+            domain: None,
             port: 8080u16,
             path: None,
         };
@@ -691,7 +688,7 @@ mod tests {
             api_features: ApiFeatures(vec![]),
             ip_v4: None,
             ip_v6: None,
-            host: Some(String::from("localhost")),
+            domain: Some(String::from("localhost")),
             port: 10900u16,
             path: None,
         };
@@ -713,7 +710,7 @@ mod tests {
             api_features: ApiFeatures(vec![]),
             ip_v4: None,
             ip_v6: None,
-            host: Some(String::from("g1.data.duniter.fr")),
+            domain: Some(String::from("g1.data.duniter.fr")),
             port: 443u16,
             path: None,
         };
@@ -730,7 +727,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: None,
-            host: Some(String::from("g1.durs.ifee.fr")),
+            domain: Some(String::from("g1.durs.ifee.fr")),
             port: 443u16,
             path: Some(String::from("ws2p")),
         };
@@ -752,7 +749,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
             ip_v6: None,
-            host: None,
+            domain: None,
             port: 443u16,
             path: Some(String::from("ws2p")),
         };
@@ -769,7 +766,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: Some(Ipv6Addr::from_str("2001:41d0:8:c5aa::1").unwrap()),
-            host: None,
+            domain: None,
             port: 443u16,
             path: Some(String::from("ws2p")),
         };
@@ -786,7 +783,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("5.135.188.170").unwrap()),
             ip_v6: Some(Ipv6Addr::from_str("2001:41d0:8:c5aa::1").unwrap()),
-            host: None,
+            domain: None,
             port: 443u16,
             path: Some(String::from("ws2p")),
         };
@@ -796,7 +793,7 @@ mod tests {
     #[test]
     fn test_parse_and_read_endpoint_with_all_fields() {
         let str_endpoint =
-            "WS2P V2 S 0x7 g1.durs.info 5.135.188.170 [2001:41d0:8:c5aa::1] 443 ws2p";
+            "WS2P V2 S 0x7 5.135.188.170 [2001:41d0:8:c5aa::1] g1.durs.info 443 ws2p";
         let endpoint = EndpointV2 {
             api: ApiName(String::from("WS2P")),
             api_version: 2,
@@ -804,7 +801,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("5.135.188.170").unwrap()),
             ip_v6: Some(Ipv6Addr::from_str("2001:41d0:8:c5aa::1").unwrap()),
-            host: Some(String::from("g1.durs.info")),
+            domain: Some(String::from("g1.durs.info")),
             port: 443u16,
             path: Some(String::from("ws2p")),
         };
diff --git a/lib/tools/network-documents/src/network_head_v3.rs b/lib/tools/network-documents/src/network_head_v3.rs
index edc69c51..67b97d57 100644
--- a/lib/tools/network-documents/src/network_head_v3.rs
+++ b/lib/tools/network-documents/src/network_head_v3.rs
@@ -111,15 +111,13 @@ impl TextDocumentParser<Rule> for NetworkHeadV3 {
     type DocumentType = NetworkHeadV3;
 
     fn parse(doc: &str) -> Result<NetworkHeadV3, TextDocumentParseError> {
-        match NetworkDocsParser::parse(Rule::head_v3, doc) {
-            Ok(mut head_v3_pairs) => {
-                Ok(NetworkHeadV3::from_pest_pair(head_v3_pairs.next().unwrap()))
-            }
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
-        }
+        let mut head_v3_pairs = NetworkDocsParser::parse(Rule::head_v3, doc)?;
+        Ok(NetworkHeadV3::from_pest_pair(
+            head_v3_pairs.next().unwrap(),
+        )?)
     }
 
-    fn from_pest_pair(pair: Pair<Rule>) -> NetworkHeadV3 {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<NetworkHeadV3, TextDocumentParseError> {
         let mut currency_str = "";
         let mut api_outgoing_conf = 0;
         let mut api_incoming_conf = 0;
@@ -166,7 +164,8 @@ impl TextDocumentParser<Rule> for NetworkHeadV3 {
                 _ => fatal_error!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
             }
         }
-        NetworkHeadV3 {
+
+        Ok(NetworkHeadV3 {
             currency_name: CurrencyName(currency_str.to_owned()),
             api_outgoing_conf,
             api_incoming_conf,
@@ -180,7 +179,7 @@ impl TextDocumentParser<Rule> for NetworkHeadV3 {
             soft_version: soft_version.to_owned(),
             signature,
             step,
-        }
+        })
     }
 }
 
diff --git a/lib/tools/network-documents/src/network_peer.rs b/lib/tools/network-documents/src/network_peer.rs
index 55aac82a..e86b1682 100644
--- a/lib/tools/network-documents/src/network_peer.rs
+++ b/lib/tools/network-documents/src/network_peer.rs
@@ -133,15 +133,11 @@ impl TextDocumentParser<Rule> for PeerCardV11 {
     type DocumentType = PeerCardV11;
 
     fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
-        match NetworkDocsParser::parse(Rule::peer_v11, doc) {
-            Ok(mut peer_v11_pairs) => {
-                Ok(PeerCardV11::from_pest_pair(peer_v11_pairs.next().unwrap()))
-            }
-            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
-        }
+        let mut peer_v11_pairs = NetworkDocsParser::parse(Rule::peer_v11, doc)?;
+        Ok(PeerCardV11::from_pest_pair(peer_v11_pairs.next().unwrap())?)
     }
 
-    fn from_pest_pair(pair: Pair<Rule>) -> PeerCardV11 {
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<PeerCardV11, TextDocumentParseError> {
         let mut currency_str = "";
         let mut node_id = NodeId(0);
         let mut issuer = None;
@@ -160,7 +156,7 @@ impl TextDocumentParser<Rule> for PeerCardV11 {
                 Rule::block_id => {
                     created_on = Some(BlockNumber(field.as_str().parse().unwrap())); // Grammar ensures that we have a digits string.
                 }
-                Rule::endpoint_v2 => endpoints.push(EndpointV2::from_pest_pair(field)),
+                Rule::endpoint_v2 => endpoints.push(EndpointV2::from_pest_pair(field)?),
                 Rule::ed25519_sig => {
                     sig = Some(Sig::Ed25519(
                         ed25519::Signature::from_base64(field.as_str()).unwrap(),
@@ -170,7 +166,8 @@ impl TextDocumentParser<Rule> for PeerCardV11 {
             }
         }
         let endpoints_len = endpoints.len();
-        PeerCardV11 {
+
+        Ok(PeerCardV11 {
             currency_name: CurrencyName(currency_str.to_owned()),
             issuer: issuer.expect("Grammar must ensure that peer v11 have valid issuer pubkey !"),
             node_id,
@@ -179,7 +176,7 @@ impl TextDocumentParser<Rule> for PeerCardV11 {
             endpoints,
             endpoints_str: Vec::with_capacity(endpoints_len),
             sig,
-        }
+        })
     }
 }
 
@@ -272,7 +269,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: None,
-            host: Some(String::from("g1.durs.ifee.fr")),
+            domain: Some(String::from("g1.durs.ifee.fr")),
             port: 443u16,
             path: Some(String::from("ws2p")),
         }
@@ -285,7 +282,7 @@ mod tests {
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
             ip_v6: None,
-            host: None,
+            domain: None,
             port: 443u16,
             path: Some(String::from("ws2p")),
         }
-- 
GitLab