use crate::{
error::Result, reg_crdt::RegisterCrdt, Entry, EntryHash, Error, Permissions, RegisterAddress,
RegisterOp, User,
};
use bls::{PublicKey, SecretKey, Signature};
use self_encryption::MIN_ENCRYPTABLE_BYTES;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use xor_name::XorName;
const MAX_REG_ENTRY_SIZE: usize = MIN_ENCRYPTABLE_BYTES / 3; const MAX_REG_NUM_ENTRIES: u16 = 1024;
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize, Debug)]
pub struct Register {
crdt: RegisterCrdt,
permissions: Permissions,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, PartialEq, Eq, Hash)]
pub struct SignedRegister {
base_register: Register,
signature: Signature,
ops: BTreeSet<RegisterOp>,
}
impl SignedRegister {
pub fn new(base_register: Register, signature: Signature) -> Self {
Self {
base_register,
signature,
ops: BTreeSet::new(),
}
}
pub fn verify(&self) -> Result<()> {
let bytes = self.base_register.bytes()?;
if !self
.base_register
.owner()
.verify(&self.signature, bytes.as_slice())
{
return Err(Error::InvalidSignature);
}
for op in &self.ops {
self.base_register.check_register_op(op)?;
}
Ok(())
}
pub fn verify_with_address(&self, address: RegisterAddress) -> Result<()> {
if self.base_register.address() != &address {
return Err(Error::InvalidRegisterAddress {
requested: Box::new(address),
got: Box::new(*self.address()),
});
}
self.verify()
}
pub fn register(self) -> Result<Register> {
let mut register = self.base_register;
for op in self.ops {
register.apply_op(op)?;
}
Ok(register)
}
pub fn merge(&mut self, other: SignedRegister) -> Result<()> {
if self.base_register != other.base_register {
return Err(Error::DifferentBaseRegister);
}
self.ops.extend(other.ops);
Ok(())
}
pub fn verified_merge(&mut self, other: SignedRegister) -> Result<()> {
if self.base_register != other.base_register {
return Err(Error::DifferentBaseRegister);
}
other.verify()?;
self.ops.extend(other.ops);
Ok(())
}
pub fn address(&self) -> &RegisterAddress {
self.base_register.address()
}
pub fn owner(&self) -> PublicKey {
self.base_register.owner()
}
pub fn add_op(&mut self, op: RegisterOp) -> Result<()> {
self.base_register.check_register_op(&op)?;
self.ops.insert(op);
Ok(())
}
}
impl Register {
pub fn new(owner: PublicKey, meta: XorName, mut permissions: Permissions) -> Self {
let address = RegisterAddress { meta, owner };
permissions.writers.insert(User::Key(owner));
Self {
crdt: RegisterCrdt::new(address),
permissions,
}
}
pub fn sign(&self, secret_key: &SecretKey) -> Result<Signature> {
if self.owner() != secret_key.public_key() {
return Err(Error::InvalidSecretKey);
}
let bytes = self.bytes()?;
let signature = secret_key.sign(bytes);
Ok(signature)
}
pub fn bytes(&self) -> Result<Vec<u8>> {
bincode::serialize(self).map_err(|_| Error::SerialisationFailed)
}
pub fn into_signed(self, secret_key: &SecretKey) -> Result<SignedRegister> {
let signature = self.sign(secret_key)?;
Ok(SignedRegister::new(self, signature))
}
#[cfg(test)]
pub fn new_owned(owner: PublicKey, meta: XorName) -> Self {
let permissions = Default::default();
Self::new(owner, meta, permissions)
}
pub fn address(&self) -> &RegisterAddress {
self.crdt.address()
}
pub fn owner(&self) -> PublicKey {
self.address().owner()
}
pub fn size(&self) -> u64 {
self.crdt.size()
}
pub fn get(&self, hash: EntryHash) -> Result<&Entry> {
self.crdt.get(hash).ok_or(Error::NoSuchEntry(hash))
}
pub fn get_cloned(&self, hash: EntryHash) -> Result<Entry> {
self.crdt.get(hash).cloned().ok_or(Error::NoSuchEntry(hash))
}
pub fn read(&self) -> BTreeSet<(EntryHash, Entry)> {
self.crdt.read()
}
pub fn permissions(&self) -> &Permissions {
&self.permissions
}
pub fn write(
&mut self,
entry: Entry,
children: BTreeSet<EntryHash>,
) -> Result<(EntryHash, RegisterOp)> {
self.check_entry_and_reg_sizes(&entry)?;
self.crdt.write(entry, children, User::Key(self.owner()))
}
pub fn apply_op(&mut self, op: RegisterOp) -> Result<()> {
self.check_entry_and_reg_sizes(&op.crdt_op.value)?;
self.check_register_op(&op)?;
self.crdt.apply_op(op)
}
pub fn merge(&mut self, other: Self) {
self.crdt.merge(other.crdt);
}
pub fn check_register_op(&self, op: &RegisterOp) -> Result<()> {
self.check_user_permissions(op.source)?;
if self.permissions.everyone_can_write() {
return Ok(()); }
match op.source {
User::Anyone => Ok(()),
User::Key(pk) => op.verify_signature(&pk),
}
}
pub fn check_user_permissions(&self, requester: User) -> Result<()> {
if requester == User::Key(self.owner()) || self.permissions.can_write(&requester) {
Ok(())
} else {
Err(Error::AccessDenied(requester))
}
}
fn check_entry_and_reg_sizes(&self, entry: &Entry) -> Result<()> {
let size = entry.len();
if size > MAX_REG_ENTRY_SIZE {
return Err(Error::EntryTooBig {
size,
max: MAX_REG_ENTRY_SIZE,
});
}
let reg_size = self.crdt.size();
if reg_size >= MAX_REG_NUM_ENTRIES.into() {
return Err(Error::TooManyEntries(reg_size as usize));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{
EntryHash, Error, Permissions, Register, RegisterAddress, Result, User, MAX_REG_NUM_ENTRIES,
};
use bls::SecretKey;
use eyre::Context;
use proptest::prelude::*;
use rand::{rngs::OsRng, seq::SliceRandom, thread_rng, Rng};
use std::{collections::BTreeSet, sync::Arc};
use xor_name::XorName;
#[test]
fn register_create() {
let meta = xor_name::rand::random();
let (authority_sk, register) = &gen_reg_replicas(None, meta, None, 1)[0];
let authority = authority_sk.public_key();
assert_eq!(register.owner(), authority);
assert_eq!(register.owner(), authority);
let address = RegisterAddress::new(meta, authority);
assert_eq!(*register.address(), address);
}
#[test]
fn register_generate_entry_hash() -> eyre::Result<()> {
let authority_sk = SecretKey::random();
let authority = authority_sk.public_key();
let meta: XorName = xor_name::rand::random();
let mut replica1 = Register::new_owned(authority, meta);
let mut replica2 = Register::new_owned(authority, meta);
let item1 = random_register_entry();
let item2 = random_register_entry();
let (entry_hash1_1, _) = replica1.write(item1.clone(), BTreeSet::new())?;
let (entry_hash1_2, _) = replica1.write(item2, BTreeSet::new())?;
assert!(entry_hash1_1 != entry_hash1_2);
let (entry_hash2_1, _) = replica2.write(item1, BTreeSet::new())?;
assert_eq!(entry_hash1_1, entry_hash2_1);
let mut parents = BTreeSet::new();
let _ = parents.insert(entry_hash1_1);
let item3 = random_register_entry();
let item4 = random_register_entry();
let (entry_hash1_1_3, _) = replica1.write(item3, parents.clone())?;
let (entry_hash2_1_4, _) = replica2.write(item4, parents.clone())?;
assert!(entry_hash1_1_3 != entry_hash2_1_4);
Ok(())
}
#[test]
fn register_concurrent_write_ops() -> eyre::Result<()> {
let authority_sk1 = SecretKey::random();
let authority1 = User::Key(authority_sk1.public_key());
let authority_sk2 = SecretKey::random();
let authority2 = User::Key(authority_sk2.public_key());
let meta: XorName = xor_name::rand::random();
let perms = Permissions::new_with([authority1, authority2]);
let mut replica1 = Register::new(authority_sk1.public_key(), meta, perms.clone());
let mut replica2 = replica1.clone();
let item1 = random_register_entry();
let (_, op1) = replica1.write(item1, BTreeSet::new())?;
let mut signed_write_op1 = op1;
signed_write_op1.sign_with(&authority_sk1);
assert_eq!(replica1.size(), 1);
assert_eq!(replica2.size(), 0);
let item2 = random_register_entry();
let (_, op2) = replica2.write(item2, BTreeSet::new())?;
let mut signed_write_op2 = op2;
signed_write_op2.sign_with(&authority_sk2);
assert_eq!(replica2.size(), 1);
replica1.apply_op(signed_write_op2)?;
replica2.apply_op(signed_write_op1)?;
verify_data_convergence(vec![replica1, replica2], 2)?;
Ok(())
}
#[test]
fn register_get_by_hash() -> eyre::Result<()> {
let (_, register) = &mut create_reg_replicas(1)[0];
let entry1 = random_register_entry();
let entry2 = random_register_entry();
let entry3 = random_register_entry();
let (entry1_hash, _) = register.write(entry1.clone(), BTreeSet::new())?;
let (entry2_hash, _) = register.write(entry2.clone(), BTreeSet::new())?;
let children = vec![entry1_hash, entry2_hash].into_iter().collect();
let (entry3_hash, _) = register.write(entry3.clone(), children)?;
assert_eq!(register.size(), 3);
let first_entry = register.get(entry1_hash)?;
assert_eq!(first_entry, &entry1);
let second_entry = register.get(entry2_hash)?;
assert_eq!(second_entry, &entry2);
let third_entry = register.get(entry3_hash)?;
assert_eq!(third_entry, &entry3);
let non_existing_hash = EntryHash::default();
let entry_not_found = register.get(non_existing_hash);
assert_eq!(entry_not_found, Err(Error::NoSuchEntry(non_existing_hash)));
Ok(())
}
#[test]
fn register_query_public_perms() -> eyre::Result<()> {
let meta = xor_name::rand::random();
let authority_sk1 = SecretKey::random();
let authority_pk1 = authority_sk1.public_key();
let owner1 = User::Key(authority_pk1);
let perms1 = Permissions::new_anyone_can_write();
let replica1 = create_reg_replica_with(meta, Some(authority_sk1), Some(perms1));
let authority_sk2 = SecretKey::random();
let authority_pk2 = authority_sk2.public_key();
let owner2 = User::Key(authority_pk2);
let perms2 = Permissions::new_with([owner1]);
let replica2 = create_reg_replica_with(meta, Some(authority_sk2), Some(perms2));
let sk_rand = SecretKey::random();
let random_user = User::Key(sk_rand.public_key());
assert_eq!(replica1.owner(), authority_pk1);
assert_eq!(replica1.check_user_permissions(User::Anyone), Ok(()));
assert_eq!(replica1.check_user_permissions(owner1), Ok(()));
assert_eq!(replica1.check_user_permissions(owner2), Ok(()));
assert_eq!(replica1.check_user_permissions(random_user), Ok(()));
assert_eq!(replica2.owner(), authority_pk2);
assert_eq!(
replica2.check_user_permissions(User::Anyone),
Err(Error::AccessDenied(User::Anyone))
);
assert_eq!(replica2.check_user_permissions(owner1), Ok(()));
assert_eq!(replica2.check_user_permissions(owner2), Ok(()));
assert_eq!(
replica2.check_user_permissions(random_user),
Err(Error::AccessDenied(random_user))
);
Ok(())
}
#[test]
fn exceeding_max_reg_entries_errors() -> eyre::Result<()> {
let meta = xor_name::rand::random();
let authority_sk1 = SecretKey::random();
let perms1 = Permissions::new_anyone_can_write();
let mut replica = create_reg_replica_with(meta, Some(authority_sk1), Some(perms1));
for _ in 0..MAX_REG_NUM_ENTRIES {
let (_hash, _op) = replica
.write(random_register_entry(), BTreeSet::new())
.context("Failed to write register entry")?;
}
let excess_entry = replica.write(random_register_entry(), BTreeSet::new());
match excess_entry {
Err(Error::TooManyEntries(size)) => {
assert_eq!(size, 1024);
Ok(())
}
anything_else => {
eyre::bail!(
"Expected Excess entries error was not found. Instead: {anything_else:?}"
)
}
}
}
fn gen_reg_replicas(
authority_sk: Option<SecretKey>,
meta: XorName,
perms: Option<Permissions>,
count: usize,
) -> Vec<(SecretKey, Register)> {
let replicas: Vec<(SecretKey, Register)> = (0..count)
.map(|_| {
let authority_sk = authority_sk.clone().unwrap_or_else(SecretKey::random);
let authority = authority_sk.public_key();
let perms = perms.clone().unwrap_or_default();
let register = Register::new(authority, meta, perms);
(authority_sk, register)
})
.collect();
assert_eq!(replicas.len(), count);
replicas
}
fn create_reg_replicas(count: usize) -> Vec<(SecretKey, Register)> {
let meta = xor_name::rand::random();
gen_reg_replicas(None, meta, None, count)
}
fn create_reg_replica_with(
meta: XorName,
authority_sk: Option<SecretKey>,
perms: Option<Permissions>,
) -> Register {
let replicas = gen_reg_replicas(authority_sk, meta, perms, 1);
replicas[0].1.clone()
}
fn verify_data_convergence(replicas: Vec<Register>, expected_size: u64) -> Result<()> {
for r in &replicas {
assert_eq!(r.size(), expected_size);
}
let r0 = &replicas[0];
for r in &replicas {
assert_eq!(r.crdt, r0.crdt);
}
Ok(())
}
fn generate_replicas(
max_quantity: usize,
) -> impl Strategy<Value = Result<(Vec<Register>, Arc<SecretKey>)>> {
let xorname = xor_name::rand::random();
let owner_sk = Arc::new(SecretKey::random());
let owner = owner_sk.public_key();
let perms = Permissions::new_anyone_can_write();
(1..max_quantity + 1).prop_map(move |quantity| {
let mut replicas = Vec::with_capacity(quantity);
for _ in 0..quantity {
let replica = Register::new(owner, xorname, perms.clone());
replicas.push(replica);
}
Ok((replicas, owner_sk.clone()))
})
}
fn generate_reg_entry() -> impl Strategy<Value = Vec<u8>> {
"\\PC*".prop_map(|s| s.into_bytes())
}
fn generate_dataset(max_quantity: usize) -> impl Strategy<Value = Vec<Vec<u8>>> {
prop::collection::vec(generate_reg_entry(), 1..max_quantity + 1)
}
fn generate_dataset_and_probability(
max_quantity: usize,
) -> impl Strategy<Value = Vec<(Vec<u8>, u8)>> {
prop::collection::vec((generate_reg_entry(), any::<u8>()), 1..max_quantity + 1)
}
proptest! {
#[test]
fn proptest_reg_doesnt_crash_with_random_data(
_data in generate_reg_entry()
) {
let meta = xor_name::rand::random();
let owner_sk = SecretKey::random();
let perms = Default::default();
let mut replicas = gen_reg_replicas(
Some(owner_sk.clone()),
meta,
Some(perms),
2);
let (_, mut replica1) = replicas.remove(0);
let (_, mut replica2) = replicas.remove(0);
let (_, op) = replica1.write(random_register_entry(), BTreeSet::new())?;
let mut write_op = op;
write_op.sign_with(&owner_sk);
replica2.apply_op(write_op)?;
verify_data_convergence(vec![replica1, replica2], 1)?;
}
#[test]
fn proptest_reg_converge_with_many_random_data(
dataset in generate_dataset(1000)
) {
let meta = xor_name::rand::random();
let owner_sk = SecretKey::random();
let perms = Default::default();
let mut replicas = gen_reg_replicas(
Some(owner_sk.clone()),
meta,
Some(perms),
2);
let (_, mut replica1) = replicas.remove(0);
let (_, mut replica2) = replicas.remove(0);
let dataset_length = dataset.len() as u64;
let mut children = BTreeSet::new();
for _data in dataset {
let (hash, op) = replica1.write(random_register_entry(), children.clone())?;
let mut write_op = op;
write_op.sign_with(&owner_sk);
replica2.apply_op(write_op)?;
children = vec![hash].into_iter().collect();
}
verify_data_convergence(vec![replica1, replica2], dataset_length)?;
}
#[test]
fn proptest_reg_converge_with_many_random_data_random_entry_children(
dataset in generate_dataset(1000)
) {
let meta = xor_name::rand::random();
let owner_sk = SecretKey::random();
let perms = Default::default();
let mut replicas = gen_reg_replicas(
Some(owner_sk.clone()),
meta,
Some(perms),
2);
let (_, mut replica1) = replicas.remove(0);
let (_, mut replica2) = replicas.remove(0);
let dataset_length = dataset.len() as u64;
let mut list_of_hashes = Vec::new();
let mut rng = thread_rng();
for _data in dataset {
let num_of_children: usize = rng.gen();
let children: BTreeSet<_> = list_of_hashes.choose_multiple(&mut OsRng, num_of_children).cloned().collect();
let (hash, op) = replica1.write(random_register_entry(), children)?;
let mut write_op = op;
write_op.sign_with(&owner_sk);
replica2.apply_op(write_op)?;
list_of_hashes.push(hash);
}
verify_data_convergence(vec![replica1, replica2], dataset_length)?;
}
#[test]
fn proptest_reg_converge_with_many_random_data_across_arbitrary_number_of_replicas(
dataset in generate_dataset(500),
res in generate_replicas(50)
) {
let (mut replicas, owner_sk) = res?;
let dataset_length = dataset.len() as u64;
let mut children = BTreeSet::new();
for _data in dataset {
let (hash, op)= replicas[0].write(random_register_entry(), children)?;
let mut signed_op = op;
signed_op.sign_with(&owner_sk);
for replica in &mut replicas {
replica.apply_op(signed_op.clone())?;
}
children = vec![hash].into_iter().collect();
}
verify_data_convergence(replicas, dataset_length)?;
}
#[test]
fn proptest_converge_with_shuffled_op_set_across_arbitrary_number_of_replicas(
dataset in generate_dataset(100),
res in generate_replicas(500)
) {
let (mut replicas, owner_sk) = res?;
let dataset_length = dataset.len() as u64;
let mut ops = vec![];
let mut children = BTreeSet::new();
for _data in dataset {
let (hash, op) = replicas[0].write(random_register_entry(), children)?;
let mut signed_op = op;
signed_op.sign_with(&owner_sk);
ops.push(signed_op);
children = vec![hash].into_iter().collect();
}
for replica in &mut replicas {
let mut ops = ops.clone();
ops.shuffle(&mut OsRng);
for op in ops {
replica.apply_op(op)?;
}
}
verify_data_convergence(replicas, dataset_length)?;
}
#[test]
fn proptest_converge_with_shuffled_ops_from_many_replicas_across_arbitrary_number_of_replicas(
dataset in generate_dataset(1000),
res in generate_replicas(7)
) {
let (mut replicas, owner_sk) = res?;
let dataset_length = dataset.len() as u64;
let mut ops = vec![];
let mut children = BTreeSet::new();
for _data in dataset {
if let Some(replica) = replicas.choose_mut(&mut OsRng)
{
let (hash, op) = replica.write(random_register_entry(), children)?;
let mut signed_op = op;
signed_op.sign_with(&owner_sk);
ops.push(signed_op);
children = vec![hash].into_iter().collect();
}
}
let opslen = ops.len() as u64;
prop_assert_eq!(dataset_length, opslen);
for replica in &mut replicas {
let mut ops = ops.clone();
ops.shuffle(&mut OsRng);
for op in ops {
replica.apply_op(op)?;
}
}
verify_data_convergence(replicas, dataset_length)?;
}
#[test]
fn proptest_dropped_data_can_be_reapplied_and_we_converge(
dataset in generate_dataset_and_probability(1000),
) {
let meta = xor_name::rand::random();
let owner_sk = SecretKey::random();
let perms = Default::default();
let mut replicas = gen_reg_replicas(
Some(owner_sk.clone()),
meta,
Some(perms),
2);
let (_, mut replica1) = replicas.remove(0);
let (_, mut replica2) = replicas.remove(0);
let dataset_length = dataset.len() as u64;
let mut ops = vec![];
let mut children = BTreeSet::new();
for (_data, delivery_chance) in dataset {
let (hash, op)= replica1.write(random_register_entry(), children)?;
let mut signed_op = op;
signed_op.sign_with(&owner_sk);
ops.push((signed_op, delivery_chance));
children = vec![hash].into_iter().collect();
}
for (op, delivery_chance) in ops.clone() {
if delivery_chance < u8::MAX / 3 {
replica2.apply_op(op)?;
}
}
if dataset_length > 50 {
assert_ne!(replica2.size(), replica1.size());
}
for (op, _) in ops {
replica2.apply_op(op)?;
}
verify_data_convergence(vec![replica1, replica2], dataset_length)?;
}
#[test]
fn proptest_converge_with_shuffled_ops_from_many_while_dropping_some_at_random(
dataset in generate_dataset_and_probability(1000),
res in generate_replicas(7),
) {
let (mut replicas, owner_sk) = res?;
let dataset_length = dataset.len() as u64;
let mut ops = vec![];
let mut children = BTreeSet::new();
for (_data, delivery_chance) in dataset {
let index: usize = OsRng.gen_range(0..replicas.len());
let replica = &mut replicas[index];
let (hash, op)=replica.write(random_register_entry(), children)?;
let mut signed_op = op;
signed_op.sign_with(&owner_sk);
ops.push((signed_op, delivery_chance));
children = vec![hash].into_iter().collect();
}
let opslen = ops.len() as u64;
prop_assert_eq!(dataset_length, opslen);
for replica in &mut replicas {
let mut ops = ops.clone();
ops.shuffle(&mut OsRng);
for (op, delivery_chance) in ops.clone() {
if delivery_chance > u8::MAX / 3 {
replica.apply_op(op)?;
}
}
for (op, _) in ops {
replica.apply_op(op)?;
}
}
verify_data_convergence(replicas, dataset_length)?;
}
#[test]
fn proptest_converge_with_shuffled_ops_including_bad_ops_which_error_and_are_not_applied(
dataset in generate_dataset(10),
bogus_dataset in generate_dataset(10), gen_replicas_result in generate_replicas(10),
) {
let (mut replicas, owner_sk) = gen_replicas_result?;
let dataset_length = dataset.len();
let bogus_dataset_length = bogus_dataset.len();
let number_replicas = replicas.len();
let mut ops = vec![];
let mut children = BTreeSet::new();
for _data in dataset {
if let Some(replica) = replicas.choose_mut(&mut OsRng)
{
let (hash, op)=replica.write(random_register_entry(), children)?;
let mut signed_op = op;
signed_op.sign_with(&owner_sk);
ops.push(signed_op);
children = vec![hash].into_iter().collect();
}
}
let xorname = xor_name::rand::random();
let random_owner_sk = SecretKey::random();
let mut bogus_replica = Register::new_owned(random_owner_sk.public_key(), xorname);
let mut children = BTreeSet::new();
for _data in bogus_dataset {
let (hash, op)=bogus_replica.write(random_register_entry(), children)?;
let mut bogus_op = op;
bogus_op.sign_with(&random_owner_sk);
bogus_replica.apply_op(bogus_op.clone())?;
ops.push(bogus_op);
children = vec![hash].into_iter().collect();
}
let opslen = ops.len();
prop_assert_eq!(dataset_length + bogus_dataset_length, opslen);
let mut err_count = vec![];
for replica in &mut replicas {
let mut ops = ops.clone();
ops.shuffle(&mut OsRng);
for op in ops {
match replica.apply_op(op) {
Ok(_) => {},
Err(error) => {err_count.push(error)},
}
}
}
assert_eq!(err_count.len(), bogus_dataset_length * number_replicas);
verify_data_convergence(replicas, dataset_length as u64)?;
}
}
fn random_register_entry() -> Vec<u8> {
let random_bytes = thread_rng().gen::<[u8; 32]>();
random_bytes.to_vec()
}
}