#![deny(
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces
)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate pest_derive;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate serde_derive;
pub mod blockstamp;
pub mod documents;
pub mod parsers;
pub mod text_document_traits;
use dup_crypto::hashs::Hash;
use dup_crypto::keys::*;
use pest::iterators::Pair;
use pest::RuleType;
use serde::Serialize;
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Error, Formatter};
use std::net::AddrParseError;
pub use crate::blockstamp::{Blockstamp, PreviousBlockstamp};
#[derive(Parser)]
#[grammar = "documents_grammar.pest"]
struct DocumentsParser;
pub trait TextDocumentParser<R: RuleType> {
type DocumentType;
fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError>;
fn from_pest_pair(pairs: Pair<R>) -> Result<Self::DocumentType, TextDocumentParseError>;
fn from_versioned_pest_pair(
version: u16,
pairs: Pair<R>,
) -> Result<Self::DocumentType, TextDocumentParseError>;
}
#[derive(Debug, Clone, Eq, Fail, PartialEq)]
#[fail(display = "Grammar error: {}", _0)]
pub struct PestError(pub String);
impl<T: pest::RuleType> From<pest::error::Error<T>> for PestError {
fn from(e: pest::error::Error<T>) -> Self {
PestError(format!("{}", e))
}
}
#[derive(Debug, Clone, Eq, Fail, PartialEq)]
pub enum TextDocumentParseError {
#[fail(display = "TextDocumentParseError: Invalid inner format: {}", _0)]
InvalidInnerFormat(String),
#[fail(display = "TextDocumentParseError: invalid ip: {}", _0)]
IpAddrError(AddrParseError),
#[fail(display = "TextDocumentParseError: {}", _0)]
PestError(PestError),
#[fail(display = "TextDocumentParseError: Unexpected rule: '{}'", _0)]
UnexpectedRule(String),
#[fail(display = "TextDocumentParseError: Unexpected version: '{}'", _0)]
UnexpectedVersion(String),
#[fail(display = "TextDocumentParseError: UnknownType.")]
UnknownType,
}
impl From<AddrParseError> for TextDocumentParseError {
fn from(e: AddrParseError) -> Self {
TextDocumentParseError::IpAddrError(e)
}
}
impl From<PestError> for TextDocumentParseError {
fn from(e: PestError) -> Self {
TextDocumentParseError::PestError(e)
}
}
impl<T: pest::RuleType> From<pest::error::Error<T>> for TextDocumentParseError {
fn from(e: pest::error::Error<T>) -> Self {
TextDocumentParseError::PestError(e.into())
}
}
#[derive(Copy, Clone, Debug, Deserialize, Ord, PartialEq, PartialOrd, Eq, Hash, Serialize)]
pub struct BlockNumber(pub u32);
impl Display for BlockNumber {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.0)
}
}
#[derive(Copy, Clone, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Hash, Serialize)]
pub struct BlockHash(pub Hash);
impl Display for BlockHash {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.0.to_hex())
}
}
impl Debug for BlockHash {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "BlockHash({})", self)
}
}
pub trait Document: Debug + Clone + PartialEq + Eq {
type PublicKey: PublicKey;
fn as_bytes(&self) -> &[u8];
fn blockstamp(&self) -> Blockstamp;
fn currency(&self) -> &str;
fn issuers(&self) -> &Vec<Self::PublicKey>;
fn no_as_bytes(&self) -> bool {
false
}
fn to_bytes(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
fn signatures(&self) -> &Vec<<Self::PublicKey as PublicKey>::Signature>;
#[inline]
fn verify_one_signature(
&self,
public_key: &Self::PublicKey,
signature: &<Self::PublicKey as PublicKey>::Signature,
) -> Result<(), SigError> {
if self.no_as_bytes() {
public_key.verify(&self.to_bytes(), signature)
} else {
public_key.verify(self.as_bytes(), signature)
}
}
fn verify_signatures(&self) -> Result<(), DocumentSigsErr> {
let issuers_count = self.issuers().len();
let signatures_count = self.signatures().len();
if issuers_count != signatures_count {
Err(DocumentSigsErr::IncompletePairs(
issuers_count,
signatures_count,
))
} else {
let issuers = self.issuers();
let signatures = self.signatures();
let mismatches: HashMap<usize, SigError> = issuers
.iter()
.zip(signatures)
.enumerate()
.filter_map(|(i, (key, signature))| {
if let Err(e) = self.verify_one_signature(key, signature) {
Some((i, e))
} else {
None
}
})
.collect();
if mismatches.is_empty() {
Ok(())
} else {
Err(DocumentSigsErr::Invalid(mismatches))
}
}
}
fn version(&self) -> u16;
}
use std::collections::HashMap;
#[derive(Debug, Eq, PartialEq)]
pub enum DocumentSigsErr {
IncompletePairs(usize, usize),
Invalid(HashMap<usize, SigError>),
}
pub trait DocumentBuilder {
type Document: Document;
type PrivateKey: PrivateKey<
Signature = <<Self::Document as Document>::PublicKey as PublicKey>::Signature,
>;
fn build_with_signature(
&self,
signatures: Vec<<<Self::Document as Document>::PublicKey as PublicKey>::Signature>,
) -> Self::Document;
fn build_and_sign(&self, private_keys: Vec<Self::PrivateKey>) -> Self::Document;
}
pub trait DocumentParser<S, D, E> {
fn parse(source: S) -> Result<D, E>;
}
pub trait ToStringObject {
type StringObject: Serialize;
fn to_string_object(&self) -> Self::StringObject;
}
pub trait ToJsonObject: ToStringObject {
fn to_json_string(&self) -> Result<String, serde_json::Error> {
Ok(serde_json::to_string(&self.to_string_object())?)
}
fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
Ok(serde_json::to_string_pretty(&self.to_string_object())?)
}
}
impl<T: ToStringObject> ToJsonObject for T {}
#[cfg(test)]
mod tests {
use super::*;
use crate::documents::UserDocumentDUBP;
#[test]
fn parse_dubp_document() {
let text = "Version: 10
Type: Identity
Currency: g1
Issuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx
UniqueID: elois
Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==";
let doc = UserDocumentDUBP::parse(text).expect("Fail to parse UserDocumentDUBP !");
println!("Doc : {:?}", doc);
}
}