use dup_crypto::keys::*;
use crate::blockstamp::Blockstamp;
use crate::documents::*;
use crate::text_document_traits::*;
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct CompactRevocationDocumentV10 {
pub issuer: PubKey,
pub signature: Sig,
}
impl CompactTextDocument for CompactRevocationDocumentV10 {
fn as_compact_text(&self) -> String {
format!(
"{issuer}:{signature}",
issuer = self.issuer,
signature = self.signature,
)
}
}
#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
pub struct CompactRevocationDocumentV10Stringified {
pub issuer: String,
pub signature: String,
}
impl ToStringObject for CompactRevocationDocumentV10 {
type StringObject = CompactRevocationDocumentV10Stringified;
fn to_string_object(&self) -> CompactRevocationDocumentV10Stringified {
CompactRevocationDocumentV10Stringified {
issuer: format!("{}", self.issuer),
signature: format!("{}", self.signature),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct RevocationDocumentV10 {
text: String,
currency: String,
issuers: Vec<PubKey>,
identity_username: String,
identity_blockstamp: Blockstamp,
identity_sig: Sig,
signatures: Vec<Sig>,
}
#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
pub struct RevocationDocumentV10Stringified {
currency: String,
issuer: String,
identity_username: String,
identity_blockstamp: String,
identity_sig: String,
signature: String,
}
impl ToStringObject for RevocationDocumentV10 {
type StringObject = RevocationDocumentV10Stringified;
fn to_string_object(&self) -> RevocationDocumentV10Stringified {
RevocationDocumentV10Stringified {
currency: self.currency.clone(),
issuer: format!("{}", self.issuers[0]),
identity_username: self.identity_username.clone(),
identity_blockstamp: format!("{}", self.identity_blockstamp),
identity_sig: format!("{}", self.identity_sig),
signature: format!("{}", self.signatures[0]),
}
}
}
impl RevocationDocumentV10 {
pub fn identity_username(&self) -> &str {
&self.identity_username
}
pub fn from_pest_pair(
pair: Pair<Rule>,
) -> Result<RevocationDocumentV10, TextDocumentParseError> {
let doc = pair.as_str();
let mut currency = "";
let mut pubkeys = Vec::with_capacity(1);
let mut uid = "";
let mut sigs = Vec::with_capacity(2);
let mut blockstamps = Vec::with_capacity(1);
for field in pair.into_inner() {
match field.as_rule() {
Rule::currency => currency = field.as_str(),
Rule::pubkey => pubkeys.push(PubKey::Ed25519(
ed25519::PublicKey::from_base58(field.as_str()).unwrap(),
)),
Rule::uid => {
uid = field.as_str();
}
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 => {
sigs.push(Sig::Ed25519(
ed25519::Signature::from_base64(field.as_str()).unwrap(),
));
}
Rule::EOI => (),
_ => fatal_error!("unexpected rule"),
}
}
Ok(RevocationDocumentV10 {
text: doc.to_owned(),
issuers: vec![pubkeys[0]],
currency: currency.to_owned(),
identity_username: uid.to_owned(),
identity_blockstamp: blockstamps[0],
identity_sig: sigs[0],
signatures: vec![sigs[1]],
})
}
}
impl Document for RevocationDocumentV10 {
type PublicKey = PubKey;
fn version(&self) -> u16 {
10
}
fn currency(&self) -> &str {
&self.currency
}
fn blockstamp(&self) -> Blockstamp {
unimplemented!()
}
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 TextDocument for RevocationDocumentV10 {
type CompactTextDocument_ = CompactRevocationDocumentV10;
fn as_text(&self) -> &str {
&self.text
}
fn to_compact_document(&self) -> Self::CompactTextDocument_ {
CompactRevocationDocumentV10 {
issuer: self.issuers[0],
signature: self.signatures[0],
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct RevocationDocumentV10Builder<'a> {
pub currency: &'a str,
pub issuer: &'a PubKey,
pub identity_username: &'a str,
pub identity_blockstamp: &'a Blockstamp,
pub identity_sig: &'a Sig,
}
impl<'a> RevocationDocumentV10Builder<'a> {
fn build_with_text_and_sigs(self, text: String, signatures: Vec<Sig>) -> RevocationDocumentV10 {
RevocationDocumentV10 {
text,
currency: self.currency.to_string(),
issuers: vec![*self.issuer],
identity_username: self.identity_username.to_string(),
identity_blockstamp: *self.identity_blockstamp,
identity_sig: *self.identity_sig,
signatures,
}
}
}
impl<'a> DocumentBuilder for RevocationDocumentV10Builder<'a> {
type Document = RevocationDocumentV10;
type PrivateKey = PrivKey;
fn build_with_signature(&self, signatures: Vec<Sig>) -> RevocationDocumentV10 {
self.build_with_text_and_sigs(self.generate_text(), signatures)
}
fn build_and_sign(&self, private_keys: Vec<PrivKey>) -> RevocationDocumentV10 {
let (text, signatures) = self.build_signed_text(private_keys);
self.build_with_text_and_sigs(text, signatures)
}
}
impl<'a> TextDocumentBuilder for RevocationDocumentV10Builder<'a> {
fn generate_text(&self) -> String {
format!(
"Version: 10
Type: Revocation
Currency: {currency}
Issuer: {issuer}
IdtyUniqueID: {idty_uid}
IdtyTimestamp: {idty_blockstamp}
IdtySignature: {idty_sig}
",
currency = self.currency,
issuer = self.issuer,
idty_uid = self.identity_username,
idty_blockstamp = self.identity_blockstamp,
idty_sig = self.identity_sig,
)
}
}
#[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(
"XXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==",
).unwrap());
let identity_blockstamp = Blockstamp::from_string(
"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
)
.unwrap();
let identity_sig = Sig::Ed25519(ed25519::Signature::from_base64(
"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
).unwrap());
let builder = RevocationDocumentV10Builder {
currency: "g1",
issuer: &pubkey,
identity_username: "tic",
identity_blockstamp: &identity_blockstamp,
identity_sig: &identity_sig,
};
assert!(builder
.build_with_signature(vec![sig])
.verify_signatures()
.is_ok());
assert!(builder
.build_and_sign(vec![prikey])
.verify_signatures()
.is_ok());
}
#[test]
fn revocation_document() {
let doc = "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 = RevocationDocumentParser::parse(doc).unwrap();
println!("Doc : {:?}", doc);
assert!(doc.verify_signatures().is_ok())
}
}