diff --git a/Cargo.lock b/Cargo.lock
index 9648b789bb4d65f41aacf4c31367e66662587faa..8fd4d60894dfa6e1b161d27e51cc4946d444288c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -94,7 +94,7 @@ dependencies = [
 
 [[package]]
 name = "duniter-documents"
-version = "0.5.0"
+version = "0.6.0"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/documents/Cargo.toml b/documents/Cargo.toml
index 2a42d7fd9d4e01bde39d80f0f12c663078a1707c..7200f0b650e9604ad2ccf75d6af14c05ca0867d3 100644
--- a/documents/Cargo.toml
+++ b/documents/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-documents"
-version = "0.5.0"
+version = "0.6.0"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@ifee.fr>"]
 description = "Handles Duniter documents"
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
diff --git a/documents/blockchain/v10/documents/certification.rs b/documents/blockchain/v10/documents/certification.rs
index d4de8631c1078f587aba3679dbe8328c294b82ac..46a4467fd11853a1ff10f24ae86304546bbedd1d 100644
--- a/documents/blockchain/v10/documents/certification.rs
+++ b/documents/blockchain/v10/documents/certification.rs
@@ -191,6 +191,16 @@ CertTimestamp: {blockstamp}
             blockstamp = self.blockstamp,
         )
     }
+
+    fn generate_compact_text(&self, signatures: Vec<ed25519::Signature>) -> String {
+        format!(
+            "{issuer}:{target}:{block_number}:{signature}",
+            issuer = self.issuer,
+            target = self.target,
+            block_number = self.blockstamp.id.0,
+            signature = signatures[0],
+        )
+    }
 }
 
 /// Certification document parser
@@ -297,6 +307,12 @@ mod tests {
             builder.build_and_sign(vec![prikey]).verify_signatures(),
             VerificationResult::Valid()
         );
+        assert_eq!(
+            builder.generate_compact_text(vec![sig]),
+            "4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR:\
+            DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:36:\
+            qfR6zqT1oJbqIsppOi64gC9yTtxb6g6XA9RYpulkq9ehMvqg2VYVigCbR0yVpqKFsnYiQTrnjgFuFRSJCJDfCw=="
+        );
     }
 
     #[test]
