use super::{AppendWrapper, AppendedData, DataIdentifier, Filter, NO_OWNER_PUB_KEY};
use error::RoutingError;
use maidsafe_utilities::serialisation::{serialise, serialised_size};
use rust_sodium::crypto::sign::{self, PublicKey, SecretKey, Signature};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{self, Debug, Formatter};
use xor_name::XorName;
pub const MAX_PUB_APPENDABLE_DATA_SIZE_IN_BYTES: u64 = 102400;
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, RustcDecodable, RustcEncodable)]
pub struct PubAppendableData {
pub name: XorName,
pub version: u64,
pub filter: Filter,
pub deleted_data: BTreeSet<AppendedData>,
pub owners: BTreeSet<PublicKey>,
pub signatures: BTreeMap<PublicKey, Signature>,
pub data: BTreeSet<AppendedData>,
}
impl PubAppendableData {
pub fn new(name: XorName,
version: u64,
owners: BTreeSet<PublicKey>,
deleted_data: BTreeSet<AppendedData>,
filter: Filter)
-> Result<PubAppendableData, RoutingError> {
if owners.len() > 1 {
return Err(RoutingError::InvalidOwners);
}
Ok(PubAppendableData {
name: name,
version: version,
filter: filter,
deleted_data: deleted_data,
owners: owners,
signatures: BTreeMap::new(),
data: BTreeSet::new(),
})
}
pub fn update_with_other(&mut self, other: PubAppendableData) -> Result<(), RoutingError> {
self.validate_self_against_successor(&other)?;
self.name = other.name;
self.version = other.version;
self.filter = other.filter;
self.deleted_data = other.deleted_data;
self.owners = other.owners;
self.signatures = other.signatures;
self.data.extend(other.data);
for ad in &self.deleted_data {
if self.data.contains(ad) {
let _remove = self.data.remove(ad);
}
}
Ok(())
}
pub fn append(&mut self, appended_data: AppendedData) -> bool {
if match self.filter {
Filter::WhiteList(ref white_list) => !white_list.contains(&appended_data.sign_key),
Filter::BlackList(ref black_list) => black_list.contains(&appended_data.sign_key),
} || self.deleted_data.contains(&appended_data) {
return false;
}
self.data.insert(appended_data)
}
pub fn apply_wrapper(&mut self, wrapper: AppendWrapper) -> bool {
if !wrapper.verify_signature() || &self.version != wrapper.version() {
return false;
}
match wrapper.pub_appended_data() {
None => false,
Some(pub_appended_data) => self.append(pub_appended_data.clone()),
}
}
pub fn name(&self) -> &XorName {
&self.name
}
pub fn identifier(&self) -> DataIdentifier {
DataIdentifier::PubAppendable(self.name)
}
pub fn validate_self_against_successor(&self,
other: &PubAppendableData)
-> Result<(), RoutingError> {
if other.owners.len() > 1 || other.signatures.len() > 1 ||
self.owners.contains(&NO_OWNER_PUB_KEY) {
return Err(RoutingError::InvalidOwners);
}
if other.name != self.name || other.version != self.version + 1 {
return Err(RoutingError::UnknownMessageType);
}
let data = other.data_to_sign()?;
super::verify_signatures(&self.owners, &data, &other.signatures)
}
fn data_to_sign(&self) -> Result<Vec<u8>, RoutingError> {
let sd = SerialisablePubAppendableData {
name: self.name,
owners: &self.owners,
version: self.version
.to_string()
.as_bytes()
.to_vec(),
filter: &self.filter,
deleted_data: &self.deleted_data,
};
serialise(&sd).map_err(From::from)
}
pub fn add_signature(&mut self, keys: &(PublicKey, SecretKey)) -> Result<usize, RoutingError> {
if !self.signatures.is_empty() {
return Err(RoutingError::InvalidOwners);
}
let data = self.data_to_sign()?;
let sig = sign::sign_detached(&data, &keys.1);
if self.signatures.insert(keys.0, sig).is_none() {
return Ok(((self.owners.len() / 2) + 1).saturating_sub(self.signatures.len()));
}
Err(RoutingError::FailedSignature)
}
pub fn replace_signatures(&mut self, new_signatures: BTreeMap<PublicKey, Signature>) {
self.signatures = new_signatures;
}
pub fn get_data(&self) -> &BTreeSet<AppendedData> {
&self.data
}
pub fn get_version(&self) -> u64 {
self.version
}
pub fn get_owners(&self) -> &BTreeSet<PublicKey> {
&self.owners
}
pub fn get_signatures(&self) -> &BTreeMap<PublicKey, Signature> {
&self.signatures
}
pub fn validate_size(&self) -> bool {
serialised_size(self) <= MAX_PUB_APPENDABLE_DATA_SIZE_IN_BYTES
}
}
impl Debug for PubAppendableData {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter,
"PubAppendableData {{ name: {}, version: {}, owners: {:?}, signatures: {:?} }}",
self.name(),
self.version,
self.owners,
self.signatures)
}
}
#[derive(RustcEncodable)]
struct SerialisablePubAppendableData<'a> {
name: XorName,
owners: &'a BTreeSet<PublicKey>,
version: Vec<u8>,
filter: &'a Filter,
deleted_data: &'a BTreeSet<AppendedData>,
}
#[cfg(test)]
mod test {
use super::*;
use data::{self, AppendWrapper, AppendedData, DataIdentifier, Filter};
use rand;
use rust_sodium::crypto::sign;
use std::collections::BTreeSet;
use xor_name::XorName;
#[test]
fn single_owner() {
let keys = sign::gen_keypair();
let mut owner_keys = BTreeSet::new();
owner_keys.insert(keys.0);
match PubAppendableData::new(rand::random(),
0,
owner_keys.clone(),
BTreeSet::new(),
Filter::white_list(None)) {
Ok(mut pub_appendable_data) => {
let data = match pub_appendable_data.data_to_sign() {
Ok(data) => data,
Err(error) => panic!("Error: {:?}", error),
};
assert!(data::verify_signatures(&owner_keys,
&data,
pub_appendable_data.get_signatures())
.is_err());
assert!(pub_appendable_data.add_signature(&keys).is_ok());
assert!(data::verify_signatures(&owner_keys,
&data,
pub_appendable_data.get_signatures())
.is_ok());
}
Err(error) => panic!("Error: {:?}", error),
}
}
#[test]
fn single_owner_other_signature() {
let keys = sign::gen_keypair();
let other_keys = sign::gen_keypair();
let mut owner_keys = BTreeSet::new();
owner_keys.insert(keys.0);
match PubAppendableData::new(rand::random(),
0,
owner_keys.clone(),
BTreeSet::new(),
Filter::white_list(None)) {
Ok(mut pub_appendable_data) => {
assert!(pub_appendable_data.add_signature(&other_keys).is_ok());
let data = match pub_appendable_data.data_to_sign() {
Ok(data) => data,
Err(error) => panic!("Error: {:?}", error),
};
assert!(data::verify_signatures(&owner_keys,
&data,
pub_appendable_data.get_signatures())
.is_err());
}
Err(error) => panic!("Error: {:?}", error),
}
}
#[test]
fn appending_with_white_list() {
let black_key = sign::gen_keypair();
let white_key = sign::gen_keypair();
let data = PubAppendableData::new(rand::random(),
0,
BTreeSet::new(),
BTreeSet::new(),
Filter::white_list(vec![white_key.0]));
let mut pub_appendable_data = unwrap!(data);
let pointer = DataIdentifier::Structured(rand::random(), 10000);
let black_appended_data = unwrap!(AppendedData::new(pointer, black_key.0, &black_key.1));
let white_appended_data = unwrap!(AppendedData::new(pointer, white_key.0, &white_key.1));
assert!(!pub_appendable_data.append(black_appended_data));
assert!(pub_appendable_data.append(white_appended_data));
}
#[test]
fn appending_with_black_list() {
let black_key = sign::gen_keypair();
let white_key = sign::gen_keypair();
let data = PubAppendableData::new(rand::random(),
0,
BTreeSet::new(),
BTreeSet::new(),
Filter::black_list(vec![black_key.0]));
let mut pub_appendable_data = unwrap!(data);
let pointer = DataIdentifier::Structured(rand::random(), 10000);
let black_appended_data = unwrap!(AppendedData::new(pointer, black_key.0, &black_key.1));
let white_appended_data = unwrap!(AppendedData::new(pointer, white_key.0, &white_key.1));
assert!(!pub_appendable_data.append(black_appended_data));
assert!(pub_appendable_data.append(white_appended_data));
}
#[test]
fn apply_wrapper() {
let keys = sign::gen_keypair();
let name: XorName = rand::random();
let data = PubAppendableData::new(name,
0,
BTreeSet::new(),
BTreeSet::new(),
Filter::black_list(None));
let mut pub_appendable_data = unwrap!(data);
let pointer = DataIdentifier::Structured(rand::random(), 10000);
let appended_data = unwrap!(AppendedData::new(pointer, keys.0, &keys.1));
let append_wrapper = AppendWrapper::new_pub(name, appended_data.clone(), 0);
assert!(pub_appendable_data.apply_wrapper(append_wrapper));
let append_wrapper = AppendWrapper::new_pub(name, appended_data, 1);
assert!(!pub_appendable_data.apply_wrapper(append_wrapper));
}
#[test]
fn transfer_ownership() {
let keys = sign::gen_keypair();
let other_keys = sign::gen_keypair();
let mut owner = BTreeSet::new();
owner.insert(keys.0);
let mut new_owner = BTreeSet::new();
new_owner.insert(other_keys.0);
let name: XorName = rand::random();
let mut data =
PubAppendableData::new(name, 0, owner, BTreeSet::new(), Filter::black_list(None));
let mut ad = unwrap!(data);
data = PubAppendableData::new(name,
1,
new_owner.clone(),
BTreeSet::new(),
Filter::black_list(None));
let mut ad_new = unwrap!(data);
assert!(ad_new.add_signature(&keys).is_ok());
assert!(ad.update_with_other(ad_new).is_ok());
data = PubAppendableData::new(name,
2,
new_owner.clone(),
BTreeSet::new(),
Filter::black_list(None));
let mut ad_fail = unwrap!(data);
assert!(ad_fail.add_signature(&keys).is_ok());
assert!(ad.update_with_other(ad_fail).is_err());
}
}