Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ impl storage::data_directory::Trait for Runtime {
type Event = Event;
type ContentId = ContentId;
type Members = Members;
type Roles = Actors;
type IsActiveDataObjectType = DataObjectTypeRegistry;
}

Expand All @@ -265,6 +266,7 @@ impl storage::data_object_storage_registry::Trait for Runtime {
type Event = Event;
type DataObjectStorageRelationshipId = u64;
type Members = Members;
type Roles = Actors;
type ContentIdExists = DataDirectory;
}

Expand Down
20 changes: 20 additions & 0 deletions src/roles/actors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use system::{self, ensure_signed};

use crate::traits::{Members, Roles};

static MSG_NO_ACTOR_FOR_ROLE: &str = "For the specified role, no actor is currently staked.";

#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)]
pub enum Role {
Storage,
Expand Down Expand Up @@ -193,6 +195,24 @@ impl<T: Trait> Roles<T> for Module<T> {
fn is_role_account(account_id: &T::AccountId) -> bool {
<ActorByAccountId<T>>::exists(account_id) || <Bondage<T>>::exists(account_id)
}

fn account_has_role(account_id: &T::AccountId, role: Role) -> bool {
Self::actor_by_account_id(account_id).map_or(false, |actor| actor.role == role)
}

fn random_account_for_role(role: Role) -> Result<T::AccountId, &'static str> {
let ids = Self::account_ids_by_role(role);
if 0 == ids.len() {
return Err(MSG_NO_ACTOR_FOR_ROLE);
}
let seed = <system::Module<T>>::random_seed();
let mut rand: u64 = 0;
for offset in 0..8 {
rand += (seed.as_ref()[offset] as u64) << offset;
}
let idx = (rand as usize) % ids.len();
return Ok(ids[idx].clone());
}
}

