extern crate base64;
extern crate pow_sha256;
use crate::dtc::error::*;
use pow_sha256::PoW;
pub static DIFFICULTY: u128 = 5;
pub static DTC_HEADER: &str = "Data-Tracker-Chain";
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MarkerIdentifier {
pub data_id: String,
pub index: usize,
pub timestamp: u64,
pub actor_id: String,
pub previous_hash: String,
}
impl MarkerIdentifier {
pub fn serialize(&self) -> String {
serde_json::to_string(&self).unwrap()
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Marker {
pub identifier: MarkerIdentifier,
pub hash: String,
nonce: u128,
}
impl Marker {
pub fn new(
idx: usize,
tmstp: u64,
act_id: String,
dat_id: String,
prev_hash: String,
) -> Marker {
let idfy = MarkerIdentifier {
data_id: dat_id,
index: idx,
timestamp: tmstp,
actor_id: act_id,
previous_hash: prev_hash,
};
Marker {
identifier: idfy.clone(),
hash: Marker::calculate_hash(idfy, DIFFICULTY).result,
nonce: DIFFICULTY,
}
}
fn calculate_hash(idfy: MarkerIdentifier, difficulty: u128) -> PoW<MarkerIdentifier> {
PoW::prove_work(&idfy, difficulty).unwrap()
}
pub fn genesis(dat_id: String) -> Marker {
let idfy = MarkerIdentifier {
data_id: dat_id,
index: 0,
timestamp: 0,
actor_id: "".to_string(),
previous_hash: "0".to_string(),
};
Marker {
identifier: idfy.clone(),
hash: Marker::calculate_hash(idfy, DIFFICULTY).result,
nonce: DIFFICULTY,
}
}
pub fn serialize(&self) -> String {
serde_json::to_string(&self).unwrap()
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tracker {
chain: Vec<Marker>,
}
impl Tracker {
pub fn new(dat_id: String) -> Tracker {
let mut tracker = Tracker { chain: Vec::new() };
tracker.chain.push(Marker::genesis(dat_id));
tracker
}
pub fn add(&mut self, tmstp: u64, act_id: String, dat_id: String) {
let prior_marker = self.chain[self.chain.len() - 1].clone();
let marker = Marker::new(self.chain.len(), tmstp, act_id, dat_id, prior_marker.hash);
self.chain.push(marker);
}
pub fn from_serialized(serialized: &str) -> Result<Tracker, Error> {
match serde_json::from_str(&serialized) {
Ok(v) => Ok(Tracker { chain: v }),
Err(_e) => Err(Error::BadChain),
}
}
pub fn get(&self, index: usize) -> Option<Marker> {
if index < self.chain.len() {
return Some(self.chain[index].clone());
}
None
}
pub fn is_empty(&self) -> bool {
self.chain.len() == 0
}
pub fn is_valid(&self) -> bool {
debug!("Validating chain ...");
for (m, marker) in self.chain.clone().iter().enumerate() {
debug!("Checking Marker #{}", m);
if marker.hash != Marker::calculate_hash(marker.clone().identifier, DIFFICULTY).result {
return false;
}
if m > 0 && marker.identifier.previous_hash != self.chain.clone()[m - 1].hash {
return false;
}
}
true
}
pub fn len(&self) -> usize {
self.chain.len()
}
pub fn serialize(&self) -> String {
serde_json::to_string(&self.chain.clone()).unwrap()
}
}
pub mod error;
#[cfg(test)]
mod tests {
use super::*;
fn get_marker() -> Marker {
Marker::new(
1,
1578071239,
"notifier~billing~receipt~email".to_string(),
"order~clothing~iStore~15150".to_string(),
"123456".to_string(),
)
}
#[test]
fn test_calc_hash() {
let _ = env_logger::builder().is_test(true).try_init();
let marker = get_marker();
let pw = Marker::calculate_hash(marker.identifier, marker.nonce);
assert!(pw.is_sufficient_difficulty(marker.nonce));
}
#[test]
fn test_marker_new() {
let mkr = get_marker();
assert_eq!(mkr.identifier.index, 1);
}
#[test]
fn test_marker_genesis() {
let mkr = Marker::genesis("order~clothing~iStore~15150".to_string());
assert_eq!(mkr.identifier.index, 0);
}
#[test]
fn test_markerchain_get() {
let mut mkrchn = Tracker::new("order~clothing~iStore~15150".to_string());
mkrchn.add(
1578071239,
"notifier~billing~receipt~email".to_string(),
"order~clothing~iStore~15150".to_string(),
);
assert!(mkrchn.get(0).is_some());
assert!(mkrchn.get(1).is_some());
assert!(mkrchn.get(2).is_none());
}
#[test]
fn test_markerchain_from_serialized() {
let mkrchn = Tracker::from_serialized(
r#"[{"identifier":{"data_id":"order~clothing~iStore~15150","index":0,"timestamp":0,"actor_id":"","previous_hash":"0"},"hash":"272081696611464773728024926793703167782","nonce":5},{"identifier":{"data_id":"order~clothing~iStore~15150","index":1,"timestamp":1578071239,"actor_id":"notifier~billing~receipt~email","previous_hash":"272081696611464773728024926793703167782"},"hash":"50104149701098700632511144125867736193","nonce":5}]"#,
);
assert!(mkrchn.is_ok());
}
#[test]
fn test_markerchain_new() {
let mkrchn = Tracker::new("order~clothing~iStore~15150".to_string());
assert_eq!(mkrchn.len(), 1);
}
#[test]
fn test_markerchain_serialize() {
let mut mkrchn = Tracker::new("order~clothing~iStore~15150".to_string());
mkrchn.add(
1578071239,
"notifier~billing~receipt~email".to_string(),
"order~clothing~iStore~15150".to_string(),
);
assert!(mkrchn.serialize().len() > 0);
}
#[test]
fn test_markerchain_valid() {
let mut mkrchn = Tracker::new("order~clothing~iStore~15150".to_string());
mkrchn.add(
1578071239,
"notifier~billing~receipt~email".to_string(),
"order~clothing~iStore~15150".to_string(),
);
assert!(Tracker::is_valid(&mkrchn));
}
#[test]
fn test_markerchain_invalid() {
let mut tracker = Tracker::new("purchaseId=12345".to_string());
tracker.add(
1578071239,
"payment-validator".to_string(),
"purchaseId=12345".to_string(),
);
tracker.add(
1578071245,
"credit-card-transaction-processor".to_string(),
"purchaseId=12345".to_string(),
);
let mut markerchain: Vec<Marker> = serde_json::from_str(&tracker.serialize()).unwrap();
markerchain[1].identifier.actor_id = "tampered data".to_string();
let serialized = serde_json::to_string(&markerchain).unwrap();
let tracker_tampered = Tracker::from_serialized(&serialized).unwrap();
assert_eq!(Tracker::is_valid(&tracker_tampered), false);
}
}