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(), )),