use dup_crypto::keys::*;
use pest::Parser;
use crate::blockstamp::Blockstamp;
use crate::documents::*;
use crate::text_document_traits::*;
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct CompactCertificationDocument {
pub issuer: PubKey,
pub target: PubKey,
pub block_number: BlockId,
pub signature: Sig,
}
impl CompactTextDocument for CompactCertificationDocument {
fn as_compact_text(&self) -> String {
format!(
"{issuer}:{target}:{block_number}:{signature}",
issuer = self.issuer,
target = self.target,
block_number = self.block_number.0,
signature = self.signature,
)
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct CertificationDocument {
text: String,
currency: String,
issuers: Vec<PubKey>,
target: PubKey,
identity_username: String,
identity_blockstamp: Blockstamp,
identity_sig: Sig,
blockstamp: Blockstamp,
signatures: Vec<Sig>,
}
#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
pub struct CertificationStringDocument {
currency: String,
issuer: String,
target: String,
identity_username: String,
identity_blockstamp: String,
identity_sig: String,
blockstamp: String,
signature: String,
}
impl ToStringObject for CertificationDocument {
type StringObject = CertificationStringDocument;
fn to_string_object(&self) -> CertificationStringDocument {
CertificationStringDocument {
currency: self.currency.clone(),
issuer: format!("{}", self.issuers[0]),
target: format!("{}", self.target),
identity_username: self.identity_username.clone(),
identity_blockstamp: format!("{}", self.identity_blockstamp),
blockstamp: format!("{}", self.blockstamp),
identity_sig: format!("{}", self.identity_sig),
signature: format!("{}", self.signatures[0]),
}
}
}
impl CertificationDocument {
pub fn identity_username(&self) -> &str {
&self.identity_username
}
pub fn source(&self) -> &PubKey {
&self.issuers[0]
}
pub fn target(&self) -> &PubKey {
&self.target
}
}
impl Document for CertificationDocument {
type PublicKey = PubKey;
type CurrencyType = str;
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 TextDocument for CertificationDocument {
type CompactTextDocument_ = CompactCertificationDocument;
fn as_text(&self) -> &str {
&self.text
}
fn to_compact_document(&self) -> Self::CompactTextDocument_ {
CompactCertificationDocument {
issuer: self.issuers[0],
target: self.target,
block_number: self.blockstamp().id,
signature: self.signatures()[0],
}
}
}
impl IntoSpecializedDocument<DUBPDocument> for CertificationDocument {
fn into_specialized(self) -> DUBPDocument {
DUBPDocument::Certification(Box::new(self))
}
}
#[derive(Debug, Copy, Clone)]
pub struct CertificationDocumentBuilder<'a> {
pub currency: &'a str,
pub issuer: &'a PubKey,
pub blockstamp: &'a Blockstamp,
pub target: &'a PubKey,
pub identity_username: &'a str,
pub identity_blockstamp: &'a Blockstamp,
pub identity_sig: &'a Sig,
}
impl<'a> CertificationDocumentBuilder<'a> {
fn build_with_text_and_sigs(self, text: String, signatures: Vec<Sig>) -> CertificationDocument {
CertificationDocument {
text,
currency: self.currency.to_string(),
issuers: vec![*self.issuer],
blockstamp: *self.blockstamp,
target: *self.target,
identity_username: self.identity_username.to_string(),
identity_blockstamp: *self.identity_blockstamp,
identity_sig: *self.identity_sig,
signatures,
}
}
}
impl<'a> DocumentBuilder for CertificationDocumentBuilder<'a> {
type Document = CertificationDocument;
type PrivateKey = PrivKey;
fn build_with_signature(&self, signatures: Vec<Sig>) -> CertificationDocument {
self.build_with_text_and_sigs(self.generate_text(), signatures)
}
fn build_and_sign(&self, private_keys: Vec<PrivKey>) -> CertificationDocument {
let (text, signatures) = self.build_signed_text(private_keys);
self.build_with_text_and_sigs(text, signatures)
}
}
impl<'a> TextDocumentBuilder for CertificationDocumentBuilder<'a> {
fn generate_text(&self) -> String {
format!(
"Version: 10
Type: Certification
Currency: {currency}
Issuer: {issuer}
IdtyIssuer: {target}
IdtyUniqueID: {idty_uid}
IdtyTimestamp: {idty_blockstamp}
IdtySignature: {idty_sig}
CertTimestamp: {blockstamp}
",
currency = self.currency,
issuer = self.issuer,
target = self.target,
idty_uid = self.identity_username,
idty_blockstamp = self.identity_blockstamp,
idty_sig = self.identity_sig,
blockstamp = self.blockstamp,
)
}
}
#[derive(Debug, Clone, Copy)]
pub struct CertificationDocumentParser;
impl TextDocumentParser<Rule> for CertificationDocumentParser {
type DocumentType = CertificationDocument;
fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
match DocumentsParser::parse(Rule::cert, doc) {
Ok(mut cert_pairs) => {
let cert_pair = cert_pairs.next().unwrap();
let cert_vx_pair = cert_pair.into_inner().next().unwrap();
match cert_vx_pair.as_rule() {
Rule::cert_v10 => Ok(CertificationDocumentParser::from_pest_pair(cert_vx_pair)),
_ => Err(TextDocumentParseError::UnexpectedVersion(format!(
"{:#?}",
cert_vx_pair.as_rule()
))),
}
}
Err(pest_error) => panic!("{}", pest_error),
}
}
fn from_pest_pair(pair: Pair<Rule>) -> Self::DocumentType {
let doc = pair.as_str();
let mut currency = "";
let mut pubkeys = Vec::with_capacity(2);
let mut uid = "";
let mut sigs = Vec::with_capacity(2);
let mut blockstamps = Vec::with_capacity(2);
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: BlockId(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 => (),
_ => panic!("unexpected rule"),
}
}
CertificationDocument {
text: doc.to_owned(),
issuers: vec![pubkeys[0]],
currency: currency.to_owned(),
target: pubkeys[1],
identity_username: uid.to_owned(),
identity_blockstamp: blockstamps[0],
identity_sig: sigs[0],
blockstamp: blockstamps[1],
signatures: vec![sigs[1]],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::VerificationResult;
use dup_crypto::keys::{PrivateKey, PublicKey, Signature};
#[test]
fn generate_real_document() {
let pubkey = PubKey::Ed25519(
ed25519::PublicKey::from_base58("4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR")
.unwrap(),
);
let prikey = PrivKey::Ed25519(ed25519::PrivateKey::from_base58(
"3XGWuuU1dQ7zaYPzE76ATfY71STzRkbT3t4DE1bSjMhYje81XdJFeXVG9uMPi3oDeRTosT2dmBAFH8VydrAUWXRZ",
).unwrap());
let sig = Sig::Ed25519(ed25519::Signature::from_base64(
"qfR6zqT1oJbqIsppOi64gC9yTtxb6g6XA9RYpulkq9ehMvqg2VYVigCbR0yVpqKFsnYiQTrnjgFuFRSJCJDfCw==",
).unwrap());
let target = PubKey::Ed25519(
ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV")
.unwrap(),
);
let identity_blockstamp = Blockstamp::from_string(
"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
)
.unwrap();
let identity_sig = Sig::Ed25519(ed25519::Signature::from_base64(
"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
).unwrap());
let blockstamp = Blockstamp::from_string(
"36-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B865",
)
.unwrap();
let builder = CertificationDocumentBuilder {
currency: "duniter_unit_test_currency",
issuer: &pubkey,
target: &target,
identity_username: "tic",
identity_blockstamp: &identity_blockstamp,
identity_sig: &identity_sig,
blockstamp: &blockstamp,
};
assert_eq!(
builder.build_with_signature(vec![sig]).verify_signatures(),
VerificationResult::Valid()
);
assert_eq!(
builder.build_and_sign(vec![prikey]).verify_signatures(),
VerificationResult::Valid()
);
}
#[test]
fn certification_document() {
let doc = "Version: 10
Type: Certification
Currency: g1-test
Issuer: 5B8iMAzq1dNmFe3ZxFTBQkqhq4fsztg1gZvxHXCk1XYH
IdtyIssuer: mMPioknj2MQCX9KyKykdw8qMRxYR2w1u3UpdiEJHgXg
IdtyUniqueID: mmpio
IdtyTimestamp: 7543-000044410C5370DE8DBA911A358F318096B7A269CFC2BB93272E397CC513EA0A
IdtySignature: SmSweUD4lEMwiZfY8ux9maBjrQQDkC85oMNsin6oSQCPdXG8sFCZ4FisUaWqKsfOlZVb/HNa+TKzD2t0Yte+DA==
CertTimestamp: 167884-0001DFCA28002A8C96575E53B8CEF8317453A7B0BA255542CCF0EC8AB5E99038
wqZxPEGxLrHGv8VdEIfUGvUcf+tDdNTMXjLzVRCQ4UhlhDRahOMjfcbP7byNYr5OfIl83S1MBxF7VJgu8YasCA==";
let doc = CertificationDocumentParser::parse(doc).unwrap();
println!("Doc : {:?}", doc);
assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
}
}