use dup_crypto::keys::*;
use durs_common_tools::fatal_error;
use crate::blockstamp::Blockstamp;
use crate::documents::*;
use crate::text_document_traits::*;
#[derive(Debug, Deserialize, Clone, Copy, Hash, Serialize, PartialEq, Eq)]
pub enum MembershipType {
In(),
Out(),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct MembershipDocumentV10 {
text: Option<String>,
currency: String,
issuers: Vec<PubKey>,
blockstamp: Blockstamp,
membership: MembershipType,
identity_username: String,
identity_blockstamp: Blockstamp,
signatures: Vec<Sig>,
}
#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
pub struct MembershipDocumentV10Stringified {
pub currency: String,
pub issuer: String,
pub blockstamp: String,
pub membership: String,
pub username: String,
pub identity_blockstamp: String,
pub signature: String,
}
impl ToStringObject for MembershipDocumentV10 {
type StringObject = MembershipDocumentV10Stringified;
fn to_string_object(&self) -> MembershipDocumentV10Stringified {
MembershipDocumentV10Stringified {
currency: self.currency.clone(),
issuer: format!("{}", self.issuers[0]),
blockstamp: format!("{}", self.blockstamp),
membership: match self.membership {
MembershipType::In() => "IN".to_owned(),
MembershipType::Out() => "OUT".to_owned(),
},
username: self.identity_username.clone(),
identity_blockstamp: format!("{}", self.identity_blockstamp),
signature: format!("{}", self.signatures[0]),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Hash, Deserialize, Serialize)]
pub enum MembershipEventType {
Join(),
Renewal(),
Rejoin(),
Expire(),
}
#[derive(Debug, Clone, PartialEq, Hash, Deserialize, Serialize)]
pub struct MembershipEvent {
pub blockstamp: Blockstamp,
pub doc: MembershipDocumentV10,
pub event_type: MembershipEventType,
pub chainable_on: u64,
}
impl MembershipDocumentV10 {
pub fn membership(&self) -> MembershipType {
self.membership
}
pub fn identity_username(&self) -> &str {
&self.identity_username
}
pub fn reduce(&mut self) {
self.text = None;
}
pub fn from_pest_pair(
pair: Pair<Rule>,
) -> Result<MembershipDocumentV10, TextDocumentParseError> {
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();
let block_id: &str = inner_rules.next().unwrap().as_str();
let block_hash: &str = inner_rules.next().unwrap().as_str();
blockstamps.push(Blockstamp {
id: BlockNumber(block_id.parse().unwrap()),
hash: BlockHash(Hash::from_hex(block_hash).unwrap()),
});
}
Rule::ed25519_sig => sig_str = field.as_str(),
Rule::EOI => (),
_ => fatal_error!("unexpected rule"),
}
}
Ok(MembershipDocumentV10 {
text: Some(doc.to_owned()),
issuers: vec![PubKey::Ed25519(
ed25519::PublicKey::from_base58(pubkey_str).unwrap(),
)],
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(),
)],
})
}
}
impl Document for MembershipDocumentV10 {
type PublicKey = PubKey;
fn version(&self) -> u16 {
10
}
fn currency(&self) -> &str {
&self.currency
}
fn blockstamp(&self) -> Blockstamp {
self.blockstamp
}
fn issuers(&self) -> &Vec<PubKey> {
&self.issuers
}
fn signatures(&self) -> &Vec<Sig> {
&self.signatures
}
fn as_bytes(&self) -> &[u8] {
self.as_text_without_signature().as_bytes()
}
}
impl CompactTextDocument for MembershipDocumentV10 {
fn as_compact_text(&self) -> String {
format!(
"{issuer}:{signature}:{blockstamp}:{idty_blockstamp}:{username}",
issuer = self.issuers[0],
signature = self.signatures[0],
blockstamp = self.blockstamp,
idty_blockstamp = self.identity_blockstamp,
username = self.identity_username,
)
}
}
#[derive(Copy, Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
pub struct CompactPoolMembershipDoc {
pub blockstamp: Blockstamp,
pub signature: Sig,
}
impl TextDocument for MembershipDocumentV10 {
type CompactTextDocument_ = MembershipDocumentV10;
fn as_text(&self) -> &str {
if let Some(ref text) = self.text {
text
} else {
fatal_error!("Try to get text of reduce membership !")
}
}
fn to_compact_document(&self) -> Self::CompactTextDocument_ {
self.clone()
}
}
#[derive(Debug, Copy, Clone)]
pub struct MembershipDocumentV10Builder<'a> {
pub currency: &'a str,
pub issuer: &'a PubKey,
pub blockstamp: &'a Blockstamp,
pub membership: MembershipType,
pub identity_username: &'a str,
pub identity_blockstamp: &'a Blockstamp,
}
impl<'a> MembershipDocumentV10Builder<'a> {
fn build_with_text_and_sigs(self, text: String, signatures: Vec<Sig>) -> MembershipDocumentV10 {
MembershipDocumentV10 {
text: Some(text),
currency: self.currency.to_string(),
issuers: vec![*self.issuer],
blockstamp: *self.blockstamp,
membership: self.membership,
identity_username: self.identity_username.to_string(),
identity_blockstamp: *self.identity_blockstamp,
signatures,
}
}
}
impl<'a> DocumentBuilder for MembershipDocumentV10Builder<'a> {
type Document = MembershipDocumentV10;
type PrivateKey = PrivKey;
fn build_with_signature(&self, signatures: Vec<Sig>) -> MembershipDocumentV10 {
self.build_with_text_and_sigs(self.generate_text(), signatures)
}
fn build_and_sign(&self, private_keys: Vec<PrivKey>) -> MembershipDocumentV10 {
let (text, signatures) = self.build_signed_text(private_keys);
self.build_with_text_and_sigs(text, signatures)
}
}
impl<'a> TextDocumentBuilder for MembershipDocumentV10Builder<'a> {
fn generate_text(&self) -> String {
format!(
"Version: 10
Type: Membership
Currency: {currency}
Issuer: {issuer}
Block: {blockstamp}
Membership: {membership}
UserID: {username}
CertTS: {ity_blockstamp}
",
currency = self.currency,
issuer = self.issuer,
blockstamp = self.blockstamp,
membership = match self.membership {
MembershipType::In() => "IN",
MembershipType::Out() => "OUT",
},
username = self.identity_username,
ity_blockstamp = self.identity_blockstamp,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use dup_crypto::keys::{PrivateKey, PublicKey, Signature};
#[test]
fn generate_real_document() {
let pubkey = PubKey::Ed25519(
ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV")
.unwrap(),
);
let prikey = PrivKey::Ed25519(
ed25519::PrivateKey::from_base58(
"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\
iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7",
)
.unwrap(),
);
let sig = Sig::Ed25519(
ed25519::Signature::from_base64(
"s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkEl\
AyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==",
)
.unwrap(),
);
let block = Blockstamp::from_string(
"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
)
.unwrap();
let builder = MembershipDocumentV10Builder {
currency: "duniter_unit_test_currency",
issuer: &pubkey,
blockstamp: &block,
membership: MembershipType::In(),
identity_username: "tic",
identity_blockstamp: &block,
};
assert!(builder
.build_with_signature(vec![sig])
.verify_signatures()
.is_ok());
assert!(builder
.build_and_sign(vec![prikey])
.verify_signatures()
.is_ok());
}
#[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 doc = MembershipDocumentParser::parse(doc).unwrap();
println!("Doc : {:?}", doc);
assert!(doc.verify_signatures().is_ok());
assert_eq!(
doc.generate_compact_text(),
"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:\
s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==:\
0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\
0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\
tic"
);
}
}