use crate::*;
#[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,
issuer: ed25519::PublicKey,
blockstamp: Blockstamp,
membership: MembershipType,
identity_username: String,
identity_blockstamp: Blockstamp,
signature: ed25519::Signature,
}
#[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: self.issuer.to_base58(),
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: self.signature.to_base64(),
}
}
}
#[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;
}
}
impl Document for MembershipDocumentV10 {
type PublicKey = ed25519::PublicKey;
fn version(&self) -> usize {
10
}
fn currency(&self) -> &str {
&self.currency
}
fn blockstamp(&self) -> Blockstamp {
self.blockstamp
}
fn issuers(&self) -> SmallVec<[Self::PublicKey; 1]> {
svec![self.issuer]
}
fn signatures(&self) -> SmallVec<[<Self::PublicKey as PublicKey>::Signature; 1]> {
svec![self.signature]
}
fn as_bytes(&self) -> BeefCow<[u8]> {
BeefCow::borrowed(self.as_text().as_bytes())
}
}
impl CompactTextDocument for MembershipDocumentV10 {
fn as_compact_text(&self) -> String {
format!(
"{issuer}:{signature}:{blockstamp}:{idty_blockstamp}:{username}",
issuer = self.issuer,
signature = self.signature,
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 {
panic!("Try to get text of reduce membership !")
}
}
fn to_compact_document(&self) -> Cow<Self::CompactTextDocument_> {
Cow::Borrowed(self)
}
}
#[derive(Debug, Copy, Clone)]
pub struct MembershipDocumentV10Builder<'a> {
pub currency: &'a str,
pub issuer: ed25519::PublicKey,
pub blockstamp: Blockstamp,
pub membership: MembershipType,
pub identity_username: &'a str,
pub identity_blockstamp: Blockstamp,
}
impl<'a> TextDocumentBuilder for MembershipDocumentV10Builder<'a> {
type Document = MembershipDocumentV10;
type Signator = ed25519::Signator;
fn build_with_text_and_sigs(
self,
text: String,
signatures: SmallVec<
[<<Self::Document as Document>::PublicKey as PublicKey>::Signature; 1],
>,
) -> MembershipDocumentV10 {
MembershipDocumentV10 {
text: Some(text),
currency: self.currency.to_string(),
issuer: self.issuer,
blockstamp: self.blockstamp,
membership: self.membership,
identity_username: self.identity_username.to_string(),
identity_blockstamp: self.identity_blockstamp,
signature: signatures[0],
}
}
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 smallvec::smallvec;
use std::str::FromStr;
use unwrap::unwrap;
#[test]
fn generate_real_document() {
let keypair = ed25519::KeyPairFromSeed32Generator::generate(unwrap!(
Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
"fail to build Seed32"
));
let pubkey = keypair.public_key();
let signator = keypair.generate_signator();
let sig = unwrap!(ed25519::Signature::from_base64(
"cUgoc8AI+Tae/AZmRfTnW+xq3XFtmYoUi2LXlmXr8/7LaXiUccQb8+Ds1nZoBp/8+t031HMwqAUpVIqww2FGCg==",
), "fail to build Signature");
let blockstamp = unwrap!(
Blockstamp::from_str(
"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
),
"fail to build Blockstamp"
);
let builder = MembershipDocumentV10Builder {
currency: "duniter_unit_test_currency",
issuer: pubkey,
blockstamp,
membership: MembershipType::In(),
identity_username: "tic",
identity_blockstamp: blockstamp,
};
assert!(builder
.build_with_signature(smallvec![sig])
.verify_signatures()
.is_ok());
assert!(builder
.build_and_sign(vec![signator])
.verify_signatures()
.is_ok());
}
}