diff --git a/protocol/blockchain/v10/documents/membership.rs b/protocol/blockchain/v10/documents/membership.rs
index 76dc1d8b779ebcdaec342ca5f97c7ecd1ca95e8a..07153b79a886e6ef368ae14953be5e3bb03ba444 100644
--- a/protocol/blockchain/v10/documents/membership.rs
+++ b/protocol/blockchain/v10/documents/membership.rs
@@ -23,6 +23,16 @@ use blockchain::{BlockchainProtocol, Document, DocumentBuilder, IntoSpecializedD
use blockchain::v10::documents::{StandardTextDocumentParser, TextDocument, TextDocumentBuilder,
V10Document, V10DocumentParsingError};
+lazy_static! {
+ static ref MEMBERSHIP_REGEX: Regex = Regex::new(
+ "^Issuer: (?P<issuer>[1-9A-Za-z][^OIl]{43,44})\n\
+Block: (?P<blockstamp>[0-9]+-[0-9A-F]{64})\n\
+Membership: (?P<membership>(IN|OUT))\n\
+UserID: (?P<ity_user>[[:alnum:]_-]+)\n\
+CertTS: (?P<ity_block>[0-9]+-[0-9A-F]{64})\n$"
+ ).unwrap();
+}
+
/// Type of a Membership.
#[derive(Debug, Clone, Copy)]
pub enum MembershipType {
@@ -38,7 +48,7 @@ pub enum MembershipType {
#[derive(Debug, Clone)]
pub struct MembershipDocument {
/// Document as text.
- ///
+ ///
/// Is used to check signatures, and other values mut be extracted from it.
text: String,
@@ -119,9 +129,9 @@ pub struct MembershipDocumentBuilder<'a> {
/// Membership message.
pub membership: MembershipType,
/// Identity username.
- pub identity_username: &'a str,
+ pub identity_username: &'a str,
/// Identity document blockstamp.
- pub identity_blockstamp: &'a Blockstamp,
+ pub identity_blockstamp: &'a Blockstamp,
}
impl<'a> MembershipDocumentBuilder<'a> {
@@ -138,7 +148,7 @@ impl<'a> MembershipDocumentBuilder<'a> {
membership: self.membership,
identity_username: self.identity_username.to_string(),
identity_blockstamp: *self.identity_blockstamp,
- signatures
+ signatures,
}
}
}
@@ -182,6 +192,55 @@ CertTS: {ity_blockstamp}
}
}
+/// Membership document parser
+#[derive(Debug, Clone, Copy)]
+pub struct MembershipDocumentParser;
+
+impl StandardTextDocumentParser for MembershipDocumentParser {
+ fn parse_standard(
+ doc: &str,
+ body: &str,
+ currency: &str,
+ signatures: Vec<ed25519::Signature>,
+ ) -> Result<V10Document, V10DocumentParsingError> {
+ if let Some(caps) = MEMBERSHIP_REGEX.captures(body) {
+ let issuer = &caps["issuer"];
+
+ let blockstamp = &caps["blockstamp"];
+ let membership = &caps["membership"];
+ let username = &caps["ity_user"];
+ let ity_block = &caps["ity_block"];
+
+ // Regex match so should not fail.
+ // TODO : Test it anyway
+ let issuer = ed25519::PublicKey::from_base58(issuer).unwrap();
+ let blockstamp = Blockstamp::from_string(blockstamp).unwrap();
+ let membership = match membership {
+ "IN" => MembershipType::In(),
+ "OUT" => MembershipType::Out(),
+ _ => panic!("Invalid membership type {}", membership),
+ };
+
+ let ity_block = Blockstamp::from_string(ity_block).unwrap();
+
+ Ok(V10Document::Membership(MembershipDocument {
+ text: doc.to_owned(),
+ issuers: vec![issuer],
+ currency: currency.to_owned(),
+ blockstamp: blockstamp,
+ membership,
+ identity_username: username.to_owned(),
+ identity_blockstamp: ity_block,
+ signatures,
+ }))
+ } else {
+ Err(V10DocumentParsingError::InvalidInnerFormat(
+ "Identity".to_string(),
+ ))
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -201,7 +260,7 @@ mod tests {
let sig = ed25519::Signature::from_base64(
"s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkEl\
- AyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==",
+ AyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==",
).unwrap();
let block = Blockstamp::from_string(
@@ -226,4 +285,44 @@ mod tests {
VerificationResult::Valid()
);
}
-}
\ No newline at end of file
+
+ #[test]
+ fn membership_standard_regex() {
+ assert!(MEMBERSHIP_REGEX.is_match(
+ "Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+Membership: IN
+UserID: tic
+CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+"
+ ));
+ }
+
+ #[test]
+ fn membership_identity_document() {
+ let doc = "Version: 10
+Type: Membership
+Currency: duniter_unit_test_currency
+Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+Membership: IN
+UserID: tic
+CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==";
+
+ let body = "Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+Membership: IN
+UserID: tic
+CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+";
+
+ let currency = "duniter_unit_test_currency";
+
+ let signatures = vec![Signature::from_base64(
+"s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw=="
+ ).unwrap(),];
+
+ let _ = MembershipDocumentParser::parse_standard(doc, body, currency, signatures).unwrap();
+ }
+}
diff --git a/protocol/blockchain/v10/documents/mod.rs b/protocol/blockchain/v10/documents/mod.rs
index 964bdd3e049e68f92f6f1f010e6beb3a665affdb..31ed9af9e8a2bd5b6661ad18e50a4616fe55b9e7 100644
--- a/protocol/blockchain/v10/documents/mod.rs
+++ b/protocol/blockchain/v10/documents/mod.rs
@@ -24,7 +24,7 @@ pub mod identity;
pub mod membership;
pub use blockchain::v10::documents::identity::{IdentityDocument, IdentityDocumentBuilder};
-pub use blockchain::v10::documents::membership::MembershipDocument;
+pub use blockchain::v10::documents::membership::{MembershipDocument, MembershipDocumentParser};
// Use of lazy_static so the regex is only compiled at first use.
lazy_static! {
@@ -166,6 +166,7 @@ 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),
_ => Err(V10DocumentParsingError::UnknownDocumentType(
doctype.to_string(),
)),