use std::fmt::{self, Debug, Formatter};
use rustc_serialize::{Decoder, Encodable, Encoder};
use xor_name::{XorName, XOR_NAME_LEN};
use sodiumoxide::crypto::hash::sha512;
const NORMAL_TO_BACKUP: [u8; XOR_NAME_LEN] =
[128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const NORMAL_TO_SACRIFICIAL: [u8; XOR_NAME_LEN] = [255; XOR_NAME_LEN];
const BACKUP_TO_SACRIFICIAL: [u8; XOR_NAME_LEN] =
[127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255];
fn xor(lhs: &[u8; XOR_NAME_LEN], mut rhs: [u8; XOR_NAME_LEN]) -> XorName {
for i in 0..XOR_NAME_LEN {
rhs[i] ^= lhs[i];
}
XorName(rhs)
}
#[derive(Hash, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Debug)]
pub enum ImmutableDataType {
Normal,
Backup,
Sacrificial,
}
#[derive(Hash, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct ImmutableData {
type_tag: ImmutableDataType,
value: Vec<u8>,
}
impl ImmutableData {
pub fn new(type_tag: ImmutableDataType, value: Vec<u8>) -> ImmutableData {
ImmutableData {
type_tag: type_tag,
value: value,
}
}
pub fn value(&self) -> &Vec<u8> {
&self.value
}
pub fn get_type_tag(&self) -> &ImmutableDataType {
&self.type_tag
}
pub fn name(&self) -> XorName {
let digest = sha512::hash(&self.value);
match self.type_tag {
ImmutableDataType::Normal => XorName(digest.0),
ImmutableDataType::Backup => xor(&digest.0, NORMAL_TO_BACKUP),
ImmutableDataType::Sacrificial => xor(&digest.0, NORMAL_TO_SACRIFICIAL),
}
}
pub fn payload_size(&self) -> usize {
self.value.len()
}
}
impl Debug for ImmutableData {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter,
"ImmutableData {{ {:?}, {} }}",
self.type_tag,
self.name())
}
}
pub fn normal_to_backup(name: &XorName) -> XorName {
xor(&name.0, NORMAL_TO_BACKUP)
}
pub fn backup_to_normal(name: &XorName) -> XorName {
xor(&name.0, NORMAL_TO_BACKUP)
}
pub fn normal_to_sacrificial(name: &XorName) -> XorName {
xor(&name.0, NORMAL_TO_SACRIFICIAL)
}
pub fn sacrificial_to_normal(name: &XorName) -> XorName {
xor(&name.0, NORMAL_TO_SACRIFICIAL)
}
pub fn backup_to_sacrificial(name: &XorName) -> XorName {
xor(&name.0, BACKUP_TO_SACRIFICIAL)
}
pub fn sacrificial_to_backup(name: &XorName) -> XorName {
xor(&name.0, BACKUP_TO_SACRIFICIAL)
}
#[cfg(test)]
mod test {
extern crate rand;
use super::*;
use self::rand::Rng;
use rustc_serialize::hex::ToHex;
use sodiumoxide::crypto::hash::sha512;
use xor_name::XorName;
fn generate_random() -> Vec<u8> {
let size = 64;
let mut data = Vec::with_capacity(size);
let mut rng = rand::thread_rng();
for _ in 0..size {
data.push(rng.gen::<u8>());
}
data
}
#[test]
fn deterministic_test() {
let value = "immutable data value".to_owned().into_bytes();
let immutable_data = ImmutableData::new(ImmutableDataType::Normal, value);
let immutable_data_name = immutable_data.name().0.as_ref().to_hex();
let expected_immutable_data_name = "9f1c9e526f47e36d782de464ea9df0a31a5c19c321f\
2a5d9c8faacdda4d59abc713445c8c853e1842d7c2c\
2311650df1ee24107371935b6be88a10cbf4cd2f8f";
assert_eq!(&expected_immutable_data_name, &immutable_data_name);
let immutable_data_backup = ImmutableData::new(ImmutableDataType::Backup, immutable_data.value().clone());
let immutable_data_backup_name = immutable_data_backup.name().0.as_ref().to_hex();
let expected_immutable_data_backup_name = "1f1c9e526f47e36d782de464ea9df0a31a5c19c321f\
2a5d9c8faacdda4d59abc713445c8c853e1842d7c2c\
2311650df1ee24107371935b6be88a10cbf4cd2f8f";
assert_eq!(&expected_immutable_data_backup_name, &immutable_data_backup_name);
let immutable_data_sacrificial = ImmutableData::new(ImmutableDataType::Sacrificial,
immutable_data.value().clone());
let immutable_data_sacrificial_name = immutable_data_sacrificial.name().0.as_ref().to_hex();
let expected_immutable_data_sacrificial_name = "60e361ad90b81c9287d21b9b15620f5ce5a3e63cde0\
d5a26370553225b2a65438ecbba3737ac1e7bd283d3\
dcee9af20e11dbef8c8e6ca4941775ef340b32d070";
assert_eq!(&expected_immutable_data_sacrificial_name, &immutable_data_sacrificial_name);
}
#[test]
fn name_is_xor_related_to_lesser_type_name() {
let value = generate_random();
let normal = ImmutableData::new(ImmutableDataType::Normal, value.clone());
let backup = ImmutableData::new(ImmutableDataType::Backup, value.clone());
let sacrificial = ImmutableData::new(ImmutableDataType::Sacrificial, value.clone());
assert_eq!(normal.name(), XorName(sha512::hash(&value).0));
assert_eq!(normal.name(), backup_to_normal(&backup.name()));
assert_eq!(normal.name(), sacrificial_to_normal(&sacrificial.name()));
assert_eq!(backup.name(), normal_to_backup(&normal.name()));
assert_eq!(backup.name(), sacrificial_to_backup(&sacrificial.name()));
assert_eq!(sacrificial.name(), normal_to_sacrificial(&normal.name()));
assert_eq!(sacrificial.name(), backup_to_sacrificial(&backup.name()));
}
}