use std::fmt::{self, Debug, Formatter};
use rustc_serialize::{Decoder, Encoder};
use xor_name::{XorName, XOR_NAME_LEN};
use sodiumoxide::crypto::hash::sha512;
use data::DataIdentifier;
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)]
pub struct ImmutableData {
value: Vec<u8>,
}
impl ImmutableData {
pub fn new(value: Vec<u8>) -> ImmutableData {
ImmutableData { value: value }
}
pub fn value(&self) -> &Vec<u8> {
&self.value
}
pub fn name(&self) -> XorName {
XorName(sha512::hash(&self.value).0)
}
pub fn payload_size(&self) -> usize {
self.value.len()
}
pub fn identifier(&self) -> DataIdentifier {
DataIdentifier::Immutable(self.name())
}
}
impl Debug for ImmutableData {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "ImmutableData {:?}", self.name())
}
}
#[derive(Hash, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct ImmutableDataBackup {
value: ImmutableData,
}
impl ImmutableDataBackup {
pub fn new(value: ImmutableData) -> ImmutableDataBackup {
ImmutableDataBackup { value: value }
}
pub fn value(&self) -> &ImmutableData {
&self.value
}
pub fn name(&self) -> XorName {
xor(&self.value.name().0, NORMAL_TO_BACKUP)
}
pub fn payload_size(&self) -> usize {
self.value.value.len()
}
pub fn identifier(&self) -> DataIdentifier {
DataIdentifier::ImmutableBackup(self.name())
}
}
impl Debug for ImmutableDataBackup {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "ImmutableDataBackup {:?}", self.name())
}
}
#[derive(Hash, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct ImmutableDataSacrificial {
value: ImmutableData,
}
impl ImmutableDataSacrificial {
pub fn new(value: ImmutableData) -> ImmutableDataSacrificial {
ImmutableDataSacrificial { value: value }
}
pub fn value(&self) -> &ImmutableData {
&self.value
}
pub fn name(&self) -> XorName {
xor(&self.value.name().0, NORMAL_TO_SACRIFICIAL)
}
pub fn payload_size(&self) -> usize {
self.value.value.len()
}
pub fn identifier(&self) -> DataIdentifier {
DataIdentifier::ImmutableSacrificial(self.name())
}
}
impl Debug for ImmutableDataSacrificial {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "ImmutableDataSacrificial {:?}", 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(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 = ImmutableDataBackup::new(immutable_data.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 = ImmutableDataSacrificial::new(immutable_data.clone());
let immutable_data_sacrificial_name = immutable_data_sacrificial.name().0.as_ref().to_hex();
let expected_immutable_data_sacrificial_name = "60e361ad90b81c9287d21b9b15620f5ce5a3e63cde\
0d5a26370553225b2a65438ecbba3737ac1e7bd283\
d3dcee9af20e11dbef8c8e6ca4941775ef340b32d0\
70";
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(value.clone());
let backup = ImmutableDataBackup::new(normal.clone());
let sacrificial = ImmutableDataSacrificial::new(normal.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()));
}
}