diff --git a/documents/blockchain/v10/documents/identity.rs b/documents/blockchain/v10/documents/identity.rs
index ac7622ec78cb27ce47dfb36c28668dd7f2980b35..855d620557fb0abd8f9add187d5c3da157afeaa6 100644
--- a/documents/blockchain/v10/documents/identity.rs
+++ b/documents/blockchain/v10/documents/identity.rs
@@ -43,7 +43,7 @@ pub struct IdentityDocument {
     /// Currency.
     currency: String,
     /// Unique ID
-    unique_id: String,
+    username: String,
     /// Blockstamp
     blockstamp: Blockstamp,
     /// Document issuer (there should be only one).
@@ -54,8 +54,8 @@ pub struct IdentityDocument {
 
 impl IdentityDocument {
     /// Unique ID
-    pub fn unique_id(&self) -> &str {
-        &self.unique_id
+    pub fn username(&self) -> &str {
+        &self.username
     }
 }
 
@@ -102,7 +102,7 @@ pub struct IdentityDocumentBuilder<'a> {
     /// Document currency.
     pub currency: &'a str,
     /// Identity unique id.
-    pub unique_id: &'a str,
+    pub username: &'a str,
     /// Reference blockstamp.
     pub blockstamp: &'a Blockstamp,
     /// Document/identity issuer.
@@ -118,7 +118,7 @@ impl<'a> IdentityDocumentBuilder<'a> {
         IdentityDocument {
             text,
             currency: self.currency.to_string(),
-            unique_id: self.unique_id.to_string(),
+            username: self.username.to_string(),
             blockstamp: *self.blockstamp,
             issuers: vec![*self.issuer],
             signatures,
@@ -147,15 +147,25 @@ impl<'a> TextDocumentBuilder for IdentityDocumentBuilder<'a> {
 Type: Identity
 Currency: {currency}
 Issuer: {issuer}
-UniqueID: {unique_id}
+UniqueID: {username}
 Timestamp: {blockstamp}
 ",
             currency = self.currency,
             issuer = self.issuer,
-            unique_id = self.unique_id,
+            username = self.username,
             blockstamp = self.blockstamp
         )
     }
+
+    fn generate_compact_text(&self, signatures: Vec<ed25519::Signature>) -> String {
+        format!(
+            "{issuer}:{signature}:{blockstamp}:{username}",
+            issuer = self.issuer,
+            signature = signatures[0],
+            blockstamp = self.blockstamp,
+            username = self.username,
+        )
+    }
 }
 
 /// Identity document parser
@@ -182,7 +192,7 @@ impl StandardTextDocumentParser for IdentityDocumentParser {
             Ok(V10Document::Identity(IdentityDocument {
                 text: doc.to_owned(),
                 currency: currency.to_owned(),
-                unique_id: uid.to_owned(),
+                username: uid.to_owned(),
                 blockstamp,
                 issuers: vec![issuer],
                 signatures,
@@ -223,7 +233,7 @@ mod tests {
 
         let builder = IdentityDocumentBuilder {
             currency: "duniter_unit_test_currency",
-            unique_id: "tic",
+            username: "tic",
             blockstamp: &block,
             issuer: &pubkey,
         };
diff --git a/documents/blockchain/v10/documents/membership.rs b/documents/blockchain/v10/documents/membership.rs
index 0fab5443d53f047616407afbc153dc491c60289d..9b9d41668d5e36940d14d5e0b4466427298353a8 100644
--- a/documents/blockchain/v10/documents/membership.rs
+++ b/documents/blockchain/v10/documents/membership.rs
@@ -190,6 +190,17 @@ CertTS: {ity_blockstamp}
             ity_blockstamp = self.identity_blockstamp,
         )
     }
+
+    fn generate_compact_text(&self, signatures: Vec<ed25519::Signature>) -> String {
+        format!(
+            "{issuer}:{signature}:{blockstamp}:{idty_blockstamp}:{username}",
+            issuer = self.issuer,
+            signature = signatures[0],
+            blockstamp = self.blockstamp,
+            idty_blockstamp = self.identity_blockstamp,
+            username = self.identity_username,
+        )
+    }
 }
 
 /// Membership document parser
@@ -284,6 +295,14 @@ mod tests {
             builder.build_and_sign(vec![prikey]).verify_signatures(),
             VerificationResult::Valid()
         );
+        assert_eq!(
+            builder.generate_compact_text(vec![sig]),
+            "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:\
+            s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==:\
+            0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\
+            0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\
+            tic"
+        );
     }
 
     #[test]
diff --git a/documents/blockchain/v10/documents/mod.rs b/documents/blockchain/v10/documents/mod.rs
index d2eb531b296e7c3314300e3a35427ae71bedd497..8a02f2ee0cdfb65debe2d972ebc944e57f75da06 100644
--- a/documents/blockchain/v10/documents/mod.rs
+++ b/documents/blockchain/v10/documents/mod.rs
@@ -28,17 +28,20 @@ pub mod identity;
 pub mod membership;
 pub mod certification;
 pub mod revocation;
+pub mod transaction;
 
 pub use blockchain::v10::documents::identity::{IdentityDocument, IdentityDocumentBuilder};
 pub use blockchain::v10::documents::membership::{MembershipDocument, MembershipDocumentParser};
 pub use blockchain::v10::documents::certification::{CertificationDocument,
                                                     CertificationDocumentParser};
 pub use blockchain::v10::documents::revocation::{RevocationDocument, RevocationDocumentParser};
+pub use blockchain::v10::documents::transaction::{TransactionDocument, TransactionDocumentBuilder,
+                                                  TransactionDocumentParser};
 
 // Use of lazy_static so the regex is only compiled at first use.
 lazy_static! {
     static ref DOCUMENT_REGEX: Regex = Regex::new(
-        "^(?P<doc>Version: 10\n\
+        "^(?P<doc>Version: (?P<version>[0-9]+)\n\
          Type: (?P<type>[[:alpha:]]+)\n\
          Currency: (?P<currency>[[:alnum:] _-]+)\n\
          (?P<body>(?:.*\n)+?))\
@@ -56,7 +59,7 @@ pub enum V10Document {
     Block(),
 
     /// Transaction document.
-    Transaction(),
+    Transaction(Box<TransactionDocument>),
 
     /// Identity document.
     Identity(IdentityDocument),
@@ -102,6 +105,13 @@ pub trait TextDocumentBuilder: DocumentBuilder {
     /// - Contains line breaks on all line.
     fn generate_text(&self) -> String;
 
+    /// Generate document compact text.
+    /// the compact format is the one used in the blocks.
+    ///
+    /// - Don't contains leading signatures
+    /// - Contains line breaks on all line.
+    fn generate_compact_text(&self, signatures: Vec<ed25519::Signature>) -> String;
+
     /// Generate final document with signatures, and also return them in an array.
     ///
     /// Returns :
@@ -139,6 +149,19 @@ pub enum V10DocumentParsingError {
     UnknownDocumentType(String),
 }
 
+/// V10 Documents in separated parts
+#[derive(Debug, Clone)]
+pub struct V10DocumentParts {
+    /// Whole document in text
+    pub doc: String,
+    /// Payload
+    pub body: String,
+    /// Currency
+    pub currency: String,
+    /// Signatures
+    pub signatures: Vec<ed25519::Signature>,
+}
+
 trait StandardTextDocumentParser {
     fn parse_standard(
         doc: &str,
@@ -148,15 +171,6 @@ trait StandardTextDocumentParser {
     ) -> Result<V10Document, V10DocumentParsingError>;
 }
 
-trait CompactTextDocumentParser<D: TextDocument> {
-    fn parse_compact(
-        doc: &str,
-        body: &str,
-        currency: &str,
-        signatures: Vec<ed25519::Signature>,
-    ) -> Result<D, V10DocumentParsingError>;
-}
-
 /// A V10 document parser.
 #[derive(Debug, Clone, Copy)]
 pub struct V10DocumentParser;
@@ -178,6 +192,13 @@ impl<'a> DocumentParser<&'a str, V10Document, V10DocumentParsingError> for V10Do
             match doctype {
                 "Identity" => IdentityDocumentParser::parse_standard(doc, body, currency, sigs),
                 "Membership" => MembershipDocumentParser::parse_standard(doc, body, currency, sigs),
+                "Certification" => {
+                    CertificationDocumentParser::parse_standard(doc, body, currency, sigs)
+                }
+                "Revocation" => RevocationDocumentParser::parse_standard(doc, body, currency, sigs),
+                "Transaction" => {
+                    TransactionDocumentParser::parse_standard(doc, body, currency, sigs)
+                }
                 _ => Err(V10DocumentParsingError::UnknownDocumentType(
                     doctype.to_string(),
                 )),
@@ -275,11 +296,11 @@ SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneN
     fn parse_identity_document() {
         let text = "Version: 10
 Type: Identity
-Currency: duniter_unit_test_currency
-Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
-UniqueID: tic
+Currency: g1
+Issuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx
+UniqueID: elois
 Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
-1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==";
+Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==";
 
         let doc = V10DocumentParser::parse(text).unwrap();
         if let V10Document::Identity(doc) = doc {
@@ -294,13 +315,13 @@ Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
     fn parse_membership_document() {
         let text = "Version: 10
 Type: Membership
-Currency: duniter_unit_test_currency
-Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+Currency: g1
+Issuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx
 Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
 Membership: IN
-UserID: tic
+UserID: elois
 CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
-s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==";
+FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==";
 
         let doc = V10DocumentParser::parse(text).unwrap();
         if let V10Document::Membership(doc) = doc {
@@ -310,4 +331,75 @@ s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrD
             panic!("Wrong document type");
         }
     }
+
+    #[test]
+    fn parse_certification_document() {
+        let text = "Version: 10
+Type: Certification
+Currency: g1
+Issuer: 2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT
+IdtyIssuer: 7jzkd8GiFnpys4X7mP78w2Y3y3kwdK6fVSLEaojd3aH9
+IdtyUniqueID: fbarbut
+IdtyTimestamp: 98221-000000575AC04F5164F7A307CDB766139EA47DD249E4A2444F292BC8AAB408B3
+IdtySignature: DjeipIeb/RF0tpVCnVnuw6mH1iLJHIsDfPGLR90Twy3PeoaDz6Yzhc/UjLWqHCi5Y6wYajV0dNg4jQRUneVBCQ==
+CertTimestamp: 99956-00000472758331FDA8388E30E50CA04736CBFD3B7C21F34E74707107794B56DD
+Hkps1QU4HxIcNXKT8YmprYTVByBhPP1U2tIM7Z8wENzLKIWAvQClkAvBE7pW9dnVa18sJIJhVZUcRrPAZfmjBA==";
+
+        let doc = V10DocumentParser::parse(text).unwrap();
+        if let V10Document::Certification(doc) = doc {
+            println!("Doc : {:?}", doc);
+            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
+        } else {
+            panic!("Wrong document type");
+        }
+    }
+
+    #[test]
+    fn parse_revocation_document() {
+        let text = "Version: 10
+Type: Revocation
+Currency: g1
+Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+IdtyUniqueID: tic
+IdtyTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+IdtySignature: 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==
+XXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==";
+
+        let doc = V10DocumentParser::parse(text).unwrap();
+        if let V10Document::Revocation(doc) = doc {
+            println!("Doc : {:?}", doc);
+            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
+        } else {
+            panic!("Wrong document type");
+        }
+    }
+
+    #[test]
+    fn parse_transaction_document() {
+        let text = "Version: 10
+Type: Transaction
+Currency: g1
+Blockstamp: 107702-0000017CDBE974DC9A46B89EE7DC2BEE4017C43A005359E0853026C21FB6A084
+Locktime: 0
+Issuers:
+Do6Y6nQ2KTo5fB4MXbSwabXVmXHxYRB9UUAaTPKn1XqC
+Inputs:
+1002:0:D:Do6Y6nQ2KTo5fB4MXbSwabXVmXHxYRB9UUAaTPKn1XqC:104937
+1002:0:D:Do6Y6nQ2KTo5fB4MXbSwabXVmXHxYRB9UUAaTPKn1XqC:105214
+Unlocks:
+0:SIG(0)
+1:SIG(0)
+Outputs:
+2004:0:SIG(DTgQ97AuJ8UgVXcxmNtULAs8Fg1kKC1Wr9SAS96Br9NG)
+Comment: c est pour 2 mois d adhesion ressourcerie
+lnpuFsIymgz7qhKF/GsZ3n3W8ZauAAfWmT4W0iJQBLKJK2GFkesLWeMj/+GBfjD6kdkjreg9M6VfkwIZH+hCCQ==";
+
+        let doc = V10DocumentParser::parse(text).unwrap();
+        if let V10Document::Transaction(doc) = doc {
+            println!("Doc : {:?}", doc);
+            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
+        } else {
+            panic!("Wrong document type");
+        }
+    }
 }
diff --git a/documents/blockchain/v10/documents/revocation.rs b/documents/blockchain/v10/documents/revocation.rs
index 6b7538b9c472913d74821518400a7ce985e9e077..3760f6369157a7044318ee0d5a56f13c513ed10d 100644
--- a/documents/blockchain/v10/documents/revocation.rs
+++ b/documents/blockchain/v10/documents/revocation.rs
@@ -165,6 +165,13 @@ IdtySignature: {idty_sig}
             idty_sig = self.identity_sig,
         )
     }
+    fn generate_compact_text(&self, signatures: Vec<ed25519::Signature>) -> String {
+        format!(
+            "{issuer}:{signature}",
+            issuer = self.issuer,
+            signature = signatures[0],
+        )
+    }
 }
 
 /// Revocation document parser
diff --git a/documents/blockchain/v10/documents/transaction.rs b/documents/blockchain/v10/documents/transaction.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f71372d95da17f2ef45607fe45b2f2806c05a8c1
--- /dev/null
+++ b/documents/blockchain/v10/documents/transaction.rs
@@ -0,0 +1,851 @@
+//  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/>.
+
+//! Wrappers around Transaction documents.
+
+use std::ops::Deref;
+
+use duniter_crypto::keys::{PublicKey, ed25519};
+use regex::Regex;
+use regex::RegexBuilder;
+
+use Blockstamp;
+use blockchain::{BlockchainProtocol, Document, DocumentBuilder, IntoSpecializedDocument};
+use blockchain::v10::documents::{StandardTextDocumentParser, TextDocument, TextDocumentBuilder,
+                                 V10Document, V10DocumentParsingError};
+
+lazy_static! {
+    static ref TRANSACTION_REGEX_SIZE: &'static usize = &40_000_000;
+    static ref TRANSACTION_REGEX_BUILDER: &'static str =
+        r"^Blockstamp: (?P<blockstamp>[0-9]+-[0-9A-F]{64})\nLocktime: (?P<locktime>[0-9]+)\nIssuers:(?P<issuers>(?:\n[1-9A-Za-z][^OIl]{43,44})+)Inputs:\n(?P<inputs>([0-9A-Za-z:]+\n)+)Unlocks:\n(?P<unlocks>([0-9]+:(SIG\([0-9]+\) ?|XHX\(\w+\) ?)+\n)+)Outputs:\n(?P<outputs>([0-9A-Za-z()&|: ]+\n)+)Comment:(?P<comment>[\\\w:/;*\[\]()?!^+=@&~#{}|<>%. -]{0,255})\n$";
+    static ref ISSUER_REGEX: Regex = Regex::new("(?P<issuer>[1-9A-Za-z][^OIl]{43,44})\n").unwrap();
+    static ref D_INPUT_REGEX: Regex = Regex::new(
+        "^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):D:(?P<pubkey>[1-9A-Za-z][^OIl]{43,44}):(?P<block_number>[0-9]+)$"
+    ).unwrap();
+    static ref T_INPUT_REGEX: Regex = Regex::new(
+        "^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):T:(?P<tx_hash>[0-9A-F]{64}):(?P<tx_index>[0-9]+)$"
+    ).unwrap();
+    static ref UNLOCKS_REGEX: Regex = Regex::new(
+        r"^(?P<index>[0-9]+):(?P<unlocks>(SIG\([0-9]+\) ?|XHX\(\w+\) ?)+)$"
+    ).unwrap();
+    static ref UNLOCK_SIG_REGEX: Regex =
+        Regex::new(r"^SIG\((?P<index>[0-9]+)\)$").unwrap();
+    static ref UNLOCK_XHX_REGEX: Regex = Regex::new(r"^XHX\((?P<code>\w+)\)$").unwrap();
+    static ref OUTPUT_COND_SIG_REGEX: Regex = Regex::new(r"^SIG\((?P<pubkey>[1-9A-Za-z][^OIl]{43,44})\)$").unwrap();
+    static ref OUTPUT_COND_XHX_REGEX: Regex = Regex::new(r"^XHX\((?P<hash>[0-9A-F]{64})\)$").unwrap();
+    static ref OUTPUT_COND_CLTV_REGEX: Regex = Regex::new(r"^CLTV\((?P<timestamp>[0-9]+)\)$").unwrap();
+    static ref OUTPUT_COND_CSV_REGEX: Regex = Regex::new(r"^CSV\((?P<timestamp>[0-9]+)\)$").unwrap();
+    static ref OUPUT_CONDS_BRAKETS: Regex = Regex::new(r"^\((?P<conditions>[0-9A-Za-z()&| ]+)\)$").unwrap();
+    static ref OUPUT_CONDS_AND: Regex = Regex::new(r"^(?P<conditions_group_1>[0-9A-Za-z()&| ]+) && (?P<conditions_group_2>[0-9A-Za-z()&| ]+)$").unwrap();
+    static ref OUPUT_CONDS_OR: Regex = Regex::new(r"^(?P<conditions_group_1>[0-9A-Za-z()&| ]+) \|\| (?P<conditions_group_2>[0-9A-Za-z()&| ]+)$").unwrap();
+    static ref OUTPUT_REGEX: Regex = Regex::new(
+        "^(?P<amount>[1-9][0-9]+):(?P<base>[0-9]+):(?P<conditions>[0-9A-Za-z()&| ]+)$"
+    ).unwrap();
+}
+
+/// Wrap a transaction input
+#[derive(Debug, Clone)]
+pub enum TransactionInput {
+    /// Universal Dividend Input
+    D(isize, usize, ed25519::PublicKey, u64),
+    /// Previous Transaction Input
+    T(isize, usize, String, usize),
+}
+
+impl ToString for TransactionInput {
+    fn to_string(&self) -> String {
+        match *self {
+            TransactionInput::D(amount, base, pubkey, block_number) => {
+                format!("{}:{}:D:{}:{}", amount, base, pubkey, block_number)
+            }
+            TransactionInput::T(amount, base, ref tx_hash, tx_index) => {
+                format!("{}:{}:T:{}:{}", amount, base, tx_hash, tx_index)
+            }
+        }
+    }
+}
+
+impl TransactionInput {
+    fn parse_from_str(source: &str) -> Result<TransactionInput, V10DocumentParsingError> {
+        if let Some(caps) = D_INPUT_REGEX.captures(source) {
+            let amount = &caps["amount"];
+            let base = &caps["base"];
+            let pubkey = &caps["pubkey"];
+            let block_number = &caps["block_number"];
+            Ok(TransactionInput::D(
+                amount.parse().expect("fail to parse input amount !"),
+                base.parse().expect("fail to parse input base !"),
+                ed25519::PublicKey::from_base58(pubkey).expect("fail to parse input pubkey !"),
+                block_number
+                    .parse()
+                    .expect("fail to parse input block_number !"),
+            ))
+        //Ok(TransactionInput::D(10, 0, PublicKey::from_base58("FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa").unwrap(), 0))
+        } else if let Some(caps) = T_INPUT_REGEX.captures(source) {
+            let amount = &caps["amount"];
+            let base = &caps["base"];
+            let tx_hash = &caps["tx_hash"];
+            let tx_index = &caps["tx_index"];
+            Ok(TransactionInput::T(
+                amount.parse().expect("fail to parse input amount"),
+                base.parse().expect("fail to parse base amount"),
+                String::from(tx_hash),
+                tx_index.parse().expect("fail to parse tx_index amount"),
+            ))
+        } else {
+            println!("Fail to parse this input = {:?}", source);
+            Err(V10DocumentParsingError::InvalidInnerFormat(String::from(
+                "Transaction2",
+            )))
+        }
+    }
+}
+
+/// Wrap a transaction unlock proof
+#[derive(Debug, Clone)]
+pub enum TransactionUnlockProof {
+    /// Indicates that the signature of the corresponding key is at the bottom of the document
+    Sig(usize),
+    /// Provides the code to unlock the corresponding funds
+    Xhx(String),
+}
+
+impl ToString for TransactionUnlockProof {
+    fn to_string(&self) -> String {
+        match *self {
+            TransactionUnlockProof::Sig(ref index) => format!("SIG({})", index),
+            TransactionUnlockProof::Xhx(ref hash) => format!("XHX({})", hash),
+        }
+    }
+}
+
+impl TransactionUnlockProof {
+    fn parse_from_str(source: &str) -> Result<TransactionUnlockProof, V10DocumentParsingError> {
+        if let Some(caps) = UNLOCK_SIG_REGEX.captures(source) {
+            let index = &caps["index"];
+            Ok(TransactionUnlockProof::Sig(
+                index.parse().expect("fail to parse SIG unlock"),
+            ))
+        } else if let Some(caps) = UNLOCK_XHX_REGEX.captures(source) {
+            let code = &caps["code"];
+            Ok(TransactionUnlockProof::Xhx(String::from(code)))
+        } else {
+            Err(V10DocumentParsingError::InvalidInnerFormat(String::from(
+                "Transaction3",
+            )))
+        }
+    }
+}
+
+/// Wrap a transaction unlocks input
+#[derive(Debug, Clone)]
+pub struct TransactionInputUnlocks {
+    /// Input index
+    pub index: usize,
+    /// List of proof to unlock funds
+    pub unlocks: Vec<TransactionUnlockProof>,
+}
+
+impl ToString for TransactionInputUnlocks {
+    fn to_string(&self) -> String {
+        let mut result: String = format!("{}:", self.index);
+        for unlock in &self.unlocks {
+            result.push_str(&format!("{} ", unlock.to_string()));
+        }
+        let new_size = result.len() - 1;
+        result.truncate(new_size);
+        result
+    }
+}
+
+impl TransactionInputUnlocks {
+    fn parse_from_str(source: &str) -> Result<TransactionInputUnlocks, V10DocumentParsingError> {
+        if let Some(caps) = UNLOCKS_REGEX.captures(source) {
+            let index = &caps["index"].parse().expect("fail to parse unlock index");
+            let unlocks = &caps["unlocks"];
+
+            let unlocks_array: Vec<&str> = unlocks.split(' ').collect();
+            let mut unlocks = Vec::new();
+            for unlock in unlocks_array {
+                unlocks.push(TransactionUnlockProof::parse_from_str(unlock)?);
+            }
+            Ok(TransactionInputUnlocks {
+                index: *index,
+                unlocks,
+            })
+        } else {
+            println!("Fail to parse this unlock = {:?}", source);
+            Err(V10DocumentParsingError::InvalidInnerFormat(String::from(
+                "Transaction4",
+            )))
+        }
+    }
+}
+
+/// Wrap a transaction ouput condition
+#[derive(Debug, Clone)]
+pub enum TransactionOuputCondition {
+    /// The consumption of funds will require a valid signature of the specified key
+    Sig(ed25519::PublicKey),
+    /// The consumption of funds will require to provide a code with the hash indicated
+    Xhx(String),
+    /// Funds may not be consumed until the blockchain reaches the timestamp indicated.
+    Cltv(u64),
+    /// Funds may not be consumed before the duration indicated, starting from the timestamp of the block where the transaction is written.
+    Csv(u64),
+}
+
+impl ToString for TransactionOuputCondition {
+    fn to_string(&self) -> String {
+        match *self {
+            TransactionOuputCondition::Sig(ref pubkey) => format!("SIG({})", pubkey),
+            TransactionOuputCondition::Xhx(ref hash) => format!("XHX({})", hash),
+            TransactionOuputCondition::Cltv(timestamp) => format!("CLTV({})", timestamp),
+            TransactionOuputCondition::Csv(duration) => format!("CSV({})", duration),
+        }
+    }
+}
+
+impl TransactionOuputCondition {
+    fn parse_from_str(source: &str) -> Result<TransactionOuputCondition, V10DocumentParsingError> {
+        if let Some(caps) = OUTPUT_COND_SIG_REGEX.captures(source) {
+            Ok(TransactionOuputCondition::Sig(
+                ed25519::PublicKey::from_base58(&caps["pubkey"])
+                    .expect("fail to parse SIG TransactionOuputCondition"),
+            ))
+        } else if let Some(caps) = OUTPUT_COND_XHX_REGEX.captures(source) {
+            Ok(TransactionOuputCondition::Xhx(String::from(&caps["hash"])))
+        } else if let Some(caps) = OUTPUT_COND_CLTV_REGEX.captures(source) {
+            Ok(TransactionOuputCondition::Cltv(
+                caps["timestamp"]
+                    .parse()
+                    .expect("fail to parse CLTV TransactionOuputCondition"),
+            ))
+        } else if let Some(caps) = OUTPUT_COND_CSV_REGEX.captures(source) {
+            Ok(TransactionOuputCondition::Csv(
+                caps["duration"]
+                    .parse()
+                    .expect("fail to parse CSV TransactionOuputCondition"),
+            ))
+        } else {
+            Err(V10DocumentParsingError::InvalidInnerFormat(
+                "Transaction5".to_string(),
+            ))
+        }
+    }
+}
+
+/// Wrap a transaction ouput condition group
+#[derive(Debug, Clone)]
+pub enum TransactionOuputConditionGroup {
+    /// Single
+    Single(TransactionOuputCondition),
+    /// Brackets
+    Brackets(Box<TransactionOuputConditionGroup>),
+    /// And operator
+    And(
+        Box<TransactionOuputConditionGroup>,
+        Box<TransactionOuputConditionGroup>,
+    ),
+    /// Or operator
+    Or(
+        Box<TransactionOuputConditionGroup>,
+        Box<TransactionOuputConditionGroup>,
+    ),
+}
+
+impl ToString for TransactionOuputConditionGroup {
+    fn to_string(&self) -> String {
+        match *self {
+            TransactionOuputConditionGroup::Single(ref condition) => condition.to_string(),
+            TransactionOuputConditionGroup::Brackets(ref condition_group) => {
+                format!("({})", condition_group.deref().to_string())
+            }
+            TransactionOuputConditionGroup::And(ref condition_group_1, ref condition_group_2) => {
+                format!(
+                    "{} && {}",
+                    condition_group_1.deref().to_string(),
+                    condition_group_2.deref().to_string()
+                )
+            }
+            TransactionOuputConditionGroup::Or(ref condition_group_1, ref condition_group_2) => {
+                format!(
+                    "{} || {}",
+                    condition_group_1.deref().to_string(),
+                    condition_group_2.deref().to_string()
+                )
+            }
+        }
+    }
+}
+
+impl TransactionOuputConditionGroup {
+    fn parse_from_str(
+        conditions: &str,
+    ) -> Result<TransactionOuputConditionGroup, V10DocumentParsingError> {
+        if let Ok(single_condition) = TransactionOuputCondition::parse_from_str(conditions) {
+            Ok(TransactionOuputConditionGroup::Single(single_condition))
+        } else if let Some(caps) = OUPUT_CONDS_BRAKETS.captures(conditions) {
+            let inner_conditions =
+                TransactionOuputConditionGroup::parse_from_str(&caps["conditions"])?;
+            Ok(TransactionOuputConditionGroup::Brackets(Box::new(
+                inner_conditions,
+            )))
+        } else if let Some(caps) = OUPUT_CONDS_AND.captures(conditions) {
+            let conditions_group_1 =
+                TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_1"])?;
+            let conditions_group_2 =
+                TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_2"])?;
+            Ok(TransactionOuputConditionGroup::And(
+                Box::new(conditions_group_1),
+                Box::new(conditions_group_2),
+            ))
+        } else if let Some(caps) = OUPUT_CONDS_OR.captures(conditions) {
+            let conditions_group_1 =
+                TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_1"])?;
+            let conditions_group_2 =
+                TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_2"])?;
+            Ok(TransactionOuputConditionGroup::Or(
+                Box::new(conditions_group_1),
+                Box::new(conditions_group_2),
+            ))
+        } else {
+            println!("fail to parse this output condition = {:?}", conditions);
+            Err(V10DocumentParsingError::InvalidInnerFormat(String::from(
+                "Transaction6",
+            )))
+        }
+    }
+}
+
+/// Wrap a transaction ouput
+#[derive(Debug, Clone)]
+pub struct TransactionOuput {
+    /// Amount
+    pub amount: isize,
+    /// Base
+    pub base: usize,
+    /// List of conditions for consum this output
+    pub conditions: TransactionOuputConditionGroup,
+}
+
+impl ToString for TransactionOuput {
+    fn to_string(&self) -> String {
+        format!(
+            "{}:{}:{}",
+            self.amount,
+            self.base,
+            self.conditions.to_string()
+        )
+    }
+}
+
+impl TransactionOuput {
+    fn parse_from_str(source: &str) -> Result<TransactionOuput, V10DocumentParsingError> {
+        if let Some(caps) = OUTPUT_REGEX.captures(source) {
+            let amount = caps["amount"].parse().expect("fail to parse output amount");
+            let base = caps["base"].parse().expect("fail to parse base amount");
+            let conditions = TransactionOuputConditionGroup::parse_from_str(&caps["conditions"])?;
+            Ok(TransactionOuput {
+                conditions,
+                amount,
+                base,
+            })
+        } else {
+            Err(V10DocumentParsingError::InvalidInnerFormat(
+                "Transaction7".to_string(),
+            ))
+        }
+    }
+}
+
+/// Wrap a Transaction document.
+///
+/// Must be created by parsing a text document or using a builder.
+#[derive(Debug, Clone)]
+pub struct TransactionDocument {
+    /// Document as text.
+    ///
+    /// Is used to check signatures, and other values
+    /// must be extracted from it.
+    text: String,
+
+    /// Currency.
+    currency: String,
+    /// Blockstamp
+    blockstamp: Blockstamp,
+    /// Locktime
+    locktime: u64,
+    /// Document issuer (there should be only one).
+    issuers: Vec<ed25519::PublicKey>,
+    /// Transaction inputs.
+    inputs: Vec<TransactionInput>,
+    /// Inputs unlocks.
+    unlocks: Vec<TransactionInputUnlocks>,
+    /// Transaction outputs.
+    outputs: Vec<TransactionOuput>,
+    /// Transaction comment
+    comment: String,
+    /// Document signature (there should be only one).
+    signatures: Vec<ed25519::Signature>,
+}
+
+impl Document for TransactionDocument {
+    type PublicKey = ed25519::PublicKey;
+    type CurrencyType = str;
+
+    fn version(&self) -> u16 {
+        10
+    }
+
+    fn currency(&self) -> &str {
+        &self.currency
+    }
+
+    fn issuers(&self) -> &Vec<ed25519::PublicKey> {
+        &self.issuers
+    }
+
+    fn signatures(&self) -> &Vec<ed25519::Signature> {
+        &self.signatures
+    }
+
+    fn as_bytes(&self) -> &[u8] {
+        self.as_text().as_bytes()
+    }
+}
+
+impl TextDocument for TransactionDocument {
+    fn as_text(&self) -> &str {
+        &self.text
+    }
+}
+
+impl IntoSpecializedDocument<BlockchainProtocol> for TransactionDocument {
+    fn into_specialized(self) -> BlockchainProtocol {
+        BlockchainProtocol::V10(Box::new(V10Document::Transaction(Box::new(self))))
+    }
+}
+
+/// Transaction document builder.
+#[derive(Debug, Copy, Clone)]
+pub struct TransactionDocumentBuilder<'a> {
+    /// Document currency.
+    pub currency: &'a str,
+    /// Reference blockstamp.
+    pub blockstamp: &'a Blockstamp,
+    /// Locktime
+    pub locktime: &'a u64,
+    /// Transaction Document issuers.
+    pub issuers: &'a Vec<ed25519::PublicKey>,
+    /// Transaction inputs.
+    pub inputs: &'a Vec<TransactionInput>,
+    /// Inputs unlocks.
+    pub unlocks: &'a Vec<TransactionInputUnlocks>,
+    /// Transaction ouputs.
+    pub outputs: &'a Vec<TransactionOuput>,
+    /// Transaction comment
+    pub comment: &'a str,
+}
+
+impl<'a> TransactionDocumentBuilder<'a> {
+    fn build_with_text_and_sigs(
+        self,
+        text: String,
+        signatures: Vec<ed25519::Signature>,
+    ) -> TransactionDocument {
+        TransactionDocument {
+            text,
+            currency: self.currency.to_string(),
+            blockstamp: *self.blockstamp,
+            locktime: *self.locktime,
+            issuers: self.issuers.clone(),
+            inputs: self.inputs.clone(),
+            unlocks: self.unlocks.clone(),
+            outputs: self.outputs.clone(),
+            comment: String::from(self.comment),
+            signatures,
+        }
+    }
+}
+
+impl<'a> DocumentBuilder for TransactionDocumentBuilder<'a> {
+    type Document = TransactionDocument;
+    type PrivateKey = ed25519::PrivateKey;
+
+    fn build_with_signature(&self, signatures: Vec<ed25519::Signature>) -> TransactionDocument {
+        self.build_with_text_and_sigs(self.generate_text(), signatures)
+    }
+
+    fn build_and_sign(&self, private_keys: Vec<ed25519::PrivateKey>) -> TransactionDocument {
+        let (text, signatures) = self.build_signed_text(private_keys);
+        self.build_with_text_and_sigs(text, signatures)
+    }
+}
+
+impl<'a> TextDocumentBuilder for TransactionDocumentBuilder<'a> {
+    fn generate_text(&self) -> String {
+        let mut issuers_string: String = "".to_owned();
+        let mut inputs_string: String = "".to_owned();
+        let mut unlocks_string: String = "".to_owned();
+        let mut outputs_string: String = "".to_owned();
+        for issuer in self.issuers {
+            issuers_string.push_str(&format!("{}\n", issuer.to_string()))
+        }
+        for input in self.inputs {
+            inputs_string.push_str(&format!("{}\n", input.to_string()))
+        }
+        for unlock in self.unlocks {
+            unlocks_string.push_str(&format!("{}\n", unlock.to_string()))
+        }
+        for output in self.outputs {
+            outputs_string.push_str(&format!("{}\n", output.to_string()))
+        }
+        format!(
+            "Version: 10
+Type: Transaction
+Currency: {currency}
+Blockstamp: {blockstamp}
+Locktime: {locktime}
+Issuers:
+{issuers}Inputs:
+{inputs}Unlocks:
+{unlocks}Outputs:
+{outputs}Comment: {comment}
+",
+            currency = self.currency,
+            blockstamp = self.blockstamp,
+            locktime = self.locktime,
+            issuers = issuers_string,
+            inputs = inputs_string,
+            unlocks = unlocks_string,
+            outputs = outputs_string,
+            comment = self.comment,
+        )
+    }
+
+    fn generate_compact_text(&self, signatures: Vec<ed25519::Signature>) -> String {
+        let mut issuers_str = String::from("");
+        for issuer in self.issuers {
+            issuers_str.push_str(&issuer.to_string());
+        }
+        let mut inputs_str = String::from("");
+        for input in self.inputs {
+            inputs_str.push_str(&input.to_string());
+        }
+        let mut unlocks_str = String::from("");
+        for unlock in self.unlocks {
+            unlocks_str.push_str(&unlock.to_string());
+        }
+        let mut outputs_str = String::from("");
+        for output in self.outputs {
+            outputs_str.push_str(&output.to_string());
+        }
+        let mut signatures_str = String::from("");
+        for sig in signatures {
+            signatures_str.push_str(&sig.to_string());
+        }
+        format!(
+            "TX:10:{issuers_count}:{inputs_count}:{unlocks_count}:{outputs_count}:{has_comment}:{locktime}
+{blockstamp}
+{issuers}
+{inputs}
+{unlocks}
+{outputs}
+{comment}
+{signatures}",
+            issuers_count = self.issuers.len(),
+            inputs_count = self.inputs.len(),
+            unlocks_count = self.unlocks.len(),
+            outputs_count = self.outputs.len(),
+            has_comment = if self.comment.is_empty() { 0 } else { 1 },
+            locktime = self.locktime,
+            blockstamp = self.blockstamp,
+            issuers = issuers_str,
+            inputs = inputs_str,
+            unlocks = unlocks_str,
+            outputs = outputs_str,
+            comment = self.comment,
+            signatures = signatures_str,
+        )
+    }
+}
+
+/// Transaction document parser
+#[derive(Debug, Clone, Copy)]
+pub struct TransactionDocumentParser;
+
+impl StandardTextDocumentParser for TransactionDocumentParser {
+    fn parse_standard(
+        doc: &str,
+        body: &str,
+        currency: &str,
+        signatures: Vec<ed25519::Signature>,
+    ) -> Result<V10Document, V10DocumentParsingError> {
+        let tx_regex: Regex = RegexBuilder::new(&TRANSACTION_REGEX_BUILDER)
+            .size_limit(**TRANSACTION_REGEX_SIZE)
+            .build()
+            .expect("fail to build TRANSACTION_REGEX !");
+        if let Some(caps) = tx_regex.captures(body) {
+            let blockstamp =
+                Blockstamp::from_string(&caps["blockstamp"]).expect("fail to parse blockstamp");
+            let locktime = caps["locktime"].parse().expect("fail to parse locktime");
+            let issuers_str = &caps["issuers"];
+            let inputs = &caps["inputs"];
+            let unlocks = &caps["unlocks"];
+            let outputs = &caps["outputs"];
+            let comment = String::from(&caps["comment"]);
+
+            let mut issuers = Vec::new();
+            for caps in ISSUER_REGEX.captures_iter(issuers_str) {
+                println!("{:?}", &caps["issuer"]);
+                issuers.push(
+                    ed25519::PublicKey::from_base58(&caps["issuer"]).expect("fail to parse issuer"),
+                );
+            }
+            let inputs_array: Vec<&str> = inputs.split('\n').collect();
+            let mut inputs = Vec::new();
+            for input in inputs_array {
+                if !input.is_empty() {
+                    inputs.push(TransactionInput::parse_from_str(input)?);
+                }
+            }
+            let unlocks_array: Vec<&str> = unlocks.split('\n').collect();
+            let mut unlocks = Vec::new();
+            for unlock in unlocks_array {
+                if !unlock.is_empty() {
+                    unlocks.push(TransactionInputUnlocks::parse_from_str(unlock)?);
+                }
+            }
+            let outputs_array: Vec<&str> = outputs.split('\n').collect();
+            let mut outputs = Vec::new();
+            for output in outputs_array {
+                if !output.is_empty() {
+                    outputs.push(TransactionOuput::parse_from_str(output)?);
+                }
+            }
+
+            Ok(V10Document::Transaction(Box::new(TransactionDocument {
+                text: doc.to_owned(),
+                currency: currency.to_owned(),
+                blockstamp,
+                locktime,
+                issuers,
+                inputs,
+                unlocks,
+                outputs,
+                comment,
+                signatures,
+            })))
+        } else {
+            Err(V10DocumentParsingError::InvalidInnerFormat(
+                "Transaction1".to_string(),
+            ))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use duniter_crypto::keys::{PrivateKey, PublicKey, Signature};
+    use blockchain::{Document, VerificationResult};
+
+    #[test]
+    fn generate_real_document() {
+        let pubkey = ed25519::PublicKey::from_base58(
+            "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV",
+        ).unwrap();
+
+        let prikey = ed25519::PrivateKey::from_base58(
+            "468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\
+             iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7",
+        ).unwrap();
+
+        let sig = ed25519::Signature::from_base64(
+            "pRQeKlzCsvPNmYAAkEP5jPPQO1RwrtFMRfCajEfkkrG0UQE0DhoTkxG3Zs2JFmvAFLw67pn1V5NQ08zsSfJkBg==",
+        ).unwrap();
+
+        let block = Blockstamp::from_string(
+            "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
+        ).unwrap();
+
+        let builder = TransactionDocumentBuilder {
+            currency: "duniter_unit_test_currency",
+            blockstamp: &block,
+            locktime: &0,
+            issuers: &vec![pubkey],
+            inputs: &vec![
+                TransactionInput::parse_from_str(
+                    "10:0:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:0",
+                ).expect("fail to parse input !"),
+            ],
+            unlocks: &vec![
+                TransactionInputUnlocks::parse_from_str("0:SIG(0)")
+                    .expect("fail to parse unlock !"),
+            ],
+            outputs: &vec![
+                TransactionOuput::parse_from_str(
+                    "10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)",
+                ).expect("fail to parse output !"),
+            ],
+            comment: "test",
+        };
+        println!(
+            "Signature = {:?}",
+            builder.build_and_sign(vec![prikey]).signatures()
+        );
+        assert_eq!(
+            builder.build_with_signature(vec![sig]).verify_signatures(),
+            VerificationResult::Valid()
+        );
+        assert_eq!(
+            builder.build_and_sign(vec![prikey]).verify_signatures(),
+            VerificationResult::Valid()
+        );
+
+        assert_eq!(
+            builder.generate_compact_text(vec![sig]),
+            "TX:10:1:1:1:1:1:0
+0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+10:0:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:0
+0:SIG(0)
+10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)
+test
+pRQeKlzCsvPNmYAAkEP5jPPQO1RwrtFMRfCajEfkkrG0UQE0DhoTkxG3Zs2JFmvAFLw67pn1V5NQ08zsSfJkBg=="
+        );
+    }
+
+    #[test]
+    fn transaction_standard_regex() {
+        let tx_regex: Regex = RegexBuilder::new(&TRANSACTION_REGEX_BUILDER)
+            .size_limit(**TRANSACTION_REGEX_SIZE)
+            .build()
+            .expect("fail to build TRANSACTION_REGEX !");
+        assert!(tx_regex.is_match(
+            "Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
+Locktime: 0
+Issuers:
+HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
+CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp
+FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa
+Inputs:
+40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
+70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
+20:2:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:46
+70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
+20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
+15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46
+Unlocks:
+0:SIG(0)
+1:XHX(7665798292)
+2:SIG(0)
+3:SIG(0) SIG(2)
+4:SIG(0) SIG(1) SIG(2)
+5:SIG(2)
+Outputs:
+120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
+146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
+Comment: -----@@@----- (why not this comment?)
+"
+        ));
+    }
+
+    #[test]
+    fn parse_transaction_document() {
+        let doc = "Version: 10
+Type: Transaction
+Currency: duniter_unit_test_currency
+Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
+Locktime: 0
+Issuers:
+DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR
+FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa
+Inputs:
+40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
+70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
+20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46
+70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
+20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
+15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46
+Unlocks:
+0:SIG(0)
+1:XHX(7665798292)
+2:SIG(0)
+3:SIG(0) SIG(2)
+4:SIG(0) SIG(1) SIG(2)
+5:SIG(2)
+Outputs:
+120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
+146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA))
+Comment: -----@@@----- (why not this comment?)
+";
+
+        let body =
+            "Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
+Locktime: 0
+Issuers:
+DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR
+FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa
+Inputs:
+40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
+70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
+20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46
+70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
+20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
+15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46
+Unlocks:
+0:SIG(0)
+1:XHX(7665798292)
+2:SIG(0)
+3:SIG(0) SIG(2)
+4:SIG(0) SIG(1) SIG(2)
+5:SIG(2)
+Outputs:
+120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
+146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA))
+Comment: -----@@@----- (why not this comment?)
+";
+
+        let currency = "duniter_unit_test_currency";
+
+        let signatures = vec![
+            Signature::from_base64(
+"kL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw=="
+        ).expect("fail to parse test signature"),
+            Signature::from_base64(
+"e3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw=="
+            ).expect("fail to parse test signature"),
+            Signature::from_base64(
+"w69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw=="
+            ).expect("fail to parse test signature"),
+        ];
+
+        let doc = TransactionDocumentParser::parse_standard(doc, body, currency, signatures)
+            .expect("fail to parse test transaction document !");
+        if let V10Document::Transaction(doc) = doc {
+            println!("Doc : {:?}", doc);
+            assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
+        } else {
+            panic!("Wrong document type");
+        }
+    }
+}