diff --git a/Cargo.lock b/Cargo.lock
index 5d99c9f4d13aa98ee2b4342138805819a2946b1a..5946f25b43c02c20da42ce4c81211a2f21eda342 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -211,7 +211,7 @@ dependencies = [
  "duniter-conf 0.1.0-a0.1",
  "duniter-crypto 0.2.0-a0.1",
  "duniter-dal 0.1.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
@@ -233,7 +233,7 @@ version = "0.1.0-a0.1"
 dependencies = [
  "dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "duniter-crypto 0.2.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -280,7 +280,7 @@ name = "duniter-dal"
 version = "0.1.0-a0.1"
 dependencies = [
  "duniter-crypto 0.2.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
  "durs-wot 0.8.0-a0.9",
@@ -294,7 +294,7 @@ dependencies = [
 
 [[package]]
 name = "duniter-documents"
-version = "0.8.0-a0.1"
+version = "0.9.0-b1"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -313,7 +313,7 @@ version = "0.1.0-a0.1"
 dependencies = [
  "duniter-crypto 0.2.0-a0.1",
  "duniter-dal 0.1.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -326,7 +326,7 @@ name = "duniter-module"
 version = "0.1.0-a0.1"
 dependencies = [
  "duniter-crypto 0.2.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -338,7 +338,7 @@ name = "duniter-network"
 version = "0.1.0-a0.1"
 dependencies = [
  "duniter-crypto 0.2.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
  "durs-network-documents 0.1.0-a0.1",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -354,7 +354,7 @@ dependencies = [
  "duniter-conf 0.1.0-a0.1",
  "duniter-crypto 0.2.0-a0.1",
  "duniter-dal 0.1.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
@@ -385,7 +385,7 @@ 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)",
  "duniter-crypto 0.2.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -429,7 +429,7 @@ dependencies = [
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "duniter-crypto 0.2.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "durs-network-documents 0.1.0-a0.1",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -447,7 +447,7 @@ dependencies = [
  "duniter-conf 0.1.0-a0.1",
  "duniter-crypto 0.2.0-a0.1",
  "duniter-dal 0.1.0-a0.1",
- "duniter-documents 0.8.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
diff --git a/blockchain/lib.rs b/blockchain/lib.rs
index dd1009ee5b8dc5e17448e797ed2bd97fb4557920..65280e547255c3228bb250c3c5a987b0d7852eb1 100644
--- a/blockchain/lib.rs
+++ b/blockchain/lib.rs
@@ -72,7 +72,7 @@ use duniter_dal::writers::requests::BlocksDBsWriteQuery;
 use duniter_dal::*;
 use duniter_documents::v10::{BlockDocument, V10Document};
 use duniter_documents::*;
-use duniter_documents::{BlockchainProtocol, Document};
+use duniter_documents::{DUBPDocument, Document};
 use duniter_message::*;
 use duniter_module::*;
 use duniter_network::{
@@ -378,7 +378,7 @@ impl BlockchainModule {
                                 "RefusedBlock({})",
                                 network_block.uncompleted_block_doc().number.0
                             );
-                            self.send_event(&DALEvent::RefusedPendingDoc(BlockchainProtocol::V10(
+                            self.send_event(&DALEvent::RefusedPendingDoc(DUBPDocument::V10(
                                 Box::new(V10Document::Block(Box::new(
                                     network_block.uncompleted_block_doc().clone(),
                                 ))),
@@ -387,23 +387,23 @@ impl BlockchainModule {
                     }
                 }
                 BlockchainDocument::Identity(ref doc) => blockchain_documents.push(
-                    BlockchainProtocol::V10(Box::new(V10Document::Identity(doc.deref().clone()))),
+                    DUBPDocument::V10(Box::new(V10Document::Identity(doc.deref().clone()))),
                 ),
                 BlockchainDocument::Membership(ref doc) => blockchain_documents.push(
-                    BlockchainProtocol::V10(Box::new(V10Document::Membership(doc.deref().clone()))),
+                    DUBPDocument::V10(Box::new(V10Document::Membership(doc.deref().clone()))),
                 ),
                 BlockchainDocument::Certification(ref doc) => {
-                    blockchain_documents.push(BlockchainProtocol::V10(Box::new(
+                    blockchain_documents.push(DUBPDocument::V10(Box::new(
                         V10Document::Certification(Box::new(doc.deref().clone())),
                     )))
                 }
                 BlockchainDocument::Revocation(ref doc) => {
-                    blockchain_documents.push(BlockchainProtocol::V10(Box::new(
-                        V10Document::Revocation(Box::new(doc.deref().clone())),
-                    )))
+                    blockchain_documents.push(DUBPDocument::V10(Box::new(V10Document::Revocation(
+                        Box::new(doc.deref().clone()),
+                    ))))
                 }
                 BlockchainDocument::Transaction(ref doc) => {
-                    blockchain_documents.push(BlockchainProtocol::V10(Box::new(
+                    blockchain_documents.push(DUBPDocument::V10(Box::new(
                         V10Document::Transaction(Box::new(doc.deref().clone())),
                     )))
                 }
@@ -424,12 +424,12 @@ impl BlockchainModule {
         }
         current_blockstamp
     }
-    fn receive_documents(&self, documents: &[BlockchainProtocol]) {
+    fn receive_documents(&self, documents: &[DUBPDocument]) {
         debug!("BlockchainModule : receive_documents()");
         for document in documents {
             trace!("BlockchainModule : Treat one document.");
             match *document {
-                BlockchainProtocol::V10(ref doc_v10) => match doc_v10.deref() {
+                DUBPDocument::V10(ref doc_v10) => match doc_v10.deref() {
                     _ => {}
                 },
                 _ => self.send_event(&DALEvent::RefusedPendingDoc(document.clone())),
diff --git a/dal/dal_event.rs b/dal/dal_event.rs
index 9a487dee7ff9337a133e88a79eba1e9d07376f9f..19f2659f0cb382b96a93bace3f44e8002f365e4d 100644
--- a/dal/dal_event.rs
+++ b/dal/dal_event.rs
@@ -24,7 +24,7 @@ pub enum DALEvent {
     /// Revert blocks in local blockchain
     RevertBlocks(Vec<Box<BlockDocument>>),
     /// Receive new valid pending document
-    NewValidPendingDoc(BlockchainProtocol),
+    NewValidPendingDoc(DUBPDocument),
     /// Receive new refused pending document
-    RefusedPendingDoc(BlockchainProtocol),
+    RefusedPendingDoc(DUBPDocument),
 }
diff --git a/documents/Cargo.toml b/documents/Cargo.toml
index 529506c982e69090fff61e6c860f05385ede59ff..665682454880e6a8e44731b2451d88b681e3729a 100644
--- a/documents/Cargo.toml
+++ b/documents/Cargo.toml
@@ -1,8 +1,8 @@
 [package]
 name = "duniter-documents"
-version = "0.8.0-a0.1"
+version = "0.9.0-b1"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@ifee.fr>"]
-description = "Handles Duniter documents"
+description = "Handles DUBP documents (DUBP: DUniter Blockhain Protocol)"
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
 readme = "README.md"
 keywords = ["duniter", "blockchain", "cryptocurrency", "document"]
diff --git a/documents/src/documents_grammar.pest b/documents/src/documents_grammar.pest
index cc509f5f63eb02674d7660f57081eff17e5b8dd4..aba4de05ba21ae2a0b0a1a1352b74f79a101280f 100644
--- a/documents/src/documents_grammar.pest
+++ b/documents/src/documents_grammar.pest
@@ -1,11 +1,14 @@
-nl = _{ "\n" }
-hexa = { ASCII_DIGIT | 'A'..'F' }
+
+
+WHITESPACE = _{ "" } // Prohibition of white spaces
+nl = _{ "\n" } // Only one way to break a line is accepted
+
+hexa_upper = { ASCII_DIGIT | 'A'..'F' }
 base58 = { !("O" | "I" | "l") ~ ASCII_ALPHANUMERIC }
 base64 = { ASCII_ALPHANUMERIC | "+" | "/" }
-
 integer = @{ "0" | ('1'..'9' ~ ASCII_DIGIT*) }
-hash = @{ hexa{64} }
 
+hash = @{ hexa_upper{64} }
 currency = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_")* }
 pubkey = @{ base58{43,44} }
 uid = @{ ASCII_ALPHA+ }
@@ -16,26 +19,25 @@ ed25519_sig = @{ base64{86,88} ~ "="{0,2} }
 v10 = _{ "Version: 10" }
 
 idty_v10 = ${
-    v10 ~ nl ~
+    SOI ~ v10 ~ nl ~
     "Type: Identity" ~ nl ~
     "Currency: " ~ currency ~ nl ~
     "Issuer: " ~ pubkey ~ nl ~
     "UniqueID: " ~ uid ~ nl ~
     "Timestamp: " ~ blockstamp ~ nl ~
     ed25519_sig? ~ nl*
+    ~ EOI
 }
 
 idty = ${
-    SOI ~
-    (&(v10 ~ nl) ~ idty_v10)
-    ~ EOI
+    (&(SOI ~ v10 ~ nl) ~ idty_v10)
 }
 
 membership_in = @{ "IN" }
 membership_out = @{ "OUT" }
 
 membership_v10 = ${
-    v10 ~ nl ~
+    SOI ~ v10 ~ nl ~
     "Type: Membership" ~ nl ~
     "Currency: " ~ currency ~ nl ~
     "Issuer: " ~ pubkey ~ nl ~
@@ -44,12 +46,11 @@ membership_v10 = ${
     "UserID: " ~ uid ~ nl ~
     "CertTS: " ~ blockstamp ~ nl ~
     ed25519_sig? ~ nl*
+    ~ EOI
 }
 
 membership = ${
-    SOI ~
-    (&("Version: 10" ~ nl) ~ membership_v10)
-    ~ EOI
+    (&(SOI ~ v10 ~ nl) ~ membership_v10)
 }
 
 cert_v10 = ${
@@ -63,16 +64,15 @@ cert_v10 = ${
     "IdtySignature: " ~ ed25519_sig ~ nl ~
     "CertTimestamp: " ~ blockstamp ~ nl ~
     ed25519_sig? ~ nl*
+    ~ EOI
 }
 
 cert = ${
-    SOI ~
-    (&("Version: 10" ~ nl) ~ cert_v10)
-    ~ EOI
+    (&(SOI ~ v10 ~ nl) ~ cert_v10)
 }
 
 revoc_v10 = ${
-    v10 ~ nl ~
+    SOI ~ v10 ~ nl ~
     "Type: Revocation" ~ nl ~
     "Currency: " ~ currency ~ nl ~
     "Issuer: " ~ pubkey ~ nl ~
@@ -80,12 +80,11 @@ revoc_v10 = ${
     "IdtyTimestamp: " ~ blockstamp ~ nl ~
     "IdtySignature: " ~ ed25519_sig ~ nl ~
     ed25519_sig? ~ nl*
+    ~ EOI
 }
 
 revoc = ${
-    SOI ~
-    (&("Version: 10" ~ nl) ~ revoc_v10)
-    ~ EOI
+    (&(SOI ~ v10 ~ nl) ~ revoc_v10)
 }
 
 tx_locktime = @{ integer }
@@ -125,7 +124,7 @@ tx_comment_char = {
 tx_comment = @{ tx_comment_char{0,255} }
 
 tx_v10 = ${
-    "Version: 10" ~ nl ~
+    SOI ~ v10 ~ nl ~
     "Type: Transaction" ~ nl ~
     "Currency: " ~ currency ~ nl ~
     "Blockstamp: " ~ blockstamp ~ nl ~
@@ -137,26 +136,23 @@ tx_v10 = ${
     "Comment: " ~ tx_comment ~ nl ~
     (ed25519_sig ~ nl)* ~ // intermediate signatures (zero if the transaction has only one signature)
     ed25519_sig? ~ nl* // last signature
+    ~ EOI
 }
 
 tx = ${
-    SOI ~
-    (&("Version: 10" ~ nl) ~ tx_v10)
-    ~ EOI
+    (&(SOI ~ v10 ~ nl) ~ tx_v10)
 }
 
 document_v10 = ${
-    SOI ~
+    &(SOI ~ v10 ~ nl) ~
     (&"Type: Identity" ~ idty_v10) | 
     (&"Type: Membership" ~ membership_v10) | 
     (&"Type: Certification" ~ cert_v10) | 
     (&"Type: Revocation" ~ revoc_v10) |
     (&"Type: Transaction" ~ tx_v10)
-    ~ EOI
 }
 
 document = ${
-    SOI ~
-    (&("Version: 10" ~ nl) ~ document_v10)
-    ~ EOI
+    &SOI ~
+    (&(v10  ~ nl) ~ document_v10)
 }
diff --git a/documents/src/lib.rs b/documents/src/lib.rs
index bff1cc7067ebbace124e75bf65e91296508d562b..cfb6ef31013cec2c07067d6eca4b457a97f41e8c 100644
--- a/documents/src/lib.rs
+++ b/documents/src/lib.rs
@@ -46,6 +46,8 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
 use currencies_codes::*;
 use duniter_crypto::hashs::Hash;
 use duniter_crypto::keys::*;
+use pest::iterators::Pair;
+use pest::Parser;
 use std::cmp::Ordering;
 use std::fmt::{Debug, Display, Error, Formatter};
 use std::io::Cursor;
@@ -58,15 +60,61 @@ pub use blockstamp::{Blockstamp, PreviousBlockstamp};
 /// Parser for Documents
 struct DocumentsParser;
 
-/// List of blockchain protocol versions.
+trait TextDocumentParser {
+    /// Type of document generated by the parser
+    type DocumentType;
+
+    /// 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<Rule>) -> Self::DocumentType;
+}
+
+/// List of possible errors while parsing.
+#[derive(Debug, Clone)]
+pub enum TextDocumentParseError {
+    /// The given source don't have a valid specific document format (document type).
+    InvalidInnerFormat(&'static str),
+    /// Type fields contains an unknown document type.
+    UnknownDocumentType(String),
+    /// Error with pest parser
+    PestError(String),
+    /// Invalid currency
+    InvalidCurrency(),
+    /// UnexpectedVersion
+    UnexpectedVersion(String),
+}
+
+/// Document of DUBP (DUniter Blockhain Protocol)
 #[derive(Debug, Clone)]
-pub enum BlockchainProtocol {
+pub enum DUBPDocument {
     /// Version 10.
     V10(Box<v10::V10Document>),
     /// Version 11. (not done yet, but defined for tests)
     V11(),
 }
 
+impl TextDocumentParser for DUBPDocument {
+    type DocumentType = DUBPDocument;
+
+    fn parse(doc: &str) -> Result<DUBPDocument, TextDocumentParseError> {
+        match DocumentsParser::parse(Rule::document, doc) {
+            Ok(mut root_ast) => Ok(DUBPDocument::from_pest_pair(root_ast.next().unwrap())), // get and unwrap the `document` rule; never fails
+            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> DUBPDocument {
+        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::V10(Box::new(v10::V10Document::from_pest_pair(doc_vx_pair)))
+            }
+            _ => panic!("unexpected rule: {:?}", doc_vx_pair.as_rule()), // Grammar ensures that we never reach this line
+        }
+    }
+}
+
 /// Currency name
 #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)]
 pub struct CurrencyName(pub String);
diff --git a/documents/src/v10/block.rs b/documents/src/v10/block.rs
index b03e00f2d642692d654842d49335439292f0ac28..3ef2ad7e75ed420907bca5dad20414ed6877abf7 100644
--- a/documents/src/v10/block.rs
+++ b/documents/src/v10/block.rs
@@ -501,9 +501,9 @@ impl TextDocument for BlockDocument {
     }
 }
 
-impl IntoSpecializedDocument<BlockchainProtocol> for BlockDocument {
-    fn into_specialized(self) -> BlockchainProtocol {
-        BlockchainProtocol::V10(Box::new(V10Document::Block(Box::new(self))))
+impl IntoSpecializedDocument<DUBPDocument> for BlockDocument {
+    fn into_specialized(self) -> DUBPDocument {
+        DUBPDocument::V10(Box::new(V10Document::Block(Box::new(self))))
     }
 }
 
@@ -512,7 +512,6 @@ mod tests {
     use super::certification::CertificationDocumentParser;
     use super::transaction::TransactionDocumentParser;
     use super::*;
-    use std::ops::Deref;
     use {Document, VerificationResult};
 
     #[test]
@@ -584,11 +583,7 @@ IdtyUniqueID: PascaleM
 IdtyTimestamp: 97401-0000003821911909F98519CC773D2D3E5CFE3D5DBB39F4F4FF33B96B4D41800E
 IdtySignature: QncUVXxZ2NfARjdJOn6luILvDuG1NuK9qSoaU4CST2Ij8z7oeVtEgryHl+EXOjSe6XniALsCT0gU8wtadcA/Cw==
 CertTimestamp: 106669-000003682E6FE38C44433DCE92E8B2A26C69B6D7867A2BAED231E788DDEF4251
-UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==", "g1").expect("Fail to parse cert1");
-        let cert1 = match cert1 {
-            V10Document::Certification(doc) => (*doc.deref()).clone(),
-            _ => panic!("Wrong document type"),
-        };
+UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==").expect("Fail to parse cert1");
 
         let tx1 = TransactionDocumentParser::parse("Version: 10
 Type: Transaction
@@ -604,11 +599,7 @@ Unlocks:
 Outputs:
 1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX)
 Comment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025  Merci
-T0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==", "g1").expect("Fail to parse tx1");
-        let tx1 = match tx1 {
-            V10Document::Transaction(doc) => (*doc.deref()).clone(),
-            _ => panic!("Wrong document type"),
-        };
+T0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==").expect("Fail to parse tx1");
 
         let tx2 = TransactionDocumentParser::parse("Version: 10
 Type: Transaction
@@ -624,11 +615,7 @@ Unlocks:
 Outputs:
 1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8)
 Comment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025  Merci
-a9PHPuSfw7jW8FRQHXFsGi/bnLjbtDnTYvEVgUC9u0WlR7GVofa+Xb+l5iy6NwuEXiwvueAkf08wPVY8xrNcCg==", "g1").expect("Fail to parse tx2");
-        let tx2 = match tx2 {
-            V10Document::Transaction(doc) => (*doc.deref()).clone(),
-            _ => panic!("Wrong document type"),
-        };
+a9PHPuSfw7jW8FRQHXFsGi/bnLjbtDnTYvEVgUC9u0WlR7GVofa+Xb+l5iy6NwuEXiwvueAkf08wPVY8xrNcCg==").expect("Fail to parse tx2");
 
         let mut block = BlockDocument {
             nonce: 0,
diff --git a/documents/src/v10/certification.rs b/documents/src/v10/certification.rs
index 00ec4df88f087ec61f53eeda31862ac2d570fd84..bcb9d80164afb08ae995f6790f1f8735c2f20ac5 100644
--- a/documents/src/v10/certification.rs
+++ b/documents/src/v10/certification.rs
@@ -138,9 +138,9 @@ impl TextDocument for CertificationDocument {
     }
 }
 
-impl IntoSpecializedDocument<BlockchainProtocol> for CertificationDocument {
-    fn into_specialized(self) -> BlockchainProtocol {
-        BlockchainProtocol::V10(Box::new(V10Document::Certification(Box::new(self))))
+impl IntoSpecializedDocument<DUBPDocument> for CertificationDocument {
+    fn into_specialized(self) -> DUBPDocument {
+        DUBPDocument::V10(Box::new(V10Document::Certification(Box::new(self))))
     }
 }
 
@@ -222,73 +222,70 @@ CertTimestamp: {blockstamp}
 pub struct CertificationDocumentParser;
 
 impl TextDocumentParser for CertificationDocumentParser {
-    fn parse(doc: &str, currency: &str) -> Result<V10Document, V10DocumentParsingError> {
+    type DocumentType = CertificationDocument;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
         match DocumentsParser::parse(Rule::cert, doc) {
-            Ok(mut doc_ast) => {
-                let cert_ast = doc_ast.next().unwrap(); // get and unwrap the `cert` rule; never fails
-                let cert_vx_ast = cert_ast.into_inner().next().unwrap(); // get and unwrap the `cert_vX` rule; never fails
-
-                match cert_vx_ast.as_rule() {
-                    Rule::cert_v10 => {
-                        let mut pubkeys = Vec::with_capacity(2);
-                        let mut uid = "";
-                        let mut sigs = Vec::with_capacity(2);
-                        let mut blockstamps = Vec::with_capacity(2);
-                        for field in cert_vx_ast.into_inner() {
-                            match field.as_rule() {
-                                Rule::currency => {
-                                    if currency != field.as_str() {
-                                        return Err(V10DocumentParsingError::InvalidCurrency());
-                                    }
-                                }
-                                Rule::pubkey => pubkeys.push(PubKey::Ed25519(
-                                    ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.
-                                )),
-                                Rule::uid => {
-                                    uid = field.as_str();
-                                }
-                                Rule::blockstamp => {
-                                    if blockstamps.len() > 1 {
-                                        return Err(V10DocumentParsingError::InvalidInnerFormat(
-                                            "Cert document must contain exactly two blockstamps !",
-                                        ));
-                                    }
-                                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
-
-                                    let block_id: &str = inner_rules.next().unwrap().as_str();
-                                    let block_hash: &str = inner_rules.next().unwrap().as_str();
-                                    blockstamps.push(Blockstamp {
-                                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
-                                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
-                                    });
-                                }
-                                Rule::ed25519_sig => {
-                                    sigs.push(Sig::Ed25519(
-                                        ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.
-                                    ));
-                                }
-                                Rule::EOI => (),
-                                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
-                            }
-                        }
-                        Ok(V10Document::Certification(Box::new(
-                            CertificationDocument {
-                                text: doc.to_owned(),
-                                issuers: vec![pubkeys[0]],
-                                currency: currency.to_owned(),
-                                target: pubkeys[1],
-                                identity_username: uid.to_owned(),
-                                identity_blockstamp: blockstamps[0],
-                                identity_sig: sigs[0],
-                                blockstamp: blockstamps[1],
-                                signatures: vec![sigs[1]],
-                            },
-                        )))
-                    }
-                    _ => Err(V10DocumentParsingError::UnexpectedVersion()),
+            Ok(mut cert_pairs) => {
+                let cert_pair = cert_pairs.next().unwrap(); // get and unwrap the `cert` rule; never fails
+                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)),
+                    _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                        "{:#?}",
+                        cert_vx_pair.as_rule()
+                    ))),
                 }
             }
-            Err(pest_error) => panic!("{}", pest_error), //Err(V10DocumentParsingError::PestError()),
+            Err(pest_error) => panic!("{}", pest_error), //Err(TextDocumentParseError::PestError()),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+        let doc = pair.as_str();
+        let mut currency = "";
+        let mut pubkeys = Vec::with_capacity(2);
+        let mut uid = "";
+        let mut sigs = Vec::with_capacity(2);
+        let mut blockstamps = Vec::with_capacity(2);
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency = field.as_str(),
+                Rule::pubkey => pubkeys.push(PubKey::Ed25519(
+                    ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.
+                )),
+                Rule::uid => {
+                    uid = field.as_str();
+                }
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamps.push(Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    });
+                }
+                Rule::ed25519_sig => {
+                    sigs.push(Sig::Ed25519(
+                        ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.
+                    ));
+                }
+                Rule::EOI => (),
+                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
+            }
+        }
+        CertificationDocument {
+            text: doc.to_owned(),
+            issuers: vec![pubkeys[0]],
+            currency: currency.to_owned(),
+            target: pubkeys[1],
+            identity_username: uid.to_owned(),
+            identity_blockstamp: blockstamps[0],
+            identity_sig: sigs[0],
+            blockstamp: blockstamps[1],
+            signatures: vec![sigs[1]],
         }
     }
 }
@@ -367,20 +364,14 @@ IdtySignature: SmSweUD4lEMwiZfY8ux9maBjrQQDkC85oMNsin6oSQCPdXG8sFCZ4FisUaWqKsfOl
 CertTimestamp: 167884-0001DFCA28002A8C96575E53B8CEF8317453A7B0BA255542CCF0EC8AB5E99038
 wqZxPEGxLrHGv8VdEIfUGvUcf+tDdNTMXjLzVRCQ4UhlhDRahOMjfcbP7byNYr5OfIl83S1MBxF7VJgu8YasCA==";
 
-        let currency = "g1-test";
-
-        let doc = CertificationDocumentParser::parse(doc, currency).unwrap();
-        if let V10Document::Certification(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
+        let doc = CertificationDocumentParser::parse(doc).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
         /*assert_eq!(
             doc.generate_compact_text(),
             "2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT:\
             7jzkd8GiFnpys4X7mP78w2Y3y3kwdK6fVSLEaojd3aH9:99956:\
             Hkps1QU4HxIcNXKT8YmprYTVByBhPP1U2tIM7Z8wENzLKIWAvQClkAvBE7pW9dnVa18sJIJhVZUcRrPAZfmjBA=="
         );*/
-        } else {
-            panic!("Wrong document type");
-        }
     }
 }
diff --git a/documents/src/v10/identity.rs b/documents/src/v10/identity.rs
index fb228f5f65974f34712cab73a9482b6b50313495..83a840bffb354e0edbcd3beeabed5777aca45056 100644
--- a/documents/src/v10/identity.rs
+++ b/documents/src/v10/identity.rs
@@ -131,9 +131,9 @@ impl TextDocument for IdentityDocument {
     }
 }
 
-impl IntoSpecializedDocument<BlockchainProtocol> for IdentityDocument {
-    fn into_specialized(self) -> BlockchainProtocol {
-        BlockchainProtocol::V10(Box::new(V10Document::Identity(self)))
+impl IntoSpecializedDocument<DUBPDocument> for IdentityDocument {
+    fn into_specialized(self) -> DUBPDocument {
+        DUBPDocument::V10(Box::new(V10Document::Identity(self)))
     }
 }
 
@@ -200,59 +200,63 @@ Timestamp: {blockstamp}
 pub struct IdentityDocumentParser;
 
 impl TextDocumentParser for IdentityDocumentParser {
-    fn parse(doc: &str, currency: &str) -> Result<V10Document, V10DocumentParsingError> {
+    type DocumentType = IdentityDocument;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
         match DocumentsParser::parse(Rule::idty, doc) {
-            Ok(mut doc_ast) => {
-                let idty_ast = doc_ast.next().unwrap(); // get and unwrap the `idty` rule; never fails
-                let idty_vx_ast = idty_ast.into_inner().next().unwrap(); // get and unwrap the `idty_vX` rule; never fails
-
-                match idty_vx_ast.as_rule() {
-                    Rule::idty_v10 => {
-                        let mut pubkey_str = "";
-                        let mut uid = "";
-                        let mut blockstamp = Blockstamp::default();
-                        let mut sig_str = "";
-                        for field in idty_vx_ast.into_inner() {
-                            match field.as_rule() {
-                                Rule::currency => {
-                                    if currency != field.as_str() {
-                                        return Err(V10DocumentParsingError::InvalidCurrency());
-                                    }
-                                }
-                                Rule::pubkey => pubkey_str = field.as_str(),
-                                Rule::uid => uid = field.as_str(),
-                                Rule::blockstamp => {
-                                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
-
-                                    let block_id: &str = inner_rules.next().unwrap().as_str();
-                                    let block_hash: &str = inner_rules.next().unwrap().as_str();
-                                    blockstamp = Blockstamp {
-                                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
-                                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
-                                    };
-                                }
-                                Rule::ed25519_sig => sig_str = field.as_str(),
-                                Rule::EOI => (),
-                                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
-                            }
-                        }
-                        Ok(V10Document::Identity(IdentityDocument {
-                            text: Some(doc.to_owned()),
-                            currency: currency.to_owned(),
-                            username: uid.to_owned(),
-                            blockstamp,
-                            issuers: vec![PubKey::Ed25519(
-                                ed25519::PublicKey::from_base58(pubkey_str).unwrap(),
-                            )], // Grammar ensures that we have a base58 string.
-                            signatures: vec![Sig::Ed25519(
-                                ed25519::Signature::from_base64(sig_str).unwrap(),
-                            )], // Grammar ensures that we have a base64 string.
-                        }))
-                    }
-                    _ => Err(V10DocumentParsingError::UnexpectedVersion()),
+            Ok(mut doc_pairs) => {
+                let mut idty_pair = doc_pairs.next().unwrap(); // get and unwrap the `idty` rule; never fails
+                let mut 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))),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+        let doc = pair.as_str();
+        let mut currency = "";
+        let mut pubkey_str = "";
+        let mut uid = "";
+        let mut blockstamp = Blockstamp::default();
+        let mut sig_str = "";
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency = field.as_str(),
+                Rule::pubkey => pubkey_str = field.as_str(),
+                Rule::uid => uid = field.as_str(),
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamp = Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    };
                 }
+                Rule::ed25519_sig => sig_str = field.as_str(),
+                Rule::EOI => (),
+                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
             }
-            Err(pest_error) => panic!("{}", pest_error), //Err(V10DocumentParsingError::PestError()),
+        }
+        IdentityDocument {
+            text: Some(doc.to_owned()),
+            currency: currency.to_owned(),
+            username: uid.to_owned(),
+            blockstamp,
+            issuers: vec![PubKey::Ed25519(
+                ed25519::PublicKey::from_base58(pubkey_str).unwrap(),
+            )], // Grammar ensures that we have a base58 string.
+            signatures: vec![Sig::Ed25519(
+                ed25519::Signature::from_base64(sig_str).unwrap(),
+            )], // Grammar ensures that we have a base64 string.
         }
     }
 }
@@ -318,14 +322,8 @@ UniqueID: tic
 Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==";
 
-        let currency = "duniter_unit_test_currency";
-
-        let doc = IdentityDocumentParser::parse(doc, currency).expect("Fail to parse idty doc !");
-        if let V10Document::Identity(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = IdentityDocumentParser::parse(doc).expect("Fail to parse idty doc !");
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 }
diff --git a/documents/src/v10/membership.rs b/documents/src/v10/membership.rs
index 55eb6e4606ff7a37fa41fcad35ca087a3e45e1ab..ac1a9dcd0b8e8b15b0a46adb2bbfcc18d847523c 100644
--- a/documents/src/v10/membership.rs
+++ b/documents/src/v10/membership.rs
@@ -167,9 +167,9 @@ impl TextDocument for MembershipDocument {
     }
 }
 
-impl IntoSpecializedDocument<BlockchainProtocol> for MembershipDocument {
-    fn into_specialized(self) -> BlockchainProtocol {
-        BlockchainProtocol::V10(Box::new(V10Document::Membership(self)))
+impl IntoSpecializedDocument<DUBPDocument> for MembershipDocument {
+    fn into_specialized(self) -> DUBPDocument {
+        DUBPDocument::V10(Box::new(V10Document::Membership(self)))
     }
 }
 
@@ -249,67 +249,70 @@ CertTS: {ity_blockstamp}
 pub struct MembershipDocumentParser;
 
 impl TextDocumentParser for MembershipDocumentParser {
-    fn parse(doc: &str, currency: &str) -> Result<V10Document, V10DocumentParsingError> {
+    type DocumentType = MembershipDocument;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
         match DocumentsParser::parse(Rule::membership, doc) {
-            Ok(mut doc_ast) => {
-                let membership_ast = doc_ast.next().unwrap(); // get and unwrap the `membership` rule; never fails
-                let membership_vx_ast = membership_ast.into_inner().next().unwrap(); // get and unwrap the `membership_vX` rule; never fails
+            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 membership_vx_ast.as_rule() {
+                match ms_vx_pair.as_rule() {
                     Rule::membership_v10 => {
-                        let mut pubkey_str = "";
-                        let mut uid = "";
-                        let mut blockstamps = Vec::with_capacity(2);
-                        let mut membership = MembershipType::In();
-                        let mut sig_str = "";
-                        for field in membership_vx_ast.into_inner() {
-                            match field.as_rule() {
-                                Rule::currency => {
-                                    if currency != field.as_str() {
-                                        return Err(V10DocumentParsingError::InvalidCurrency());
-                                    }
-                                }
-                                Rule::pubkey => pubkey_str = field.as_str(),
-                                Rule::uid => uid = field.as_str(),
-                                Rule::membership_in => membership = MembershipType::In(),
-                                Rule::membership_out => membership = MembershipType::Out(),
-                                Rule::blockstamp => {
-                                    if blockstamps.len() > 1 {
-                                        return Err(V10DocumentParsingError::InvalidInnerFormat("Membership document must have exactly two blockstamps !"));
-                                    }
-                                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
-
-                                    let block_id: &str = inner_rules.next().unwrap().as_str();
-                                    let block_hash: &str = inner_rules.next().unwrap().as_str();
-                                    blockstamps.push(Blockstamp {
-                                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
-                                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
-                                    });
-                                }
-                                Rule::ed25519_sig => sig_str = field.as_str(),
-                                Rule::EOI => (),
-                                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
-                            }
-                        }
-                        Ok(V10Document::Membership(MembershipDocument {
-                            text: Some(doc.to_owned()),
-                            issuers: vec![PubKey::Ed25519(
-                                ed25519::PublicKey::from_base58(pubkey_str).unwrap(),
-                            )], // Grammar ensures that we have a base58 string.
-                            currency: currency.to_owned(),
-                            blockstamp: blockstamps[0],
-                            membership,
-                            identity_username: uid.to_owned(),
-                            identity_blockstamp: blockstamps[1],
-                            signatures: vec![Sig::Ed25519(
-                                ed25519::Signature::from_base64(sig_str).unwrap(),
-                            )], // Grammar ensures that we have a base64 string.
-                        }))
+                        Ok(MembershipDocumentParser::from_pest_pair(ms_vx_pair))
                     }
-                    _ => Err(V10DocumentParsingError::UnexpectedVersion()),
+                    _ => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                        "{:#?}",
+                        ms_vx_pair.as_rule()
+                    ))),
                 }
             }
-            Err(pest_error) => panic!("{}", pest_error), //Err(V10DocumentParsingError::PestError()),
+            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+        let doc = pair.as_str();
+        let mut currency = "";
+        let mut pubkey_str = "";
+        let mut uid = "";
+        let mut blockstamps = Vec::with_capacity(2);
+        let mut membership = MembershipType::In();
+        let mut sig_str = "";
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency = field.as_str(),
+                Rule::uid => uid = field.as_str(),
+                Rule::pubkey => pubkey_str = field.as_str(),
+                Rule::membership_in => membership = MembershipType::In(),
+                Rule::membership_out => membership = MembershipType::Out(),
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamps.push(Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    });
+                }
+                Rule::ed25519_sig => sig_str = field.as_str(),
+                Rule::EOI => (),
+                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
+            }
+        }
+        MembershipDocument {
+            text: Some(doc.to_owned()),
+            issuers: vec![PubKey::Ed25519(
+                ed25519::PublicKey::from_base58(pubkey_str).unwrap(),
+            )], // Grammar ensures that we have a base58 string.
+            currency: currency.to_owned(),
+            blockstamp: blockstamps[0],
+            membership,
+            identity_username: uid.to_owned(),
+            identity_blockstamp: blockstamps[1],
+            signatures: vec![Sig::Ed25519(
+                ed25519::Signature::from_base64(sig_str).unwrap(),
+            )], // Grammar ensures that we have a base64 string.
         }
     }
 }
@@ -379,13 +382,10 @@ UserID: tic
 CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
 s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==";
 
-        let currency = "duniter_unit_test_currency";
-
-        let doc = MembershipDocumentParser::parse(doc, currency).unwrap();
-        if let V10Document::Membership(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
-            assert_eq!(
+        let doc = MembershipDocumentParser::parse(doc).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
+        assert_eq!(
             doc.generate_compact_text(),
                 "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:\
                 s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==:\
@@ -393,8 +393,5 @@ s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrD
                 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\
                 tic"
             );
-        } else {
-            panic!("Wrong document type");
-        }
     }
 }
diff --git a/documents/src/v10/mod.rs b/documents/src/v10/mod.rs
index 0cefad227a7d6a59f717ae1cecbac6b9721997dc..cc2240d15798d1847dc3bc58c27ac05044fef305 100644
--- a/documents/src/v10/mod.rs
+++ b/documents/src/v10/mod.rs
@@ -24,6 +24,7 @@ pub mod transaction;
 
 use crypto::digest::Digest;
 use duniter_crypto::keys::PrivateKey;
+use pest::Parser;
 
 pub use v10::block::BlockDocument;
 use v10::certification::*;
@@ -53,8 +54,6 @@ impl<D: TextDocument> TextDocumentFormat<D> {
 }
 
 /// List of wrapped document types.
-///
-/// > TODO Add wrapped types in enum variants.
 #[derive(Debug, Clone)]
 pub enum V10Document {
     /// Block document.
@@ -76,6 +75,41 @@ pub enum V10Document {
     Revocation(Box<RevocationDocument>),
 }
 
+impl TextDocumentParser for V10Document {
+    type DocumentType = V10Document;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
+        match DocumentsParser::parse(Rule::document_v10, doc) {
+            Ok(mut document_v10_pairs) => Ok(V10Document::from_pest_pair(
+                document_v10_pairs.next().unwrap(),
+            )), // get and unwrap the `document_v10` rule; never fails
+            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+        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 => V10Document::Identity(
+                identity::IdentityDocumentParser::from_pest_pair(doc_type_v10_pair),
+            ),
+            Rule::membership_v10 => V10Document::Membership(
+                membership::MembershipDocumentParser::from_pest_pair(doc_type_v10_pair),
+            ),
+            Rule::cert_v10 => V10Document::Certification(Box::new(
+                certification::CertificationDocumentParser::from_pest_pair(doc_type_v10_pair),
+            )),
+            Rule::revoc_v10 => V10Document::Revocation(Box::new(
+                revocation::RevocationDocumentParser::from_pest_pair(doc_type_v10_pair),
+            )),
+            Rule::tx_v10 => V10Document::Transaction(Box::new(
+                transaction::TransactionDocumentParser::from_pest_pair(doc_type_v10_pair),
+            )),
+            _ => panic!("unexpected rule: {:?}", doc_type_v10_pair.as_rule()), // Grammar ensures that we never reach this line
+        }
+    }
+}
+
 /// Trait for a compact V10 document.
 pub trait CompactTextDocument: Sized + Clone {
     /// Generate document compact text.
@@ -176,23 +210,6 @@ pub trait TextDocumentBuilder: DocumentBuilder {
     }
 }
 
-/// List of possible errors while parsing.
-#[derive(Debug, Clone)]
-pub enum V10DocumentParsingError {
-    /// The given source don't have a valid document format.
-    InvalidWrapperFormat(),
-    /// The given source don't have a valid specific document format (document type).
-    InvalidInnerFormat(&'static str),
-    /// Type fields contains an unknown document type.
-    UnknownDocumentType(String),
-    /// Error with pest parser
-    PestError(),
-    /// Invalid currency
-    InvalidCurrency(),
-    /// UnexpectedVersion
-    UnexpectedVersion(),
-}
-
 /// V10 Documents in separated parts
 #[derive(Debug, Clone)]
 pub struct V10DocumentParts {
@@ -206,16 +223,12 @@ pub struct V10DocumentParts {
     pub signatures: Vec<Sig>,
 }
 
-trait TextDocumentParser {
-    fn parse(doc: &str, currency: &str) -> Result<V10Document, V10DocumentParsingError>;
-}
-
 /*/// A V10 document parser.
 #[derive(Debug, Clone, Copy)]
 pub struct V10DocumentParser;
 
-impl<'a> DocumentParser<&'a str, V10Document, V10DocumentParsingError> for V10DocumentParser {
-    fn parse(source: &'a str) -> Result<V10Document, V10DocumentParsingError> {
+impl<'a> DocumentParser<&'a str, V10Document, TextDocumentParseError> for V10DocumentParser {
+    fn parse(source: &'a str) -> Result<V10Document, TextDocumentParseError> {
         /*match DocumentsParser::parse(Rule::document_v10, source) {
             Ok(mut source_ast) => {
                 let doc_v10_ast = source_ast.next().unwrap(); // get and unwrap the `document_v10` rule; never fails
@@ -229,7 +242,7 @@ impl<'a> DocumentParser<&'a str, V10Document, V10DocumentParsingError> for V10Do
                     Rule::tx_v10 => TransactionDocumentParser::parse_standard(doc_type_v10_ast.as_str(), "", currency, vec![]),
                 }
             }
-            Err(_) => Err(V10DocumentParsingError::InvalidWrapperFormat()),
+            Err(_) => Err(TextDocumentParseError::InvalidWrapperFormat()),
         }*/
 if let Some(caps) = DOCUMENT_REGEX.captures(source) {
 let doctype = &caps["type"];
@@ -243,12 +256,12 @@ match doctype {
 "Certification" => CertificationDocumentParser::parse_standard(source, currency),
 "Revocation" => RevocationDocumentParser::parse_standard(source, currency),
 "Transaction" => TransactionDocumentParser::parse_standard(source, currency),
-_ => Err(V10DocumentParsingError::UnknownDocumentType(
+_ => Err(TextDocumentParseError::UnknownDocumentType(
 doctype.to_string(),
 )),
 }
 } else {
-Err(V10DocumentParsingError::InvalidWrapperFormat())
+Err(TextDocumentParseError::InvalidWrapperFormat())
 }
 }
 }*/
@@ -398,13 +411,9 @@ UniqueID: elois
 Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
 Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==";
 
-        let doc = IdentityDocumentParser::parse(text, "g1").unwrap();
-        if let V10Document::Identity(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = IdentityDocumentParser::parse(text).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 
     #[test]
@@ -419,13 +428,9 @@ UserID: elois
 CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
 FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==";
 
-        let doc = MembershipDocumentParser::parse(text, "g1").unwrap();
-        if let V10Document::Membership(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = MembershipDocumentParser::parse(text).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 
     #[test]
@@ -441,13 +446,9 @@ IdtySignature: DjeipIeb/RF0tpVCnVnuw6mH1iLJHIsDfPGLR90Twy3PeoaDz6Yzhc/UjLWqHCi5Y
 CertTimestamp: 99956-00000472758331FDA8388E30E50CA04736CBFD3B7C21F34E74707107794B56DD
 Hkps1QU4HxIcNXKT8YmprYTVByBhPP1U2tIM7Z8wENzLKIWAvQClkAvBE7pW9dnVa18sJIJhVZUcRrPAZfmjBA==";
 
-        let doc = CertificationDocumentParser::parse(text, "g1").unwrap();
-        if let V10Document::Certification(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = CertificationDocumentParser::parse(text).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 
     #[test]
@@ -461,13 +462,9 @@ IdtyTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B85
 IdtySignature: 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==
 XXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==";
 
-        let doc = RevocationDocumentParser::parse(text, "g1").unwrap();
-        if let V10Document::Revocation(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = RevocationDocumentParser::parse(text).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 
     #[test]
@@ -490,12 +487,8 @@ Outputs:
 Comment: c est pour 2 mois d adhesion ressourcerie
 lnpuFsIymgz7qhKF/GsZ3n3W8ZauAAfWmT4W0iJQBLKJK2GFkesLWeMj/+GBfjD6kdkjreg9M6VfkwIZH+hCCQ==";
 
-        let doc = TransactionDocumentParser::parse(text, "g1").unwrap();
-        if let V10Document::Transaction(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = TransactionDocumentParser::parse(text).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 }
diff --git a/documents/src/v10/revocation.rs b/documents/src/v10/revocation.rs
index d4ca1983cd9918576f982d9f82201ac168d52dcd..607a648372bc29cf056e548212edb85cea3745ae 100644
--- a/documents/src/v10/revocation.rs
+++ b/documents/src/v10/revocation.rs
@@ -116,9 +116,9 @@ impl TextDocument for RevocationDocument {
     }
 }
 
-impl IntoSpecializedDocument<BlockchainProtocol> for RevocationDocument {
-    fn into_specialized(self) -> BlockchainProtocol {
-        BlockchainProtocol::V10(Box::new(V10Document::Revocation(Box::new(self))))
+impl IntoSpecializedDocument<DUBPDocument> for RevocationDocument {
+    fn into_specialized(self) -> DUBPDocument {
+        DUBPDocument::V10(Box::new(V10Document::Revocation(Box::new(self))))
     }
 }
 
@@ -190,103 +190,69 @@ IdtySignature: {idty_sig}
 pub struct RevocationDocumentParser;
 
 impl TextDocumentParser for RevocationDocumentParser {
-    fn parse(doc: &str, currency: &str) -> Result<V10Document, V10DocumentParsingError> {
+    type DocumentType = RevocationDocument;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
         match DocumentsParser::parse(Rule::revoc, doc) {
-            Ok(mut doc_ast) => {
-                let revoc_ast = doc_ast.next().unwrap(); // get and unwrap the `revoc` rule; never fails
-                let revoc_vx_ast = revoc_ast.into_inner().next().unwrap(); // get and unwrap the `revoc_vX` rule; never fails
-
-                match revoc_vx_ast.as_rule() {
-                    Rule::revoc_v10 => {
-                        let mut pubkeys = Vec::with_capacity(1);
-                        let mut uid = "";
-                        let mut sigs = Vec::with_capacity(2);
-                        let mut blockstamps = Vec::with_capacity(1);
-                        for field in revoc_vx_ast.into_inner() {
-                            match field.as_rule() {
-                                Rule::currency => {
-                                    if currency != field.as_str() {
-                                        return Err(V10DocumentParsingError::InvalidCurrency());
-                                    }
-                                }
-                                Rule::pubkey => {
-                                    if !pubkeys.is_empty() {
-                                        return Err(V10DocumentParsingError::InvalidInnerFormat(
-                                            "Revocation document must contain exactly one pubkey !",
-                                        ));
-                                    }
-                                    pubkeys.push(PubKey::Ed25519(
-                                        ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.
-                                    ));
-                                }
-                                Rule::uid => {
-                                    uid = field.as_str();
-                                }
-                                Rule::blockstamp => {
-                                    if blockstamps.len() > 1 {
-                                        return Err(V10DocumentParsingError::InvalidInnerFormat(
-                                            "Revocation document must contain exactly one blockstamp !",
-                                        ));
-                                    }
-                                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
-
-                                    let block_id: &str = inner_rules.next().unwrap().as_str();
-                                    let block_hash: &str = inner_rules.next().unwrap().as_str();
-                                    blockstamps.push(Blockstamp {
-                                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
-                                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
-                                    });
-                                }
-                                Rule::ed25519_sig => {
-                                    sigs.push(Sig::Ed25519(
-                                        ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.
-                                    ));
-                                }
-                                Rule::EOI => (),
-                                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
-                            }
-                        }
-                        Ok(V10Document::Revocation(Box::new(RevocationDocument {
-                            text: doc.to_owned(),
-                            issuers: vec![pubkeys[0]],
-                            currency: currency.to_owned(),
-                            identity_username: uid.to_owned(),
-                            identity_blockstamp: blockstamps[0],
-                            identity_sig: sigs[0],
-                            signatures: vec![sigs[1]],
-                        })))
-                    }
-                    _ => Err(V10DocumentParsingError::UnexpectedVersion()),
+            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) => panic!("{}", pest_error), //Err(V10DocumentParsingError::PestError()),
+            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+        let doc = pair.as_str();
+        let mut currency = "";
+        let mut pubkeys = Vec::with_capacity(1);
+        let mut uid = "";
+        let mut sigs = Vec::with_capacity(2);
+        let mut blockstamps = Vec::with_capacity(1);
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency = field.as_str(),
+                Rule::pubkey => pubkeys.push(PubKey::Ed25519(
+                    ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.
+                )),
+                Rule::uid => {
+                    uid = field.as_str();
+                }
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // { integer ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamps.push(Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    });
+                }
+                Rule::ed25519_sig => {
+                    sigs.push(Sig::Ed25519(
+                        ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.
+                    ));
+                }
+                Rule::EOI => (),
+                _ => panic!("unexpected rule"), // Grammar ensures that we never reach this line
+            }
+        }
+        RevocationDocument {
+            text: doc.to_owned(),
+            issuers: vec![pubkeys[0]],
+            currency: currency.to_owned(),
+            identity_username: uid.to_owned(),
+            identity_blockstamp: blockstamps[0],
+            identity_sig: sigs[0],
+            signatures: vec![sigs[1]],
         }
-
-        /*if let Some(caps) = REVOCATION_REGEX.captures(body) {
-            let issuer = &caps["issuer"];
-            let identity_username = &caps["idty_uid"];
-            let identity_blockstamp = &caps["idty_blockstamp"];
-            let identity_sig = &caps["idty_sig"];
-        
-            // Regex match so should not fail.
-            // TODO : Test it anyway
-            let issuer = PubKey::Ed25519(ed25519::PublicKey::from_base58(issuer).unwrap());
-            let identity_username = String::from(identity_username);
-            let identity_blockstamp = Blockstamp::from_string(identity_blockstamp).unwrap();
-            let identity_sig = Sig::Ed25519(Signature::from_base64(identity_sig).unwrap());
-        
-            Ok(V10Document::Revocation(Box::new(RevocationDocument {
-                text: doc.to_owned(),
-                issuers: vec![issuer],
-                currency: currency.to_owned(),
-                identity_username,
-                identity_blockstamp,
-                identity_sig,
-                signatures,
-            })))
-        } else {
-            Err(V10DocumentParsingError::InvalidInnerFormat("Revocation"))
-        }*/
     }
 }
 
@@ -354,14 +320,8 @@ IdtyTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B85
 IdtySignature: 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==
 XXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==";
 
-        let currency = "g1";
-
-        let doc = RevocationDocumentParser::parse(doc, currency).unwrap();
-        if let V10Document::Revocation(doc) = doc {
-            println!("Doc : {:?}", doc);
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
-        } else {
-            panic!("Wrong document type");
-        }
+        let doc = RevocationDocumentParser::parse(doc).unwrap();
+        println!("Doc : {:?}", doc);
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
     }
 }
diff --git a/documents/src/v10/transaction.rs b/documents/src/v10/transaction.rs
index 8969526018579cf0970d60c5adf6bd35da745e9a..c0729efc4dbb638155fd653d52ff0129bd9f254c 100644
--- a/documents/src/v10/transaction.rs
+++ b/documents/src/v10/transaction.rs
@@ -76,7 +76,7 @@ impl ToString for TransactionInput {
 }
 
 impl TransactionInput {
-    fn from_pest_pairs(mut pairs: Pairs<Rule>) -> TransactionInput {
+    fn from_pest_pair(mut pairs: Pairs<Rule>) -> TransactionInput {
         let tx_input_type_pair = pairs.next().unwrap();
         match tx_input_type_pair.as_rule() {
             Rule::tx_input_du => {
@@ -108,14 +108,14 @@ impl TransactionInput {
 }
 
 impl FromStr for TransactionInput {
-    type Err = V10DocumentParsingError;
+    type Err = TextDocumentParseError;
 
     fn from_str(source: &str) -> Result<Self, Self::Err> {
         match DocumentsParser::parse(Rule::tx_input, source) {
-            Ok(mut pairs) => Ok(TransactionInput::from_pest_pairs(
+            Ok(mut pairs) => Ok(TransactionInput::from_pest_pair(
                 pairs.next().unwrap().into_inner(),
             )),
-            Err(_) => Err(V10DocumentParsingError::InvalidInnerFormat(
+            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
                 "Invalid unlocks !",
             )),
         }
@@ -124,7 +124,7 @@ impl FromStr for TransactionInput {
 
 /*impl TransactionInput {
     /// Parse Transaction Input from string.
-    pub fn from_str(source: &str) -> Result<TransactionInput, V10DocumentParsingError> {
+    pub fn from_str(source: &str) -> Result<TransactionInput, TextDocumentParseError> {
         if let Some(caps) = D_INPUT_REGEX.captures(source) {
             let amount = &caps["amount"];
             let base = &caps["base"];
@@ -156,7 +156,7 @@ impl FromStr for TransactionInput {
             ))
         } else {
             println!("Fail to parse this input = {:?}", source);
-            Err(V10DocumentParsingError::InvalidInnerFormat("Transaction2"))
+            Err(TextDocumentParseError::InvalidInnerFormat("Transaction2"))
         }
     }
 }*/
@@ -201,7 +201,7 @@ impl ToString for TransactionInputUnlocks {
 }
 
 impl TransactionInputUnlocks {
-    fn from_pest_pairs(pairs: Pairs<Rule>) -> TransactionInputUnlocks {
+    fn from_pest_pair(pairs: Pairs<Rule>) -> TransactionInputUnlocks {
         let mut input_index = 0;
         let mut unlock_conds = Vec::new();
         for unlock_field in pairs {
@@ -231,14 +231,14 @@ impl TransactionInputUnlocks {
 }
 
 impl FromStr for TransactionInputUnlocks {
-    type Err = V10DocumentParsingError;
+    type Err = TextDocumentParseError;
 
     fn from_str(source: &str) -> Result<Self, Self::Err> {
         match DocumentsParser::parse(Rule::tx_unlock, source) {
-            Ok(mut pairs) => Ok(TransactionInputUnlocks::from_pest_pairs(
+            Ok(mut pairs) => Ok(TransactionInputUnlocks::from_pest_pair(
                 pairs.next().unwrap().into_inner(),
             )),
-            Err(_) => Err(V10DocumentParsingError::InvalidInnerFormat(
+            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
                 "Invalid unlocks !",
             )),
         }
@@ -446,7 +446,7 @@ impl ToString for TransactionOutput {
 }
 
 impl TransactionOutput {
-    fn from_pest_pairs(mut utxo_pairs: Pairs<Rule>) -> TransactionOutput {
+    fn from_pest_pair(mut utxo_pairs: Pairs<Rule>) -> TransactionOutput {
         let amount = TxAmount(utxo_pairs.next().unwrap().as_str().parse().unwrap());
         let base = TxBase(utxo_pairs.next().unwrap().as_str().parse().unwrap());
         let conditions_pairs = utxo_pairs.next().unwrap();
@@ -463,14 +463,14 @@ impl TransactionOutput {
 }
 
 impl FromStr for TransactionOutput {
-    type Err = V10DocumentParsingError;
+    type Err = TextDocumentParseError;
 
     fn from_str(source: &str) -> Result<Self, Self::Err> {
         match DocumentsParser::parse(Rule::tx_output, source) {
-            Ok(mut utxo_pairs) => Ok(TransactionOutput::from_pest_pairs(
+            Ok(mut utxo_pairs) => Ok(TransactionOutput::from_pest_pair(
                 utxo_pairs.next().unwrap().into_inner(),
             )),
-            Err(_) => Err(V10DocumentParsingError::InvalidInnerFormat(
+            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
                 "Invalid output !",
             )),
         }
@@ -654,9 +654,9 @@ impl TextDocument for TransactionDocument {
     }
 }
 
-impl IntoSpecializedDocument<BlockchainProtocol> for TransactionDocument {
-    fn into_specialized(self) -> BlockchainProtocol {
-        BlockchainProtocol::V10(Box::new(V10Document::Transaction(Box::new(self))))
+impl IntoSpecializedDocument<DUBPDocument> for TransactionDocument {
+    fn into_specialized(self) -> DUBPDocument {
+        DUBPDocument::V10(Box::new(V10Document::Transaction(Box::new(self))))
     }
 }
 
@@ -762,79 +762,83 @@ Issuers:
 pub struct TransactionDocumentParser;
 
 impl TextDocumentParser for TransactionDocumentParser {
-    fn parse(doc: &str, currency: &str) -> Result<V10Document, V10DocumentParsingError> {
+    type DocumentType = TransactionDocument;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
         match DocumentsParser::parse(Rule::tx, doc) {
-            Ok(mut doc_ast) => {
-                let tx_ast = doc_ast.next().unwrap(); // get and unwrap the `tx` rule; never fails
-                let tx_vx_ast = tx_ast.into_inner().next().unwrap(); // get and unwrap the `tx_vX` rule; never fails
-
-                match tx_vx_ast.as_rule() {
-                    Rule::tx_v10 => {
-                        let mut blockstamp = Blockstamp::default();
-                        let mut locktime = 0;
-                        let mut issuers = Vec::new();
-                        let mut inputs = Vec::new();
-                        let mut unlocks = Vec::new();
-                        let mut outputs = Vec::new();
-                        let mut comment = "";
-                        let mut sigs = Vec::new();
-
-                        for field in tx_vx_ast.into_inner() {
-                            match field.as_rule() {
-                                Rule::currency => {
-                                    if currency != field.as_str() {
-                                        return Err(V10DocumentParsingError::InvalidCurrency());
-                                    }
-                                }
-                                Rule::blockstamp => {
-                                    let mut inner_rules = field.into_inner(); // ${ block_id ~ "-" ~ hash }
-
-                                    let block_id: &str = inner_rules.next().unwrap().as_str();
-                                    let block_hash: &str = inner_rules.next().unwrap().as_str();
-                                    blockstamp = Blockstamp {
-                                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
-                                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
-                                    };
-                                }
-                                Rule::tx_locktime => locktime = field.as_str().parse().unwrap(), // Grammar ensures that we have digits characters.
-                                Rule::pubkey => issuers.push(PubKey::Ed25519(
-                                    ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.
-                                )),
-                                Rule::tx_input => inputs
-                                    .push(TransactionInput::from_pest_pairs(field.into_inner())),
-                                Rule::tx_unlock => unlocks.push(
-                                    TransactionInputUnlocks::from_pest_pairs(field.into_inner()),
-                                ),
-                                Rule::tx_output => outputs
-                                    .push(TransactionOutput::from_pest_pairs(field.into_inner())),
-                                Rule::tx_comment => comment = field.as_str(),
-                                Rule::ed25519_sig => {
-                                    sigs.push(Sig::Ed25519(
-                                        ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.
-                                    ));
-                                }
-                                Rule::EOI => (),
-                                _ => panic!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
-                            }
-                        }
-                        Ok(V10Document::Transaction(Box::new(TransactionDocument {
-                            text: Some(doc.to_owned()),
-                            currency: currency.to_owned(),
-                            blockstamp,
-                            locktime,
-                            issuers,
-                            inputs,
-                            unlocks,
-                            outputs,
-                            comment: comment.to_owned(),
-                            signatures: sigs,
-                            hash: None,
-                        })))
-                    }
-                    _ => Err(V10DocumentParsingError::UnexpectedVersion()),
+            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) => panic!("{}", pest_error), //Err(V10DocumentParsingError::PestError()),
+            Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
+        let doc = pair.as_str();
+        let mut currency = "";
+        let mut blockstamp = Blockstamp::default();
+        let mut locktime = 0;
+        let mut issuers = Vec::new();
+        let mut inputs = Vec::new();
+        let mut unlocks = Vec::new();
+        let mut outputs = Vec::new();
+        let mut comment = "";
+        let mut sigs = Vec::new();
+
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency = field.as_str(),
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // ${ block_id ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamp = Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    };
+                }
+                Rule::tx_locktime => locktime = field.as_str().parse().unwrap(), // Grammar ensures that we have digits characters.
+                Rule::pubkey => issuers.push(PubKey::Ed25519(
+                    ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.
+                )),
+                Rule::tx_input => inputs.push(TransactionInput::from_pest_pair(field.into_inner())),
+                Rule::tx_unlock => {
+                    unlocks.push(TransactionInputUnlocks::from_pest_pair(field.into_inner()))
+                }
+                Rule::tx_output => {
+                    outputs.push(TransactionOutput::from_pest_pair(field.into_inner()))
+                }
+                Rule::tx_comment => comment = field.as_str(),
+                Rule::ed25519_sig => {
+                    sigs.push(Sig::Ed25519(
+                        ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.
+                    ));
+                }
+                Rule::EOI => (),
+                _ => panic!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
+            }
+        }
+        TransactionDocument {
+            text: Some(doc.to_owned()),
+            currency: currency.to_owned(),
+            blockstamp,
+            locktime,
+            issuers,
+            inputs,
+            unlocks,
+            outputs,
+            comment: comment.to_owned(),
+            signatures: sigs,
+            hash: None,
         }
     }
 }
@@ -995,17 +999,14 @@ kL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E
 e3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw==
 w69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw==";
 
-        let currency = "duniter_unit_test_currency";
-
-        let doc = TransactionDocumentParser::parse(doc, currency)
+        let doc = TransactionDocumentParser::parse(doc)
             .expect("fail to parse test transaction document !");
-        if let V10Document::Transaction(doc) = doc {
-            //println!("Doc : {:?}", doc);
-            println!("{}", doc.generate_compact_text());
-            assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
-            assert_eq!(
-                doc.generate_compact_text(),
-                "TX:10:3:6:6:3:1:0
+        //println!("Doc : {:?}", doc);
+        println!("{}", doc.generate_compact_text());
+        assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
+        assert_eq!(
+            doc.generate_compact_text(),
+            "TX:10:3:6:6:3:1:0
 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
 4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR
@@ -1029,9 +1030,6 @@ FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa
 kL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw==
 e3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw==
 w69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw=="
-            );
-        } else {
-            panic!("Wrong document type");
-        }
+        );
     }
 }
diff --git a/message/lib.rs b/message/lib.rs
index b6d75d48790c2a630ee1f9ba69dc04fb0798be8b..b30c0fdc2ee24cf3efb116fb3d8a335592d9034f 100644
--- a/message/lib.rs
+++ b/message/lib.rs
@@ -42,7 +42,7 @@ use duniter_crypto::keys::Sig;
 use duniter_dal::dal_event::DALEvent;
 use duniter_dal::dal_requests::{DALRequest, DALResponse};
 use duniter_documents::BlockId;
-use duniter_documents::BlockchainProtocol;
+use duniter_documents::DUBPDocument;
 use duniter_module::*;
 use duniter_network::{NetworkEvent, NetworkResponse, OldNetworkRequest};
 
@@ -89,7 +89,7 @@ pub enum DursMsgContent {
     /// Pow module response
     ProverResponse(BlockId, Sig, u64),
     /// Client API event
-    ReceiveDocsFromClient(Vec<BlockchainProtocol>),
+    ReceiveDocsFromClient(Vec<DUBPDocument>),
     /// Stop signal
     Stop(),
 }