decl_module! {
Expand Down
14 changes: 8 additions & 6 deletions src/storage/data_directory.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![cfg_attr(not(feature = "std"), no_std)]

use crate::roles::actors;
use crate::storage::data_object_type_registry::Trait as DOTRTrait;
use crate::traits::{ContentIdExists, IsActiveDataObjectType, Members};
use crate::traits::{ContentIdExists, IsActiveDataObjectType, Members, Roles};
use parity_codec::Codec;
use parity_codec_derive::{Decode, Encode};
use primitives::Ed25519AuthorityId;
Expand All @@ -27,6 +28,7 @@ pub trait Trait: timestamp::Trait + system::Trait + DOTRTrait + MaybeDebug {
+ PartialEq;

type Members: Members<Self>;
type Roles: Roles<Self>;
type IsActiveDataObjectType: IsActiveDataObjectType<Self>;
}

Expand Down Expand Up @@ -120,9 +122,7 @@ decl_module! {

// The liaison is something we need to take from staked roles. The idea
// is to select the liaison, for now randomly.
// FIXME without that module, we're currently hardcoding it, to the
// origin, which is wrong on many levels.
let liaison = who.clone();
let liaison = T::Roles::random_account_for_role(actors::Role::Storage)?;

// Let's create the entry then
let new_id = Self::next_content_id();
Expand Down Expand Up @@ -209,9 +209,10 @@ mod tests {
_ => (0u64, 0xdeadbeefu64), // invalid value, unlikely to match
};
assert_ne!(liaison, 0xdeadbeefu64);
assert_eq!(liaison, TEST_MOCK_LIAISON);

// Accepting content should not work with some random origin
let res = TestDataDirectory::accept_content(Origin::signed(42), content_id);
let res = TestDataDirectory::accept_content(Origin::signed(1), content_id);
assert!(res.is_err());

// However, with the liaison as origin it should.
Expand All @@ -235,9 +236,10 @@ mod tests {
_ => (0u64, 0xdeadbeefu64), // invalid value, unlikely to match
};
assert_ne!(liaison, 0xdeadbeefu64);
assert_eq!(liaison, TEST_MOCK_LIAISON);

// Rejecting content should not work with some random origin
let res = TestDataDirectory::reject_content(Origin::signed(42), content_id);
let res = TestDataDirectory::reject_content(Origin::signed(1), content_id);
assert!(res.is_err());

// However, with the liaison as origin it should.
Expand Down
33 changes: 22 additions & 11 deletions src/storage/data_object_storage_registry.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![cfg_attr(not(feature = "std"), no_std)]

use crate::roles::actors;
use crate::storage::data_directory::Trait as DDTrait;
use crate::traits::{ContentHasStorage, ContentIdExists, Members};
use crate::traits::{ContentHasStorage, ContentIdExists, Members, Roles};
use parity_codec::Codec;
use parity_codec_derive::{Decode, Encode};
use rstd::prelude::*;
Expand All @@ -26,11 +27,14 @@ pub trait Trait: timestamp::Trait + system::Trait + DDTrait + MaybeDebug {
+ PartialEq;

type Members: Members<Self>;
type Roles: Roles<Self>;
type ContentIdExists: ContentIdExists<Self>;
}

static MSG_CID_NOT_FOUND: &str = "Content with this ID not found.";
static MSG_DOSR_NOT_FOUND: &str = "No data object storage relationship found for this ID.";
static MSG_ONLY_STORAGE_PROVIDER_MAY_CREATE_DOSR: &str =
"Only storage providers can create data object storage relationships.";
static MSG_ONLY_STORAGE_PROVIDER_MAY_CLAIM_READY: &str =
"Only the storage provider in a DOSR can decide whether they're ready.";

Expand Down Expand Up @@ -104,10 +108,9 @@ decl_module! {
pub fn add_relationship(origin, cid: T::ContentId) {
// Origin has to be a storage provider
let who = ensure_signed(origin)?;
// TODO check for being staked as a storage provider
// if !T::Members::is_active_member(&who) {
// return Err(MSG_CREATOR_MUST_BE_MEMBER);
// }

// Check that the origin is a storage provider
ensure!(<T as Trait>::Roles::account_has_role(&who, actors::Role::Storage), MSG_ONLY_STORAGE_PROVIDER_MAY_CREATE_DOSR);

// Content ID must exist
ensure!(T::ContentIdExists::has_content(&cid), MSG_CID_NOT_FOUND);
Expand Down Expand Up @@ -190,8 +193,11 @@ mod tests {
#[test]
fn test_add_relationship() {
with_default_mock_builder(|| {
// The content needs to exist - in our mock, that's with the content ID 42
let res = TestDataObjectStorageRegistry::add_relationship(Origin::signed(1), 42);
// The content needs to exist - in our mock, that's with the content ID TEST_MOCK_EXISTING_CID
let res = TestDataObjectStorageRegistry::add_relationship(
Origin::signed(TEST_MOCK_LIAISON),
TEST_MOCK_EXISTING_CID,
);
assert!(res.is_ok());
});
}
Expand All @@ -208,7 +214,10 @@ mod tests {
fn test_toggle_ready() {
with_default_mock_builder(|| {
// Create a DOSR
let res = TestDataObjectStorageRegistry::add_relationship(Origin::signed(1), 42);
let res = TestDataObjectStorageRegistry::add_relationship(
Origin::signed(TEST_MOCK_LIAISON),
TEST_MOCK_EXISTING_CID,
);
assert!(res.is_ok());

// Grab DOSR ID from event
Expand All @@ -231,14 +240,16 @@ mod tests {

// Toggling with the wrong ID should fail.
let res = TestDataObjectStorageRegistry::set_relationship_ready(
Origin::signed(1),
Origin::signed(TEST_MOCK_LIAISON),
dosr_id + 1,
);
assert!(res.is_err());

// Toggling with the correct ID and origin should succeed
let res =
TestDataObjectStorageRegistry::set_relationship_ready(Origin::signed(1), dosr_id);
let res = TestDataObjectStorageRegistry::set_relationship_ready(
Origin::signed(TEST_MOCK_LIAISON),
dosr_id,
);
assert!(res.is_ok());
assert_eq!(System::events().last().unwrap().event,
MetaEvent::data_object_storage_registry(data_object_storage_registry::RawEvent::DataObjectStorageRelationshipReadyUpdated(
Expand Down
81 changes: 73 additions & 8 deletions src/storage/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
pub use super::{
content_directory, data_directory, data_object_storage_registry, data_object_type_registry,
};
use crate::governance::GovernanceCurrency;
use crate::roles::actors;
use crate::traits;
use runtime_io::with_externalities;
pub use system;
Expand All @@ -26,9 +28,19 @@ impl_outer_event! {
data_directory<T>,
data_object_storage_registry<T>,
content_directory<T>,
actors<T>,
balances<T>,
}
}

pub const TEST_FIRST_DATA_OBJECT_TYPE_ID: u64 = 1000;
pub const TEST_FIRST_CONTENT_ID: u64 = 2000;
pub const TEST_FIRST_RELATIONSHIP_ID: u64 = 3000;
pub const TEST_FIRST_METADATA_ID: u64 = 4000;

pub const TEST_MOCK_LIAISON: u64 = 0xd00du64;
pub const TEST_MOCK_EXISTING_CID: u64 = 42;

pub struct MockMembers {}
impl<T: system::Trait> traits::Members<T> for MockMembers {
type Id = u64;
Expand All @@ -46,6 +58,27 @@ impl<T: system::Trait> traits::Members<T> for MockMembers {
}
}

pub struct MockRoles {}
impl traits::Roles<Test> for MockRoles {
fn is_role_account(_account_id: &<Test as system::Trait>::AccountId) -> bool {
false
}

fn account_has_role(
account_id: &<Test as system::Trait>::AccountId,
_role: actors::Role,
) -> bool {
*account_id == TEST_MOCK_LIAISON
}

fn random_account_for_role(
_role: actors::Role,
) -> Result<<Test as system::Trait>::AccountId, &'static str> {
// We "randomly" select an account Id.
Ok(TEST_MOCK_LIAISON)
}
}

pub struct AnyDataObjectTypeIsActive {}
impl<T: data_object_type_registry::Trait> traits::IsActiveDataObjectType<T>
for AnyDataObjectTypeIsActive
Expand All @@ -58,21 +91,21 @@ impl<T: data_object_type_registry::Trait> traits::IsActiveDataObjectType<T>
pub struct MockContent {}
impl traits::ContentIdExists<Test> for MockContent {
fn has_content(which: &<Test as data_directory::Trait>::ContentId) -> bool {
*which == 42
*which == TEST_MOCK_EXISTING_CID
}

fn get_data_object(
which: &<Test as data_directory::Trait>::ContentId,
) -> Result<data_directory::DataObject<Test>, &'static str> {
match *which {
42 => Ok(data_directory::DataObject {
TEST_MOCK_EXISTING_CID => Ok(data_directory::DataObject {
data_object_type: 1,
signing_key: None,
size: 1234,
added_at_block: 10,
added_at_time: 1024,
owner: 1,
liaison: 1, // TODO change to another later
liaison: TEST_MOCK_LIAISON,
liaison_judgement: data_directory::LiaisonJudgement::Pending,
}),
_ => Err("nope, missing"),
Expand Down Expand Up @@ -108,13 +141,15 @@ impl data_directory::Trait for Test {
type Event = MetaEvent;
type ContentId = u64;
type Members = MockMembers;
type Roles = MockRoles;
type IsActiveDataObjectType = AnyDataObjectTypeIsActive;
}

impl data_object_storage_registry::Trait for Test {
type Event = MetaEvent;
type DataObjectStorageRelationshipId = u64;
type Members = MockMembers;
type Roles = MockRoles;
type ContentIdExists = MockContent;
}

Expand All @@ -125,6 +160,11 @@ impl content_directory::Trait for Test {
type Members = MockMembers;
}

impl actors::Trait for Test {
type Event = MetaEvent;
type Members = MockMembers;
}

impl timestamp::Trait for Test {
type Moment = u64;
type OnTimestampSet = ();
Expand All @@ -136,6 +176,29 @@ impl consensus::Trait for Test {
type Log = DigestItem;
}

impl balances::Trait for Test {
type Event = MetaEvent;

/// The balance of an account.
type Balance = u32;

/// A function which is invoked when the free-balance has fallen below the existential deposit and
/// has been reduced to zero.
///
/// Gives a chance to clean up resources associated with the given account.
type OnFreeBalanceZero = ();

/// Handler for when a new account is created.
type OnNewAccount = ();

/// A function that returns true iff a given account can transfer its funds to another account.
type EnsureAccountLiquid = ();
}

impl GovernanceCurrency for Test {
type Currency = balances::Module<Self>;
}

pub struct ExtBuilder {
first_data_object_type_id: u64,
first_content_id: u64,
Expand Down Expand Up @@ -224,11 +287,8 @@ pub type TestDataDirectory = data_directory::Module<Test>;
// pub type TestDataObject = data_directory::DataObject<Test>;
pub type TestDataObjectStorageRegistry = data_object_storage_registry::Module<Test>;
pub type TestContentDirectory = content_directory::Module<Test>;
pub type TestActors = actors::Module<Test>;

pub const TEST_FIRST_DATA_OBJECT_TYPE_ID: u64 = 1000;
pub const TEST_FIRST_CONTENT_ID: u64 = 2000;
pub const TEST_FIRST_RELATIONSHIP_ID: u64 = 3000;
pub const TEST_FIRST_METADATA_ID: u64 = 4000;
pub fn with_default_mock_builder<R, F: FnOnce() -> R>(f: F) -> R {
with_externalities(
&mut ExtBuilder::default()
Expand All @@ -237,6 +297,11 @@ pub fn with_default_mock_builder<R, F: FnOnce() -> R>(f: F) -> R {
.first_relationship_id(TEST_FIRST_RELATIONSHIP_ID)
.first_metadata_id(TEST_FIRST_METADATA_ID)
.build(),
|| f(),
|| {
let roles: Vec<actors::Role> = vec![actors::Role::Storage];
assert!(TestActors::set_available_roles(roles).is_ok(), "");

f()
},
)
}
14 changes: 14 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]

use crate::roles::actors;
use crate::storage::{data_directory, data_object_storage_registry, data_object_type_registry};
use parity_codec::Codec;
use runtime_primitives::traits::{As, MaybeSerializeDebug, Member, SimpleArithmetic};
Expand Down Expand Up @@ -42,12 +43,25 @@ impl<T: system::Trait> Members<T> for () {
// Roles
pub trait Roles<T: system::Trait> {
fn is_role_account(account_id: &T::AccountId) -> bool;

fn account_has_role(account_id: &T::AccountId, role: actors::Role) -> bool;

// If available, return a random account ID for the given role.
fn random_account_for_role(role: actors::Role) -> Result<T::AccountId, &'static str>;
}

impl<T: system::Trait> Roles<T> for () {
fn is_role_account(_who: &T::AccountId) -> bool {
false
}

fn account_has_role(_account_id: &T::AccountId, _role: actors::Role) -> bool {
false
}

fn random_account_for_role(_role: actors::Role) -> Result<T::AccountId, &'static str> {
Err("not implemented")
}
}

// Storage
Expand Down