Browse Source

Merge branch 'development' into update-substrate

Mokhtar Naamani 6 years ago
parent
commit
af64bbbea8

+ 7 - 11
src/governance/council.rs

@@ -1,13 +1,12 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use srml_support::{StorageValue, StorageMap, dispatch::Result, decl_module, decl_event, decl_storage, ensure};
-use srml_support::traits::{Currency};
-use system::{self, ensure_signed};
-use runtime_primitives::traits::{As, Zero};
 use rstd::prelude::*;
+use runtime_primitives::traits::{As, Zero};
+use srml_support::{decl_event, decl_module, decl_storage, ensure, StorageValue};
+use system;
 
-pub use super::election::{self, Seats, Seat, CouncilElected};
-pub use super::{ GovernanceCurrency, BalanceOf };
+pub use super::election::{self, CouncilElected, Seat, Seats};
+pub use super::{BalanceOf, GovernanceCurrency};
 
 // Hook For announcing that council term has ended
 pub trait CouncilTermEnded {
@@ -37,7 +36,7 @@ decl_storage! {
     }
 }
 
-/// Event for this module.
+// Event for this module.
 decl_event!(
     pub enum Event<T> where <T as system::Trait>::BlockNumber {
         CouncilTermEnded(BlockNumber),
@@ -56,7 +55,6 @@ impl<T: Trait> CouncilElected<Seats<T::AccountId, BalanceOf<T>>, T::BlockNumber>
 }
 
 impl<T: Trait> Module<T> {
-
     pub fn is_term_ended() -> bool {
         <system::Module<T>>::block_number() >= Self::term_ends_at()
     }
@@ -123,9 +121,7 @@ decl_module! {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
     use crate::governance::mock::*;
-    use parity_codec::Encode;
     use runtime_io::with_externalities;
     use srml_support::*;
 
@@ -161,7 +157,7 @@ mod tests {
     #[test]
     fn set_council_test() {
         with_externalities(&mut initial_test_ext(), || {
-            assert_ok!(Council::set_council(vec![4,5,6]));
+            assert_ok!(Council::set_council(vec![4, 5, 6]));
             assert!(Council::is_councilor(&4));
             assert!(Council::is_councilor(&5));
             assert!(Council::is_councilor(&6));

File diff suppressed because it is too large
+ 369 - 182
src/governance/election.rs


+ 12 - 10
src/governance/mock.rs

@@ -1,15 +1,14 @@
 #![cfg(test)]
 
-use rstd::prelude::*;
-pub use super::{election, council, proposals, GovernanceCurrency};
+pub use super::{council, election, proposals, GovernanceCurrency};
+use crate::traits::Members;
 pub use system;
-use crate::traits::{Members};
 
-pub use primitives::{H256, Blake2Hasher};
+pub use primitives::{Blake2Hasher, H256};
 pub use runtime_primitives::{
+    testing::{Digest, DigestItem, Header, UintAuthorityId},
+    traits::{BlakeTwo256, IdentityLookup, OnFinalise},
     BuildStorage,
-    traits::{BlakeTwo256, OnFinalise, IdentityLookup},
-    testing::{Digest, DigestItem, Header, UintAuthorityId}
 };
 
 use srml_support::impl_outer_origin;
@@ -21,16 +20,16 @@ impl_outer_origin! {
 pub struct MockMembership {}
 impl<T: system::Trait> Members<T> for MockMembership {
     type Id = u32;
-    fn is_active_member(who: &T::AccountId) -> bool {
+    fn is_active_member(_who: &T::AccountId) -> bool {
         // all accounts are considered members.
         // There is currently no test coverage for non-members.
         // Should add some coverage, and update this method to reflect which accounts are or are not members
         true
     }
-    fn lookup_member_id(account_id: &T::AccountId) -> Result<Self::Id, &'static str> {
+    fn lookup_member_id(_account_id: &T::AccountId) -> Result<Self::Id, &'static str> {
         Err("not implemented!")
     }
-    fn lookup_account_by_member_id(id: Self::Id) -> Result<T::AccountId, &'static str> {
+    fn lookup_account_by_member_id(_id: Self::Id) -> Result<T::AccountId, &'static str> {
         Err("not implemented!")
     }
 }
@@ -104,7 +103,10 @@ impl GovernanceCurrency for Test {
 // This function basically just builds a genesis storage key/value store according to
 // our desired mockup.
 pub fn initial_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
-    let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
+    let t = system::GenesisConfig::<Test>::default()
+        .build_storage()
+        .unwrap()
+        .0;
 
     runtime_io::TestExternalities::new(t)
 }

+ 3 - 3
src/governance/mod.rs

@@ -3,12 +3,12 @@
 use srml_support::traits::{Currency, LockableCurrency};
 use system;
 
-pub mod election;
 pub mod council;
+pub mod election;
 pub mod proposals;
 
-mod stake;
 mod sealed_vote;
+mod stake;
 
 pub trait GovernanceCurrency: system::Trait + Sized {
     type Currency: Currency<Self::AccountId> +
@@ -17,4 +17,4 @@ pub trait GovernanceCurrency: system::Trait + Sized {
 
 pub type BalanceOf<T> = <<T as GovernanceCurrency>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
 
-mod mock;
+mod mock;

+ 340 - 197
src/governance/proposals.rs

@@ -1,14 +1,18 @@
-use srml_support::{StorageValue, StorageMap, dispatch, decl_module, decl_event, decl_storage, ensure};
-use srml_support::traits::{Currency};
-use primitives::{storage::well_known_keys};
-use runtime_primitives::traits::{As, Hash, Zero};
-use runtime_io::print;
-use {balances, consensus, system::{self, ensure_signed}};
 use rstd::prelude::*;
+use runtime_io::print;
+use runtime_primitives::traits::{As, Hash, Zero};
+use srml_support::traits::Currency;
+use srml_support::{
+    decl_event, decl_module, decl_storage, dispatch, ensure, StorageMap, StorageValue,
+};
+use {consensus, system::{self, ensure_signed}};
+
+#[cfg(test)]
+use primitives::storage::well_known_keys;
 
 use super::council;
-pub use super::{ GovernanceCurrency, BalanceOf };
-use crate::traits::{Members};
+pub use super::{BalanceOf, GovernanceCurrency};
+use crate::traits::Members;
 
 const DEFAULT_APPROVAL_QUORUM: u32 = 60;
 const DEFAULT_MIN_STAKE: u64 = 100;
@@ -343,7 +347,6 @@ decl_module! {
 }
 
 impl<T: Trait> Module<T> {
-
     fn current_block() -> T::BlockNumber {
         <system::Module<T>>::block_number()
     }
@@ -382,21 +385,19 @@ impl<T: Trait> Module<T> {
         Ok(())
     }
 
-    fn end_block(now: T::BlockNumber) -> dispatch::Result {
-
+    fn end_block(_now: T::BlockNumber) -> dispatch::Result {
         // TODO refactor this method
 
         // TODO iterate over not expired proposals and tally
 
-          Self::tally()?;
-          // TODO approve or reject a proposal
+        Self::tally()?;
+        // TODO approve or reject a proposal
 
         Ok(())
     }
 
     /// Get the voters for the current proposal.
-    pub fn tally(/* proposal_id: u32 */) -> dispatch::Result {
-
+    pub fn tally() -> dispatch::Result {
         let councilors: u32 = Self::councilors_count();
         let quorum: u32 = Self::approval_quorum_seats();
 
@@ -433,26 +434,25 @@ impl<T: Trait> Module<T> {
             // Instead let other councilors cast their vote
             // up until the proposal's expired.
 
-            let new_status: Option<ProposalStatus> =
-                if all_councilors_slashed {
-                    Some(Slashed)
-                } else if all_councilors_voted {
-                    if quorum_reached {
-                        Some(Approved)
-                    } else {
-                        Some(Rejected)
-                    }
-                } else if is_expired {
-                    if quorum_reached {
-                        Some(Approved)
-                    } else {
-                        // Proposal has been expired and quorum not reached.
-                        Some(Expired)
-                    }
+            let new_status: Option<ProposalStatus> = if all_councilors_slashed {
+                Some(Slashed)
+            } else if all_councilors_voted {
+                if quorum_reached {
+                    Some(Approved)
                 } else {
-                    // Councilors still have time to vote on this proposal.
-                    None
-                };
+                    Some(Rejected)
+                }
+            } else if is_expired {
+                if quorum_reached {
+                    Some(Approved)
+                } else {
+                    // Proposal has been expired and quorum not reached.
+                    Some(Expired)
+                }
+            } else {
+                // Councilors still have time to vote on this proposal.
+                None
+            };
 
             // TODO move next block outside of tally to 'end_block'
             if let Some(status) = new_status {
@@ -493,7 +493,7 @@ impl<T: Trait> Module<T> {
                 Slashed => Self::_slash_proposal(pid)?,
                 Rejected | Expired => Self::_reject_proposal(pid)?,
                 Approved => Self::_approve_proposal(pid)?,
-                Active | Cancelled => { /* nothing */ },
+                Active | Cancelled => { /* nothing */ }
             }
             <ActiveProposalIds<T>>::put(other_active_ids);
             <Proposals<T>>::mutate(proposal_id, |p| p.status = new_status.clone());
@@ -549,16 +549,15 @@ impl<T: Trait> Module<T> {
 mod tests {
 
     use super::*;
+    use primitives::{Blake2Hasher, H256};
     use runtime_io::with_externalities;
-    use primitives::{H256, Blake2Hasher};
     // The testing primitives are very useful for avoiding having to work with signatures
     // or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried.
     use runtime_primitives::{
+        testing::{Digest, DigestItem, Header, UintAuthorityId},
+        traits::{BlakeTwo256, IdentityLookup, OnFinalise},
         BuildStorage,
-        traits::{BlakeTwo256, OnFinalise, IdentityLookup},
-        testing::{Digest, DigestItem, Header, UintAuthorityId}
     };
-    use system::{EventRecord, Phase};
     use srml_support::*;
 
     impl_outer_origin! {
@@ -623,16 +622,16 @@ mod tests {
     pub struct MockMembership {}
     impl<T: system::Trait> Members<T> for MockMembership {
         type Id = u32;
-        fn is_active_member(who: &T::AccountId) -> bool {
+        fn is_active_member(_who: &T::AccountId) -> bool {
             // all accounts are considered members.
             // There is currently no test coverage for non-members.
             // Should add some coverage, and update this method to reflect which accounts are or are not members
             true
         }
-        fn lookup_member_id(account_id: &T::AccountId) -> Result<Self::Id, &'static str> {
+        fn lookup_member_id(_account_id: &T::AccountId) -> Result<Self::Id, &'static str> {
             Err("not implemented!")
         }
-        fn lookup_account_by_member_id(id: Self::Id) -> Result<T::AccountId, &'static str> {
+        fn lookup_account_by_member_id(_id: Self::Id) -> Result<T::AccountId, &'static str> {
             Err("not implemented!")
         }
     }
@@ -652,13 +651,7 @@ mod tests {
 
     const NOT_COUNCILOR: u64 = 22;
 
-    const ALL_COUNCILORS: [u64; 5] = [
-        COUNCILOR1,
-        COUNCILOR2,
-        COUNCILOR3,
-        COUNCILOR4,
-        COUNCILOR5
-    ];
+    const ALL_COUNCILORS: [u64; 5] = [COUNCILOR1, COUNCILOR2, COUNCILOR3, COUNCILOR4, COUNCILOR5];
 
     // TODO Figure out how to test Events in test... (low priority)
     // mod proposals {
@@ -673,21 +666,36 @@ mod tests {
     // This function basically just builds a genesis storage key/value store according to
     // our desired mockup.
     fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
-        let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
+        let mut t = system::GenesisConfig::<Test>::default()
+            .build_storage()
+            .unwrap()
+            .0;
         // We use default for brevity, but you can configure as desired if needed.
-        t.extend(balances::GenesisConfig::<Test>::default().build_storage().unwrap().0);
-
-        let council_mock: council::Seats<u64, u64> =
-            ALL_COUNCILORS.iter().map(|&c| council::Seat {
+        t.extend(
+            balances::GenesisConfig::<Test>::default()
+                .build_storage()
+                .unwrap()
+                .0,
+        );
+
+        let council_mock: council::Seats<u64, u64> = ALL_COUNCILORS
+            .iter()
+            .map(|&c| council::Seat {
                 member: c,
                 stake: 0u64,
                 backers: vec![],
-            }).collect();
+            })
+            .collect();
 
-        t.extend(council::GenesisConfig::<Test>{
-            active_council: council_mock,
-            term_ends_at: 0,
-        }.build_storage().unwrap().0);
+        t.extend(
+            council::GenesisConfig::<Test> {
+                active_council: council_mock,
+                term_ends_at: 0,
+            }
+            .build_storage()
+            .unwrap()
+            .0,
+        );
 
         // t.extend(GenesisConfig::<Test>{
         //     // Here we can override defaults.
@@ -737,14 +745,14 @@ mod tests {
         stake: Option<u64>,
         name: Option<Vec<u8>>,
         description: Option<Vec<u8>>,
-        wasm_code: Option<Vec<u8>>
+        wasm_code: Option<Vec<u8>>,
     ) -> dispatch::Result {
         Proposals::create_proposal(
             Origin::signed(origin.unwrap_or(PROPOSER1)),
             stake.unwrap_or(min_stake()),
             name.unwrap_or(self::name()),
             description.unwrap_or(self::description()),
-            wasm_code.unwrap_or(self::wasm_code())
+            wasm_code.unwrap_or(self::wasm_code()),
         )
     }
 
@@ -753,11 +761,15 @@ mod tests {
     }
 
     macro_rules! assert_runtime_code_empty {
-        () => { assert_eq!(get_runtime_code(), None) }
+        () => {
+            assert_eq!(get_runtime_code(), None)
+        };
     }
 
     macro_rules! assert_runtime_code {
-        ($code:expr) => { assert_eq!(get_runtime_code(), Some($code)) }
+        ($code:expr) => {
+            assert_eq!(get_runtime_code(), Some($code))
+        };
     }
 
     #[test]
@@ -768,7 +780,10 @@ mod tests {
             assert_eq!(Proposals::cancellation_fee(), DEFAULT_CANCELLATION_FEE);
             assert_eq!(Proposals::rejection_fee(), DEFAULT_REJECTION_FEE);
             assert_eq!(Proposals::name_max_len(), DEFAULT_NAME_MAX_LEN);
-            assert_eq!(Proposals::description_max_len(), DEFAULT_DESCRIPTION_MAX_LEN);
+            assert_eq!(
+                Proposals::description_max_len(),
+                DEFAULT_DESCRIPTION_MAX_LEN
+            );
             assert_eq!(Proposals::wasm_code_max_len(), DEFAULT_WASM_CODE_MAX_LEN);
             assert_eq!(Proposals::proposal_count(), 0);
             assert!(Proposals::active_proposal_ids().is_empty());
@@ -793,12 +808,15 @@ mod tests {
                 description: description(),
                 wasm_hash,
                 proposed_at: 1,
-                status: Active
+                status: Active,
             };
             assert_eq!(Proposals::proposals(1), expected_proposal);
 
             // Check that stake amount has been locked on proposer's balance:
-            assert_eq!(Balances::free_balance(PROPOSER1), initial_balance() - min_stake());
+            assert_eq!(
+                Balances::free_balance(PROPOSER1),
+                initial_balance() - min_stake()
+            );
             assert_eq!(Balances::reserved_balance(PROPOSER1), min_stake());
 
             // TODO expect event ProposalCreated(AccountId, u32)
@@ -810,8 +828,10 @@ mod tests {
         with_externalities(&mut new_test_ext(), || {
             // In this test a proposer has an empty balance
             // thus he is not considered as a member.
-            assert_eq!(_create_default_proposal(),
-                Err(MSG_ONLY_MEMBERS_CAN_PROPOSE));
+            assert_eq!(
+                _create_default_proposal(),
+                Err(MSG_ONLY_MEMBERS_CAN_PROPOSE)
+            );
         });
     }
 
@@ -820,9 +840,10 @@ mod tests {
         with_externalities(&mut new_test_ext(), || {
             Balances::deposit_creating(&PROPOSER1, initial_balance());
 
-            assert_eq!(_create_proposal(
-                None, Some(min_stake() - 1), None, None, None),
-                Err(MSG_STAKE_IS_TOO_LOW));
+            assert_eq!(
+                _create_proposal(None, Some(min_stake() - 1), None, None, None),
+                Err(MSG_STAKE_IS_TOO_LOW)
+            );
 
             // Check that balances remain unchanged afer a failed attempt to create a proposal:
             assert_eq!(Balances::free_balance(PROPOSER1), initial_balance());
@@ -835,9 +856,10 @@ mod tests {
         with_externalities(&mut new_test_ext(), || {
             Balances::deposit_creating(&PROPOSER1, initial_balance());
 
-            assert_eq!(_create_proposal(
-                None, Some(initial_balance() + 1), None, None, None),
-                Err(MSG_STAKE_IS_GREATER_THAN_BALANCE));
+            assert_eq!(
+                _create_proposal(None, Some(initial_balance() + 1), None, None, None),
+                Err(MSG_STAKE_IS_GREATER_THAN_BALANCE)
+            );
 
             // Check that balances remain unchanged afer a failed attempt to create a proposal:
             assert_eq!(Balances::free_balance(PROPOSER1), initial_balance());
@@ -851,19 +873,22 @@ mod tests {
             Balances::deposit_creating(&PROPOSER1, initial_balance());
 
             // Empty name:
-            assert_eq!(_create_proposal(
-                None, None, Some(vec![]), None, None),
-                Err(MSG_EMPTY_NAME_PROVIDED));
+            assert_eq!(
+                _create_proposal(None, None, Some(vec![]), None, None),
+                Err(MSG_EMPTY_NAME_PROVIDED)
+            );
 
             // Empty description:
-            assert_eq!(_create_proposal(
-                None, None, None, Some(vec![]), None),
-                Err(MSG_EMPTY_DESCRIPTION_PROVIDED));
+            assert_eq!(
+                _create_proposal(None, None, None, Some(vec![]), None),
+                Err(MSG_EMPTY_DESCRIPTION_PROVIDED)
+            );
 
             // Empty WASM code:
-            assert_eq!(_create_proposal(
-                None, None, None, None, Some(vec![])),
-                Err(MSG_EMPTY_WASM_CODE_PROVIDED));
+            assert_eq!(
+                _create_proposal(None, None, None, None, Some(vec![])),
+                Err(MSG_EMPTY_WASM_CODE_PROVIDED)
+            );
         });
     }
 
@@ -873,19 +898,22 @@ mod tests {
             Balances::deposit_creating(&PROPOSER1, initial_balance());
 
             // Too long name:
-            assert_eq!(_create_proposal(
-                None, None, Some(too_long_name()), None, None),
-                Err(MSG_TOO_LONG_NAME));
+            assert_eq!(
+                _create_proposal(None, None, Some(too_long_name()), None, None),
+                Err(MSG_TOO_LONG_NAME)
+            );
 
             // Too long description:
-            assert_eq!(_create_proposal(
-                None, None, None, Some(too_long_description()), None),
-                Err(MSG_TOO_LONG_DESCRIPTION));
+            assert_eq!(
+                _create_proposal(None, None, None, Some(too_long_description()), None),
+                Err(MSG_TOO_LONG_DESCRIPTION)
+            );
 
             // Too long WASM code:
-            assert_eq!(_create_proposal(
-                None, None, None, None, Some(too_long_wasm_code())),
-                Err(MSG_TOO_LONG_WASM_CODE));
+            assert_eq!(
+                _create_proposal(None, None, None, None, Some(too_long_wasm_code())),
+                Err(MSG_TOO_LONG_WASM_CODE)
+            );
         });
     }
 
@@ -915,7 +943,10 @@ mod tests {
             assert!(Proposals::active_proposal_ids().is_empty());
 
             // Check that proposer's balance reduced by cancellation fee and other part of his stake returned to his balance:
-            assert_eq!(Balances::free_balance(PROPOSER1), initial_balance() - cancellation_fee());
+            assert_eq!(
+                Balances::free_balance(PROPOSER1),
+                initial_balance() - cancellation_fee()
+            );
             assert_eq!(Balances::reserved_balance(PROPOSER1), 0);
 
             // TODO expect event ProposalCancelled(AccountId, u32)
@@ -935,12 +966,17 @@ mod tests {
             let updated_free_balance = Balances::free_balance(PROPOSER1);
             let updated_reserved_balance = Balances::reserved_balance(PROPOSER1);
 
-            assert_eq!(Proposals::cancel_proposal(Origin::signed(PROPOSER1), 1),
-                Err(MSG_PROPOSAL_FINALIZED));
+            assert_eq!(
+                Proposals::cancel_proposal(Origin::signed(PROPOSER1), 1),
+                Err(MSG_PROPOSAL_FINALIZED)
+            );
 
             // Check that proposer's balance and locked stake haven't been changed:
             assert_eq!(Balances::free_balance(PROPOSER1), updated_free_balance);
-            assert_eq!(Balances::reserved_balance(PROPOSER1), updated_reserved_balance);
+            assert_eq!(
+                Balances::reserved_balance(PROPOSER1),
+                updated_reserved_balance
+            );
         });
     }
 
@@ -950,8 +986,10 @@ mod tests {
             Balances::deposit_creating(&PROPOSER1, initial_balance());
             Balances::deposit_creating(&PROPOSER2, initial_balance());
             assert_ok!(_create_default_proposal());
-            assert_eq!(Proposals::cancel_proposal(Origin::signed(PROPOSER2), 1),
-                Err(MSG_YOU_DONT_OWN_THIS_PROPOSAL));
+            assert_eq!(
+                Proposals::cancel_proposal(Origin::signed(PROPOSER2), 1),
+                Err(MSG_YOU_DONT_OWN_THIS_PROPOSAL)
+            );
         });
     }
 
@@ -966,11 +1004,17 @@ mod tests {
             assert_ok!(_create_default_proposal());
 
             assert_ok!(Proposals::vote_on_proposal(
-                Origin::signed(COUNCILOR1), 1, Approve));
+                Origin::signed(COUNCILOR1),
+                1,
+                Approve
+            ));
 
             // Check that a vote has been saved:
             assert_eq!(Proposals::votes_by_proposal(1), vec![(COUNCILOR1, Approve)]);
-            assert_eq!(Proposals::vote_by_account_and_proposal((COUNCILOR1, 1)), Approve);
+            assert_eq!(
+                Proposals::vote_by_account_and_proposal((COUNCILOR1, 1)),
+                Approve
+            );
 
             // TODO expect event Voted(PROPOSER1, 1, Approve)
         });
@@ -984,10 +1028,14 @@ mod tests {
             assert_ok!(_create_default_proposal());
 
             assert_ok!(Proposals::vote_on_proposal(
-                Origin::signed(COUNCILOR1), 1, Approve));
-            assert_eq!(Proposals::vote_on_proposal(
-                Origin::signed(COUNCILOR1), 1, Approve),
-                Err(MSG_YOU_ALREADY_VOTED));
+                Origin::signed(COUNCILOR1),
+                1,
+                Approve
+            ));
+            assert_eq!(
+                Proposals::vote_on_proposal(Origin::signed(COUNCILOR1), 1, Approve),
+                Err(MSG_YOU_ALREADY_VOTED)
+            );
         });
     }
 
@@ -1003,7 +1051,10 @@ mod tests {
             // Check that a vote has been sent automatically,
             // such as the proposer is a councilor:
             assert_eq!(Proposals::votes_by_proposal(1), vec![(COUNCILOR1, Approve)]);
-            assert_eq!(Proposals::vote_by_account_and_proposal((COUNCILOR1, 1)), Approve);
+            assert_eq!(
+                Proposals::vote_by_account_and_proposal((COUNCILOR1, 1)),
+                Approve
+            );
         });
     }
 
@@ -1013,9 +1064,10 @@ mod tests {
             Balances::deposit_creating(&PROPOSER1, initial_balance());
 
             assert_ok!(_create_default_proposal());
-            assert_eq!(Proposals::vote_on_proposal(
-                Origin::signed(NOT_COUNCILOR), 1, Approve),
-                Err(MSG_ONLY_COUNCILORS_CAN_VOTE));
+            assert_eq!(
+                Proposals::vote_on_proposal(Origin::signed(NOT_COUNCILOR), 1, Approve),
+                Err(MSG_ONLY_COUNCILORS_CAN_VOTE)
+            );
         });
     }
 
@@ -1026,9 +1078,10 @@ mod tests {
 
             assert_ok!(_create_default_proposal());
             assert_ok!(Proposals::cancel_proposal(Origin::signed(PROPOSER1), 1));
-            assert_eq!(Proposals::vote_on_proposal(
-                Origin::signed(COUNCILOR1), 1, Approve),
-                Err(MSG_PROPOSAL_FINALIZED));
+            assert_eq!(
+                Proposals::vote_on_proposal(Origin::signed(COUNCILOR1), 1, Approve),
+                Err(MSG_PROPOSAL_FINALIZED)
+            );
         });
     }
 
@@ -1043,8 +1096,15 @@ mod tests {
             let mut expected_votes: Vec<(u64, VoteKind)> = vec![];
             for &councilor in ALL_COUNCILORS.iter() {
                 expected_votes.push((councilor, Approve));
-                assert_ok!(Proposals::vote_on_proposal(Origin::signed(councilor), 1, Approve));
-                assert_eq!(Proposals::vote_by_account_and_proposal((councilor, 1)), Approve);
+                assert_ok!(Proposals::vote_on_proposal(
+                    Origin::signed(councilor),
+                    1,
+                    Approve
+                ));
+                assert_eq!(
+                    Proposals::vote_by_account_and_proposal((councilor, 1)),
+                    Approve
+                );
             }
             assert_eq!(Proposals::votes_by_proposal(1), expected_votes);
 
@@ -1055,9 +1115,10 @@ mod tests {
             assert_eq!(Proposals::proposals(1).status, Approved);
 
             // Try to vote on finalized proposal:
-            assert_eq!(Proposals::vote_on_proposal(
-                Origin::signed(COUNCILOR1), 1, Reject),
-                Err(MSG_PROPOSAL_FINALIZED));
+            assert_eq!(
+                Proposals::vote_on_proposal(Origin::signed(COUNCILOR1), 1, Reject),
+                Err(MSG_PROPOSAL_FINALIZED)
+            );
         });
     }
 
@@ -1075,8 +1136,15 @@ mod tests {
             let mut expected_votes: Vec<(u64, VoteKind)> = vec![];
             for &councilor in ALL_COUNCILORS.iter() {
                 expected_votes.push((councilor, Approve));
-                assert_ok!(Proposals::vote_on_proposal(Origin::signed(councilor), 1, Approve));
-                assert_eq!(Proposals::vote_by_account_and_proposal((councilor, 1)), Approve);
+                assert_ok!(Proposals::vote_on_proposal(
+                    Origin::signed(councilor),
+                    1,
+                    Approve
+                ));
+                assert_eq!(
+                    Proposals::vote_by_account_and_proposal((councilor, 1)),
+                    Approve
+                );
             }
             assert_eq!(Proposals::votes_by_proposal(1), expected_votes);
 
@@ -1090,15 +1158,18 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Approved);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: 0,
-                approvals: ALL_COUNCILORS.len() as u32,
-                rejections: 0,
-                slashes: 0,
-                status: Approved,
-                finalized_at: 2
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: 0,
+                    approvals: ALL_COUNCILORS.len() as u32,
+                    rejections: 0,
+                    slashes: 0,
+                    status: Approved,
+                    finalized_at: 2
+                }
+            );
 
             // Check that proposer's stake has been added back to his balance:
             assert_eq!(Balances::free_balance(PROPOSER1), initial_balance());
@@ -1120,9 +1191,16 @@ mod tests {
             let approvals = Proposals::approval_quorum_seats();
             let rejections = councilors - approvals;
             for i in 0..councilors as usize {
-                let vote = if (i as u32) < approvals { Approve } else { Reject };
+                let vote = if (i as u32) < approvals {
+                    Approve
+                } else {
+                    Reject
+                };
                 assert_ok!(Proposals::vote_on_proposal(
-                    Origin::signed(ALL_COUNCILORS[i]), 1, vote));
+                    Origin::signed(ALL_COUNCILORS[i]),
+                    1,
+                    vote
+                ));
             }
             assert_eq!(Proposals::votes_by_proposal(1).len() as u32, councilors);
 
@@ -1136,15 +1214,18 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Approved);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: 0,
-                approvals: approvals,
-                rejections: rejections,
-                slashes: 0,
-                status: Approved,
-                finalized_at: 2
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: 0,
+                    approvals: approvals,
+                    rejections: rejections,
+                    slashes: 0,
+                    status: Approved,
+                    finalized_at: 2
+                }
+            );
 
             // Check that proposer's stake has been added back to his balance:
             assert_eq!(Balances::free_balance(PROPOSER1), initial_balance());
@@ -1164,9 +1245,16 @@ mod tests {
             // Only quorum of councilors approved, other councilors didn't vote:
             let approvals = Proposals::approval_quorum_seats();
             for i in 0..approvals as usize {
-                let vote = if (i as u32) < approvals { Approve } else { Slash };
+                let vote = if (i as u32) < approvals {
+                    Approve
+                } else {
+                    Slash
+                };
                 assert_ok!(Proposals::vote_on_proposal(
-                    Origin::signed(ALL_COUNCILORS[i]), 1, vote));
+                    Origin::signed(ALL_COUNCILORS[i]),
+                    1,
+                    vote
+                ));
             }
             assert_eq!(Proposals::votes_by_proposal(1).len() as u32, approvals);
 
@@ -1188,15 +1276,18 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Approved);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: 0,
-                approvals: approvals,
-                rejections: 0,
-                slashes: 0,
-                status: Approved,
-                finalized_at: expiration_block
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: 0,
+                    approvals: approvals,
+                    rejections: 0,
+                    slashes: 0,
+                    status: Approved,
+                    finalized_at: expiration_block
+                }
+            );
 
             // Check that proposer's stake has been added back to his balance:
             assert_eq!(Balances::free_balance(PROPOSER1), initial_balance());
@@ -1218,9 +1309,16 @@ mod tests {
             let approvals = Proposals::approval_quorum_seats() - 1;
             let abstentions = councilors - approvals;
             for i in 0..councilors as usize {
-                let vote = if (i as u32) < approvals { Approve } else { Abstain };
+                let vote = if (i as u32) < approvals {
+                    Approve
+                } else {
+                    Abstain
+                };
                 assert_ok!(Proposals::vote_on_proposal(
-                    Origin::signed(ALL_COUNCILORS[i]), 1, vote));
+                    Origin::signed(ALL_COUNCILORS[i]),
+                    1,
+                    vote
+                ));
             }
             assert_eq!(Proposals::votes_by_proposal(1).len() as u32, councilors);
 
@@ -1234,18 +1332,24 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Rejected);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: abstentions,
-                approvals: approvals,
-                rejections: 0,
-                slashes: 0,
-                status: Rejected,
-                finalized_at: 2
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: abstentions,
+                    approvals: approvals,
+                    rejections: 0,
+                    slashes: 0,
+                    status: Rejected,
+                    finalized_at: 2
+                }
+            );
 
             // Check that proposer's balance reduced by burnt stake:
-            assert_eq!(Balances::free_balance(PROPOSER1), initial_balance() - rejection_fee());
+            assert_eq!(
+                Balances::free_balance(PROPOSER1),
+                initial_balance() - rejection_fee()
+            );
             assert_eq!(Balances::reserved_balance(PROPOSER1), 0);
 
             // TODO expect event ProposalStatusUpdated(1, Rejected)
@@ -1263,8 +1367,15 @@ mod tests {
             let mut expected_votes: Vec<(u64, VoteKind)> = vec![];
             for &councilor in ALL_COUNCILORS.iter() {
                 expected_votes.push((councilor, Reject));
-                assert_ok!(Proposals::vote_on_proposal(Origin::signed(councilor), 1, Reject));
-                assert_eq!(Proposals::vote_by_account_and_proposal((councilor, 1)), Reject);
+                assert_ok!(Proposals::vote_on_proposal(
+                    Origin::signed(councilor),
+                    1,
+                    Reject
+                ));
+                assert_eq!(
+                    Proposals::vote_by_account_and_proposal((councilor, 1)),
+                    Reject
+                );
             }
             assert_eq!(Proposals::votes_by_proposal(1), expected_votes);
 
@@ -1278,18 +1389,24 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Rejected);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: 0,
-                approvals: 0,
-                rejections: ALL_COUNCILORS.len() as u32,
-                slashes: 0,
-                status: Rejected,
-                finalized_at: 2
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: 0,
+                    approvals: 0,
+                    rejections: ALL_COUNCILORS.len() as u32,
+                    slashes: 0,
+                    status: Rejected,
+                    finalized_at: 2
+                }
+            );
 
             // Check that proposer's balance reduced by burnt stake:
-            assert_eq!(Balances::free_balance(PROPOSER1), initial_balance() - rejection_fee());
+            assert_eq!(
+                Balances::free_balance(PROPOSER1),
+                initial_balance() - rejection_fee()
+            );
             assert_eq!(Balances::reserved_balance(PROPOSER1), 0);
 
             // TODO expect event ProposalStatusUpdated(1, Rejected)
@@ -1307,8 +1424,15 @@ mod tests {
             let mut expected_votes: Vec<(u64, VoteKind)> = vec![];
             for &councilor in ALL_COUNCILORS.iter() {
                 expected_votes.push((councilor, Slash));
-                assert_ok!(Proposals::vote_on_proposal(Origin::signed(councilor), 1, Slash));
-                assert_eq!(Proposals::vote_by_account_and_proposal((councilor, 1)), Slash);
+                assert_ok!(Proposals::vote_on_proposal(
+                    Origin::signed(councilor),
+                    1,
+                    Slash
+                ));
+                assert_eq!(
+                    Proposals::vote_by_account_and_proposal((councilor, 1)),
+                    Slash
+                );
             }
             assert_eq!(Proposals::votes_by_proposal(1), expected_votes);
 
@@ -1322,18 +1446,24 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Slashed);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: 0,
-                approvals: 0,
-                rejections: 0,
-                slashes: ALL_COUNCILORS.len() as u32,
-                status: Slashed,
-                finalized_at: 2
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: 0,
+                    approvals: 0,
+                    rejections: 0,
+                    slashes: ALL_COUNCILORS.len() as u32,
+                    status: Slashed,
+                    finalized_at: 2
+                }
+            );
 
             // Check that proposer's balance reduced by burnt stake:
-            assert_eq!(Balances::free_balance(PROPOSER1), initial_balance() - min_stake());
+            assert_eq!(
+                Balances::free_balance(PROPOSER1),
+                initial_balance() - min_stake()
+            );
             assert_eq!(Balances::reserved_balance(PROPOSER1), 0);
 
             // TODO expect event ProposalStatusUpdated(1, Slashed)
@@ -1359,9 +1489,16 @@ mod tests {
             // Less than a quorum of councilors approved:
             let approvals = Proposals::approval_quorum_seats() - 1;
             for i in 0..approvals as usize {
-                let vote = if (i as u32) < approvals { Approve } else { Slash };
+                let vote = if (i as u32) < approvals {
+                    Approve
+                } else {
+                    Slash
+                };
                 assert_ok!(Proposals::vote_on_proposal(
-                    Origin::signed(ALL_COUNCILORS[i]), 1, vote));
+                    Origin::signed(ALL_COUNCILORS[i]),
+                    1,
+                    vote
+                ));
             }
             assert_eq!(Proposals::votes_by_proposal(1).len() as u32, approvals);
 
@@ -1376,18 +1513,24 @@ mod tests {
 
             assert!(Proposals::active_proposal_ids().is_empty());
             assert_eq!(Proposals::proposals(1).status, Expired);
-            assert_eq!(Proposals::tally_results(1), TallyResult {
-                proposal_id: 1,
-                abstentions: 0,
-                approvals: approvals,
-                rejections: 0,
-                slashes: 0,
-                status: Expired,
-                finalized_at: expiration_block
-            });
+            assert_eq!(
+                Proposals::tally_results(1),
+                TallyResult {
+                    proposal_id: 1,
+                    abstentions: 0,
+                    approvals: approvals,
+                    rejections: 0,
+                    slashes: 0,
+                    status: Expired,
+                    finalized_at: expiration_block
+                }
+            );
 
             // Check that proposer's balance reduced by burnt stake:
-            assert_eq!(Balances::free_balance(PROPOSER1), initial_balance() - rejection_fee());
+            assert_eq!(
+                Balances::free_balance(PROPOSER1),
+                initial_balance() - rejection_fee()
+            );
             assert_eq!(Balances::reserved_balance(PROPOSER1), 0);
 
             // TODO expect event ProposalStatusUpdated(1, Rejected)

+ 31 - 11
src/governance/sealed_vote.rs

@@ -1,23 +1,33 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use srml_support::{ensure};
 use parity_codec::Encode;
 use rstd::vec::Vec;
+use srml_support::ensure;
 
 #[derive(Clone, Copy, Encode, Decode, Default)]
 pub struct SealedVote<AccountId, Stake, Hash, Vote>
-    where Vote: Encode, Hash: PartialEq, AccountId: PartialEq
+where
+    Vote: Encode,
+    Hash: PartialEq,
+    AccountId: PartialEq,
 {
-  pub voter: AccountId,
-  pub commitment: Hash, // 32 bytes - salted hash of serialized Vote
-  pub stake: Stake,
-  vote: Option<Vote>, // will be set when unsealing
+    pub voter: AccountId,
+    pub commitment: Hash, // 32 bytes - salted hash of serialized Vote
+    pub stake: Stake,
+    vote: Option<Vote>, // will be set when unsealing
 }
 
 impl<AccountId, Stake, Hash, Vote> SealedVote<AccountId, Stake, Hash, Vote>
-    where Vote: Encode, Hash: PartialEq, AccountId: PartialEq
+where
+    Vote: Encode,
+    Hash: PartialEq,
+    AccountId: PartialEq,
 {
-    pub fn new(voter: AccountId, stake: Stake, commitment: Hash) -> SealedVote<AccountId, Stake, Hash, Vote> {
+    pub fn new(
+        voter: AccountId,
+        stake: Stake,
+        commitment: Hash,
+    ) -> SealedVote<AccountId, Stake, Hash, Vote> {
         SealedVote {
             voter,
             commitment,
@@ -26,7 +36,12 @@ impl<AccountId, Stake, Hash, Vote> SealedVote<AccountId, Stake, Hash, Vote>
         }
     }
 
-    pub fn new_unsealed(voter: AccountId, stake: Stake, commitment: Hash, vote: Vote) -> SealedVote<AccountId, Stake, Hash, Vote> {
+    pub fn new_unsealed(
+        voter: AccountId,
+        stake: Stake,
+        commitment: Hash,
+        vote: Vote,
+    ) -> SealedVote<AccountId, Stake, Hash, Vote> {
         SealedVote {
             voter,
             commitment,
@@ -35,7 +50,12 @@ impl<AccountId, Stake, Hash, Vote> SealedVote<AccountId, Stake, Hash, Vote>
         }
     }
 
-    pub fn unseal(&mut self, vote: Vote, salt: &mut Vec<u8>, hasher: fn(&[u8]) -> Hash) -> Result<(), &'static str> {
+    pub fn unseal(
+        &mut self,
+        vote: Vote,
+        salt: &mut Vec<u8>,
+        hasher: fn(&[u8]) -> Hash,
+    ) -> Result<(), &'static str> {
         // only unseal once
         ensure!(self.is_not_revealed(), "vote already unsealed");
 
@@ -67,4 +87,4 @@ impl<AccountId, Stake, Hash, Vote> SealedVote<AccountId, Stake, Hash, Vote>
     pub fn is_not_revealed(&self) -> bool {
         self.vote.is_none()
     }
-}
+}

+ 17 - 10
src/governance/stake.rs

@@ -1,18 +1,20 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 use rstd::cmp::Ordering;
-use runtime_primitives::traits::{SimpleArithmetic};
+use runtime_primitives::traits::SimpleArithmetic;
 
 #[derive(Encode, Decode, Clone, Copy, Default, Debug)]
 pub struct Stake<Balance>
-    where Balance: Copy + SimpleArithmetic,
+where
+    Balance: Copy + SimpleArithmetic,
 {
     pub new: Balance,
     pub transferred: Balance,
 }
 
 impl<Balance> Stake<Balance>
-    where Balance: Copy + SimpleArithmetic,
+where
+    Balance: Copy + SimpleArithmetic,
 {
     pub fn total(&self) -> Balance {
         self.new + self.transferred
@@ -21,7 +23,7 @@ impl<Balance> Stake<Balance>
     pub fn add(&self, v: &Self) -> Self {
         Self {
             new: self.new + v.new,
-            transferred: self.transferred + v.transferred
+            transferred: self.transferred + v.transferred,
         }
     }
 }
@@ -63,8 +65,10 @@ mod tests {
 
     #[test]
     fn adding() {
-        let a1: u128 = 3; let b1: u128 = 2;
-        let a2: u128 = 5; let b2: u128 = 7;
+        let a1: u128 = 3;
+        let b1: u128 = 2;
+        let a2: u128 = 5;
+        let b2: u128 = 7;
 
         let s1 = Stake {
             new: a1,
@@ -84,9 +88,12 @@ mod tests {
 
     #[test]
     fn equality() {
-        let a1: u128 = 3; let b1: u128 = 2;
-        let a2: u128 = 2; let b2: u128 = 3;
-        let a3: u128 = 10; let b3: u128 = 10;
+        let a1: u128 = 3;
+        let b1: u128 = 2;
+        let a2: u128 = 2;
+        let b2: u128 = 3;
+        let a3: u128 = 10;
+        let b3: u128 = 10;
 
         let s1 = Stake {
             new: a1,
@@ -111,4 +118,4 @@ mod tests {
 
         assert_ne!(s1, s4);
     }
-}
+}

+ 121 - 84
src/lib.rs

@@ -3,7 +3,7 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(not(feature = "std"), feature(alloc))]
 // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
-#![recursion_limit="256"]
+#![recursion_limit = "256"]
 
 #[cfg(feature = "std")]
 #[macro_use]
@@ -15,43 +15,46 @@ use substrate_client as client;
 extern crate parity_codec_derive;
 
 pub mod governance;
-use governance::{election, council, proposals};
+use governance::{council, election, proposals};
 pub mod storage;
-use storage::{data_object_type_registry};
+use storage::{
+    content_directory, data_directory, data_object_storage_registry, data_object_type_registry,
+    downloads,
+};
+mod membership;
 mod memo;
 mod traits;
-mod membership;
 use membership::members;
 mod migration;
 mod roles;
 use roles::actors;
-
-use rstd::prelude::*;
+use rstd::prelude::*; // needed for Vec
+use client::{
+    block_builder::api::{self as block_builder_api, CheckInherentsResult, InherentData},
+    impl_runtime_apis, runtime_api,
+};
 #[cfg(feature = "std")]
 use primitives::bytes;
 use primitives::{ed25519, OpaqueMetadata};
 use runtime_primitives::{
 	ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str,
-	traits::{self as runtime_traits, Convert, BlakeTwo256, Block as BlockT, StaticLookup, Verify},
+	traits::{self as runtime_traits, BlakeTwo256, Block as BlockT, StaticLookup, Verify},
 };
-use client::{
-	block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api},
-	runtime_api, impl_runtime_apis
-};
-use version::RuntimeVersion;
+
 #[cfg(feature = "std")]
 use version::NativeVersion;
+use version::RuntimeVersion;
 
 // A few exports that help ease life for downstream crates.
+pub use balances::Call as BalancesCall;
+pub use consensus::Call as ConsensusCall;
 #[cfg(any(feature = "std", test))]
 pub use runtime_primitives::BuildStorage;
-pub use consensus::Call as ConsensusCall;
-pub use timestamp::Call as TimestampCall;
-pub use balances::Call as BalancesCall;
-pub use runtime_primitives::{Permill, Perbill};
+pub use runtime_primitives::{Perbill, Permill};
+pub use srml_support::{construct_runtime, StorageValue};
 pub use timestamp::BlockPeriod;
-pub use srml_support::{StorageValue, construct_runtime};
 pub use staking::StakerStatus;
+pub use timestamp::Call as TimestampCall;
 
 /// The type that is used for identifying authorities.
 pub type AuthorityId = <AuthoritySignature as Verify>::Signer;
@@ -65,6 +68,9 @@ pub type AccountId = <AccountSignature as Verify>::Signer;
 /// The type used by accounts to prove their ID.
 pub type AccountSignature = ed25519::Signature;
 
+/// Alias for ContentId, used in various places
+pub type ContentId = u64;
+
 /// A hash of some data used by the chain.
 pub type Hash = primitives::H256;
 
@@ -102,50 +108,50 @@ pub mod opaque {
 
 /// This runtime version.
 pub const VERSION: RuntimeVersion = RuntimeVersion {
-	spec_name: create_runtime_str!("joystream-node"),
-	impl_name: create_runtime_str!("joystream-node"),
-	authoring_version: 3,
-	spec_version: 5,
-	impl_version: 0,
-	apis: RUNTIME_API_VERSIONS,
+    spec_name: create_runtime_str!("joystream-node"),
+    impl_name: create_runtime_str!("joystream-node"),
+    authoring_version: 3,
+    spec_version: 5,
+    impl_version: 0,
+    apis: RUNTIME_API_VERSIONS,
 };
 
 /// The version infromation used to identify this runtime when compiled natively.
 #[cfg(feature = "std")]
 pub fn native_version() -> NativeVersion {
-	NativeVersion {
-		runtime_version: VERSION,
-		can_author_with: Default::default(),
-	}
+    NativeVersion {
+        runtime_version: VERSION,
+        can_author_with: Default::default(),
+    }
 }
 
 impl system::Trait for Runtime {
-	/// The identifier used to distinguish between accounts.
-	type AccountId = AccountId;
-	/// The lookup mechanism to get account ID from whatever is passed in dispatchers.
-	type Lookup = Indices;
-	/// The index type for storing how many extrinsics an account has signed.
-	type Index = Nonce;
-	/// The index type for blocks.
-	type BlockNumber = BlockNumber;
-	/// The type for hashing blocks and tries.
-	type Hash = Hash;
-	/// The hashing algorithm used.
-	type Hashing = BlakeTwo256;
-	/// The header digest type.
-	type Digest = generic::Digest<Log>;
-	/// The header type.
-	type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
-	/// The ubiquitous event type.
-	type Event = Event;
-	/// The ubiquitous log type.
-	type Log = Log;
-	/// The ubiquitous origin type.
-	type Origin = Origin;
+    /// The identifier used to distinguish between accounts.
+    type AccountId = AccountId;
+    /// The lookup mechanism to get account ID from whatever is passed in dispatchers.
+    type Lookup = Indices;
+    /// The index type for storing how many extrinsics an account has signed.
+    type Index = Nonce;
+    /// The index type for blocks.
+    type BlockNumber = BlockNumber;
+    /// The type for hashing blocks and tries.
+    type Hash = Hash;
+    /// The hashing algorithm used.
+    type Hashing = BlakeTwo256;
+    /// The header digest type.
+    type Digest = generic::Digest<Log>;
+    /// The header type.
+    type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
+    /// The ubiquitous event type.
+    type Event = Event;
+    /// The ubiquitous log type.
+    type Log = Log;
+    /// The ubiquitous origin type.
+    type Origin = Origin;
 }
 
 impl aura::Trait for Runtime {
-	type HandleReport = aura::StakingSlasher<Runtime>;
+    type HandleReport = aura::StakingSlasher<Runtime>;
 }
 
 impl consensus::Trait for Runtime {
@@ -165,21 +171,21 @@ impl session::Trait for Runtime {
 }
 
 impl indices::Trait for Runtime {
-	/// The type for recording indexing into the account enumeration. If this ever overflows, there
-	/// will be problems!
-	type AccountIndex = u32;
-	/// Use the standard means of resolving an index hint from an id.
-	type ResolveHint = indices::SimpleResolveHint<Self::AccountId, Self::AccountIndex>;
-	/// Determine whether an account is dead.
-	type IsDeadAccount = Balances;
-	/// The uniquitous event type.
-	type Event = Event;
+    /// The type for recording indexing into the account enumeration. If this ever overflows, there
+    /// will be problems!
+    type AccountIndex = u32;
+    /// Use the standard means of resolving an index hint from an id.
+    type ResolveHint = indices::SimpleResolveHint<Self::AccountId, Self::AccountIndex>;
+    /// Determine whether an account is dead.
+    type IsDeadAccount = Balances;
+    /// The uniquitous event type.
+    type Event = Event;
 }
 
 impl timestamp::Trait for Runtime {
-	/// A timestamp: seconds since the unix epoch.
-	type Moment = u64;
-	type OnTimestampSet = Aura;
+    /// A timestamp: seconds since the unix epoch.
+    type Moment = u64;
+    type OnTimestampSet = Aura;
 }
 
 impl balances::Trait for Runtime {
@@ -198,9 +204,9 @@ impl balances::Trait for Runtime {
 }
 
 impl sudo::Trait for Runtime {
-	/// The uniquitous event type.
-	type Event = Event;
-	type Proposal = Call;
+    /// The uniquitous event type.
+    type Event = Event;
+    type Proposal = Call;
 }
 
 impl staking::Trait for Runtime {
@@ -213,49 +219,76 @@ impl staking::Trait for Runtime {
 }
 
 impl governance::GovernanceCurrency for Runtime {
-	type Currency = balances::Module<Self>;
+    type Currency = balances::Module<Self>;
 }
 
 impl governance::proposals::Trait for Runtime {
-	type Event = Event;
-	type Members = Members;
+    type Event = Event;
+    type Members = Members;
 }
 
 impl governance::election::Trait for Runtime {
-	type Event = Event;
-	type CouncilElected = (Council,);
-	type Members = Members;
+    type Event = Event;
+    type CouncilElected = (Council,);
+    type Members = Members;
 }
 
 impl governance::council::Trait for Runtime {
-	type Event = Event;
-	type CouncilTermEnded = (CouncilElection,);
+    type Event = Event;
+    type CouncilTermEnded = (CouncilElection,);
 }
 
 impl memo::Trait for Runtime {
-	type Event = Event;
+    type Event = Event;
 }
 
 impl storage::data_object_type_registry::Trait for Runtime {
-	type Event = Event;
-	type DataObjectTypeId = u64;
+    type Event = Event;
+    type DataObjectTypeId = u64;
+}
+
+impl storage::data_directory::Trait for Runtime {
+    type Event = Event;
+    type ContentId = ContentId;
+    type Members = Members;
+    type IsActiveDataObjectType = DataObjectTypeRegistry;
+}
+
+impl storage::downloads::Trait for Runtime {
+    type Event = Event;
+    type DownloadSessionId = u64;
+    type ContentHasStorage = DataObjectStorageRegistry;
+}
+
+impl storage::data_object_storage_registry::Trait for Runtime {
+    type Event = Event;
+    type DataObjectStorageRelationshipId = u64;
+    type Members = Members;
+    type ContentIdExists = DataDirectory;
+}
+
+impl storage::content_directory::Trait for Runtime {
+    type Event = Event;
+    type MetadataId = u64;
+    type SchemaId = u64;
+    type Members = Members;
 }
 
 impl members::Trait for Runtime {
-	type Event = Event;
-	type MemberId = u64;
-	type PaidTermId = u64;
-	type SubscriptionId = u64;
-	type Roles = Actors;
+    type Event = Event;
+    type MemberId = u64;
+    type PaidTermId = u64;
+    type SubscriptionId = u64;
+    type Roles = Actors;
 }
 
 impl migration::Trait for Runtime {
-	type Event = Event;
+    type Event = Event;
 }
 
 impl actors::Trait for Runtime {
-	type Event = Event;
-	type Members = Members;
+    type Event = Event;
+    type Members = Members;
 }
 
 construct_runtime!(
@@ -281,6 +314,10 @@ construct_runtime!(
 		Migration: migration::{Module, Call, Storage, Event<T>},
 		Actors: actors::{Module, Call, Storage, Event<T>},
 		DataObjectTypeRegistry: data_object_type_registry::{Module, Call, Storage, Event<T>, Config<T>},
+		DataDirectory: data_directory::{Module, Call, Storage, Event<T>},
+		DataObjectStorageRegistry: data_object_storage_registry::{Module, Call, Storage, Event<T>, Config<T>},
+		DownloadSessions: downloads::{Module, Call, Storage, Event<T>, Config<T>},
+		ContentDirectory: content_directory::{Module, Call, Storage, Event<T>, Config<T>},
 	}
 );
 

+ 91 - 37
src/membership/members.rs

@@ -1,27 +1,53 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use rstd::prelude::*;
+use crate::governance::{BalanceOf, GovernanceCurrency};
+use crate::traits::{Members, Roles};
 use parity_codec::Codec;
-use parity_codec_derive::{Encode, Decode};
-use srml_support::{StorageMap, StorageValue, dispatch, decl_module, decl_storage, decl_event, ensure, Parameter};
-use srml_support::traits::{Currency};
-use runtime_primitives::traits::{Zero, SimpleArithmetic, As, Member, MaybeSerializeDebug};
+use parity_codec_derive::{Decode, Encode};
+use rstd::prelude::*;
+use runtime_primitives::traits::{As, MaybeSerializeDebug, Member, SimpleArithmetic};
+use srml_support::traits::Currency;
+use srml_support::{
+    decl_event, decl_module, decl_storage, dispatch, ensure, Parameter, StorageMap, StorageValue,
+};
 use system::{self, ensure_signed};
-use crate::governance::{GovernanceCurrency, BalanceOf };
-use {timestamp};
-use crate::traits::{Members, Roles};
+use timestamp;
 
 pub trait Trait: system::Trait + GovernanceCurrency + timestamp::Trait {
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 
-    type MemberId: Parameter + Member + SimpleArithmetic + Codec + Default + Copy
-        + As<usize> + As<u64> + MaybeSerializeDebug + PartialEq;
-
-    type PaidTermId: Parameter + Member + SimpleArithmetic + Codec + Default + Copy
-        + As<usize> + As<u64> + MaybeSerializeDebug + PartialEq;
-
-    type SubscriptionId: Parameter + Member + SimpleArithmetic + Codec + Default + Copy
-        + As<usize> + As<u64> + MaybeSerializeDebug + PartialEq;
+    type MemberId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    type PaidTermId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    type SubscriptionId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
 
     type Roles: Roles<Self>;
 }
@@ -84,7 +110,7 @@ pub struct PaidMembershipTerms<T: Trait> {
     /// Quantity of native tokens which must be provably burned
     pub fee: BalanceOf<T>,
     /// String of capped length describing human readable conditions which are being agreed upon
-    pub text: Vec<u8>
+    pub text: Vec<u8>,
 }
 
 impl<T: Trait> Default for PaidMembershipTerms<T> {
@@ -92,7 +118,7 @@ impl<T: Trait> Default for PaidMembershipTerms<T> {
         PaidMembershipTerms {
             id: T::PaidTermId::sa(DEFAULT_PAID_TERM_ID),
             fee: BalanceOf::<T>::sa(DEFAULT_PAID_TERM_FEE),
-            text: DEFAULT_PAID_TERM_TEXT.as_bytes().to_vec()
+            text: DEFAULT_PAID_TERM_TEXT.as_bytes().to_vec(),
         }
     }
 }
@@ -179,11 +205,9 @@ impl<T: Trait> Members<T> for Module<T> {
     type Id = T::MemberId;
 
     fn is_active_member(who: &T::AccountId) -> bool {
-        match Self::ensure_is_member(who)
-            .and_then(|member_id| Self::ensure_profile(member_id))
-        {
+        match Self::ensure_is_member(who).and_then(|member_id| Self::ensure_profile(member_id)) {
             Ok(profile) => !profile.suspended,
-            Err(err) => false
+            Err(_err) => false,
         }
     }
 
@@ -310,18 +334,25 @@ decl_module! {
 
 impl<T: Trait> Module<T> {
     fn ensure_not_member(who: &T::AccountId) -> dispatch::Result {
-        ensure!(!<MemberIdByAccountId<T>>::exists(who), "account already associated with a membership");
+        ensure!(
+            !<MemberIdByAccountId<T>>::exists(who),
+            "account already associated with a membership"
+        );
         Ok(())
     }
 
     pub fn ensure_is_member(who: &T::AccountId) -> Result<T::MemberId, &'static str> {
-        let member_id = Self::member_id_by_account_id(who).ok_or("no member id found for accountid")?;
+        let member_id =
+            Self::member_id_by_account_id(who).ok_or("no member id found for accountid")?;
         Ok(member_id)
     }
 
     fn ensure_is_member_primary_account(who: T::AccountId) -> Result<T::MemberId, &'static str> {
         let member_id = Self::ensure_is_member(&who)?;
-        ensure!(Self::account_id_by_member_id(member_id) == who, "not primary account");
+        ensure!(
+            Self::account_id_by_member_id(member_id) == who,
+            "not primary account"
+        );
         Ok(member_id)
     }
 
@@ -330,21 +361,33 @@ impl<T: Trait> Module<T> {
         Ok(profile)
     }
 
-    fn ensure_active_terms_id(terms_id: T::PaidTermId) -> Result<PaidMembershipTerms<T>, &'static str> {
+    fn ensure_active_terms_id(
+        terms_id: T::PaidTermId,
+    ) -> Result<PaidMembershipTerms<T>, &'static str> {
         let active_terms = Self::active_paid_membership_terms();
-        ensure!(active_terms.iter().any(|&id| id == terms_id), "paid terms id not active");
-        let terms = Self::paid_membership_terms_by_id(terms_id).ok_or("paid membership term id does not exist")?;
+        ensure!(
+            active_terms.iter().any(|&id| id == terms_id),
+            "paid terms id not active"
+        );
+        let terms = Self::paid_membership_terms_by_id(terms_id)
+            .ok_or("paid membership term id does not exist")?;
         Ok(terms)
     }
 
-    fn ensure_unique_handle(handle: &Vec<u8> ) -> dispatch::Result {
+    fn ensure_unique_handle(handle: &Vec<u8>) -> dispatch::Result {
         ensure!(!<Handles<T>>::exists(handle), "handle already registered");
         Ok(())
     }
 
     fn validate_handle(handle: &Vec<u8>) -> dispatch::Result {
-        ensure!(handle.len() >= Self::min_handle_length() as usize, "handle too short");
-        ensure!(handle.len() <= Self::max_handle_length() as usize, "handle too long");
+        ensure!(
+            handle.len() >= Self::min_handle_length() as usize,
+            "handle too short"
+        );
+        ensure!(
+            handle.len() <= Self::max_handle_length() as usize,
+            "handle too long"
+        );
         Ok(())
     }
 
@@ -355,14 +398,19 @@ impl<T: Trait> Module<T> {
     }
 
     fn validate_avatar(uri: &Vec<u8>) -> dispatch::Result {
-        ensure!(uri.len() <= Self::max_avatar_uri_length() as usize, "avatar uri too long");
+        ensure!(
+            uri.len() <= Self::max_avatar_uri_length() as usize,
+            "avatar uri too long"
+        );
         Ok(())
     }
 
     /// Basic user input validation
     fn check_user_registration_info(user_info: UserInfo) -> Result<CheckedUserInfo, &'static str> {
         // Handle is required during registration
-        let handle = user_info.handle.ok_or("handle must be provided during registration")?;
+        let handle = user_info
+            .handle
+            .ok_or("handle must be provided during registration")?;
         Self::validate_handle(&handle)?;
 
         let about = Self::validate_text(&user_info.about.unwrap_or_default());
@@ -377,7 +425,11 @@ impl<T: Trait> Module<T> {
     }
 
     // Mutating methods
-    fn insert_member(who: &T::AccountId, user_info: &CheckedUserInfo, entry_method: EntryMethod<T>) -> T::MemberId {
+    fn insert_member(
+        who: &T::AccountId,
+        user_info: &CheckedUserInfo,
+        entry_method: EntryMethod<T>,
+    ) -> T::MemberId {
         let new_member_id = Self::next_member_id();
 
         let profile: Profile<T> = Profile {
@@ -396,12 +448,14 @@ impl<T: Trait> Module<T> {
         <AccountIdByMemberId<T>>::insert(new_member_id, who.clone());
         <MemberProfile<T>>::insert(new_member_id, profile);
         <Handles<T>>::insert(user_info.handle.clone(), new_member_id);
-        <NextMemberId<T>>::mutate(|n| { *n += T::MemberId::sa(1); });
+        <NextMemberId<T>>::mutate(|n| {
+            *n += T::MemberId::sa(1);
+        });
 
         new_member_id
     }
 
-    fn _change_member_about_text (id: T::MemberId, text: &Vec<u8>) -> dispatch::Result {
+    fn _change_member_about_text(id: T::MemberId, text: &Vec<u8>) -> dispatch::Result {
         let mut profile = Self::ensure_profile(id)?;
         let text = Self::validate_text(text);
         profile.about = text;
@@ -430,4 +484,4 @@ impl<T: Trait> Module<T> {
         <MemberProfile<T>>::insert(id, profile);
         Ok(())
     }
-}
+}

+ 36 - 29
src/membership/mock.rs

@@ -1,15 +1,15 @@
 #![cfg(test)]
 
-pub use srml_support::traits::{Currency};
-pub use crate::governance::{GovernanceCurrency};
-pub use super::{members};
+pub use srml_support::traits::Currency;
+pub use super::members;
+pub use crate::governance::GovernanceCurrency;
 pub use system;
 
-pub use primitives::{H256, Blake2Hasher};
+pub use primitives::{Blake2Hasher, H256};
 pub use runtime_primitives::{
+    testing::{Digest, DigestItem, Header, UintAuthorityId},
+    traits::{BlakeTwo256, IdentityLookup, OnFinalise},
     BuildStorage,
-    traits::{BlakeTwo256, OnFinalise, IdentityLookup},
-    testing::{Digest, DigestItem, Header, UintAuthorityId}
 };
 
 use srml_support::impl_outer_origin;
@@ -79,39 +79,46 @@ impl members::Trait for Test {
 }
 
 pub struct ExtBuilder {
-	first_member_id: u32,
-	default_paid_membership_fee: u32,
+    first_member_id: u32,
+    default_paid_membership_fee: u32,
 }
 impl Default for ExtBuilder {
-	fn default() -> Self {
-		Self {
-			first_member_id: 1,
-			default_paid_membership_fee: 100,
-		}
-	}
+    fn default() -> Self {
+        Self {
+            first_member_id: 1,
+            default_paid_membership_fee: 100,
+        }
+    }
 }
 
 impl ExtBuilder {
-	pub fn first_member_id(mut self, first_member_id: u32) -> Self {
-		self.first_member_id = first_member_id;
-		self
-	}
-	pub fn default_paid_membership_fee(mut self, default_paid_membership_fee: u32) -> Self {
-		self.default_paid_membership_fee = default_paid_membership_fee;
-		self
-	}
+    pub fn first_member_id(mut self, first_member_id: u32) -> Self {
+        self.first_member_id = first_member_id;
+        self
+    }
+    pub fn default_paid_membership_fee(mut self, default_paid_membership_fee: u32) -> Self {
+        self.default_paid_membership_fee = default_paid_membership_fee;
+        self
+    }
     pub fn build(self) -> runtime_io::TestExternalities<Blake2Hasher> {
-        let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
-
-        t.extend(members::GenesisConfig::<Test>{
-            first_member_id: self.first_member_id,
-            default_paid_membership_fee: self.default_paid_membership_fee,
-        }.build_storage().unwrap().0);
+        let mut t = system::GenesisConfig::<Test>::default()
+            .build_storage()
+            .unwrap()
+            .0;
+
+        t.extend(
+            members::GenesisConfig::<Test> {
+                first_member_id: self.first_member_id,
+                default_paid_membership_fee: self.default_paid_membership_fee,
+            }
+            .build_storage()
+            .unwrap()
+            .0,
+        );
 
         t.into()
     }
 }
 
-pub type System = system::Module<Test>;
 pub type Balances = balances::Module<Test>;
 pub type Members = members::Module<Test>;

+ 1 - 1
src/membership/mod.rs

@@ -3,4 +3,4 @@
 pub mod members;
 
 mod mock;
-mod tests;
+mod tests;

+ 166 - 106
src/membership/tests.rs

@@ -1,6 +1,5 @@
 #![cfg(test)]
 
-use super::*;
 use super::mock::*;
 
 use runtime_io::with_externalities;
@@ -8,15 +7,22 @@ use srml_support::*;
 
 fn assert_ok_unwrap<T>(value: Option<T>, err: &'static str) -> T {
     match value {
-        None => { assert!(false, err); value.unwrap() },
-        Some(v) => v
+        None => {
+            assert!(false, err);
+            value.unwrap()
+        }
+        Some(v) => v,
     }
 }
 
 fn get_alice_info() -> members::UserInfo {
     members::UserInfo {
         handle: Some(String::from("alice").as_bytes().to_vec()),
-        avatar_uri: Some(String::from("http://avatar-url.com/alice").as_bytes().to_vec()),
+        avatar_uri: Some(
+            String::from("http://avatar-url.com/alice")
+                .as_bytes()
+                .to_vec(),
+        ),
         about: Some(String::from("my name is alice").as_bytes().to_vec()),
     }
 }
@@ -24,7 +30,11 @@ fn get_alice_info() -> members::UserInfo {
 fn get_bob_info() -> members::UserInfo {
     members::UserInfo {
         handle: Some(String::from("bobby").as_bytes().to_vec()),
-        avatar_uri: Some(String::from("http://avatar-url.com/bob").as_bytes().to_vec()),
+        avatar_uri: Some(
+            String::from("http://avatar-url.com/bob")
+                .as_bytes()
+                .to_vec(),
+        ),
         about: Some(String::from("my name is bob").as_bytes().to_vec()),
     }
 }
@@ -33,7 +43,11 @@ const ALICE_ACCOUNT_ID: u64 = 1;
 const DEFAULT_TERMS_ID: u32 = 0;
 
 fn buy_default_membership_as_alice() -> dispatch::Result {
-    Members::buy_membership(Origin::signed(ALICE_ACCOUNT_ID), DEFAULT_TERMS_ID, get_alice_info())
+    Members::buy_membership(
+        Origin::signed(ALICE_ACCOUNT_ID),
+        DEFAULT_TERMS_ID,
+        get_alice_info(),
+    )
 }
 
 fn set_alice_free_balance(balance: u32) {
@@ -45,18 +59,24 @@ fn initial_state() {
     const DEFAULT_FEE: u32 = 500;
     const DEFAULT_FIRST_ID: u32 = 1000;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE)
-        .first_member_id(DEFAULT_FIRST_ID).build(), ||
-    {
-        assert_eq!(Members::first_member_id(), DEFAULT_FIRST_ID);
-        assert_eq!(Members::next_member_id(), DEFAULT_FIRST_ID);
-
-        let default_terms = assert_ok_unwrap(Members::paid_membership_terms_by_id(DEFAULT_TERMS_ID), "default terms not initialized");
-
-        assert_eq!(default_terms.id, DEFAULT_TERMS_ID);
-        assert_eq!(default_terms.fee, DEFAULT_FEE);
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .first_member_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            assert_eq!(Members::first_member_id(), DEFAULT_FIRST_ID);
+            assert_eq!(Members::next_member_id(), DEFAULT_FIRST_ID);
+
+            let default_terms = assert_ok_unwrap(
+                Members::paid_membership_terms_by_id(DEFAULT_TERMS_ID),
+                "default terms not initialized",
+            );
+
+            assert_eq!(default_terms.id, DEFAULT_TERMS_ID);
+            assert_eq!(default_terms.fee, DEFAULT_FEE);
+        },
+    );
 }
 
 #[test]
@@ -65,56 +85,70 @@ fn buy_membership() {
     const DEFAULT_FIRST_ID: u32 = 1000;
     const SURPLUS_BALANCE: u32 = 500;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE)
-        .first_member_id(DEFAULT_FIRST_ID).build(), ||
-    {
-        let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
-        set_alice_free_balance(initial_balance);
-
-        assert_ok!(buy_default_membership_as_alice());
-
-        let member_id = assert_ok_unwrap(Members::member_id_by_account_id(&ALICE_ACCOUNT_ID), "member id not assigned");
-
-        let profile = assert_ok_unwrap(Members::member_profile(&member_id), "member profile not created");
-
-        assert_eq!(Some(profile.handle), get_alice_info().handle);
-        assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
-        assert_eq!(Some(profile.about), get_alice_info().about);
-
-        assert_eq!(Balances::free_balance(&ALICE_ACCOUNT_ID), SURPLUS_BALANCE);
-
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .first_member_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
+            set_alice_free_balance(initial_balance);
+
+            assert_ok!(buy_default_membership_as_alice());
+
+            let member_id = assert_ok_unwrap(
+                Members::member_id_by_account_id(&ALICE_ACCOUNT_ID),
+                "member id not assigned",
+            );
+
+            let profile = assert_ok_unwrap(
+                Members::member_profile(&member_id),
+                "member profile not created",
+            );
+
+            assert_eq!(Some(profile.handle), get_alice_info().handle);
+            assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
+            assert_eq!(Some(profile.about), get_alice_info().about);
+
+            assert_eq!(Balances::free_balance(&ALICE_ACCOUNT_ID), SURPLUS_BALANCE);
+        },
+    );
 }
 
 #[test]
 fn buy_membership_fails_without_enough_balance() {
     const DEFAULT_FEE: u32 = 500;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE).build(), ||
-    {
-        let initial_balance = DEFAULT_FEE - 1;
-        set_alice_free_balance(initial_balance);
-
-        assert!(buy_default_membership_as_alice().is_err());
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .build(),
+        || {
+            let initial_balance = DEFAULT_FEE - 1;
+            set_alice_free_balance(initial_balance);
+
+            assert!(buy_default_membership_as_alice().is_err());
+        },
+    );
 }
 
 #[test]
 fn new_memberships_allowed_flag() {
     const DEFAULT_FEE: u32 = 500;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE).build(), ||
-    {
-        let initial_balance = DEFAULT_FEE + 1;
-        set_alice_free_balance(initial_balance);
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .build(),
+        || {
+            let initial_balance = DEFAULT_FEE + 1;
+            set_alice_free_balance(initial_balance);
 
-        <members::NewMembershipsAllowed<Test>>::put(false);
+            <members::NewMembershipsAllowed<Test>>::put(false);
 
-        assert!(buy_default_membership_as_alice().is_err());
-    });
+            assert!(buy_default_membership_as_alice().is_err());
+        },
+    );
 }
 
 #[test]
@@ -122,19 +156,21 @@ fn account_cannot_create_multiple_memberships() {
     const DEFAULT_FEE: u32 = 500;
     const SURPLUS_BALANCE: u32 = 500;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE).build(), ||
-    {
-        let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
-        set_alice_free_balance(initial_balance);
-
-        // First time it works
-        assert_ok!(buy_default_membership_as_alice());
-
-        // second attempt should fail
-        assert!(buy_default_membership_as_alice().is_err());
-
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .build(),
+        || {
+            let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
+            set_alice_free_balance(initial_balance);
+
+            // First time it works
+            assert_ok!(buy_default_membership_as_alice());
+
+            // second attempt should fail
+            assert!(buy_default_membership_as_alice().is_err());
+        },
+    );
 }
 
 #[test]
@@ -142,19 +178,21 @@ fn unique_handles() {
     const DEFAULT_FEE: u32 = 500;
     const SURPLUS_BALANCE: u32 = 500;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE).build(), ||
-    {
-        let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
-        set_alice_free_balance(initial_balance);
-
-        // alice's handle already taken
-        <members::Handles<Test>>::insert(get_alice_info().handle.unwrap(), 1);
-
-        // should not be allowed to buy membership with that handle
-        assert!(buy_default_membership_as_alice().is_err());
-
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .build(),
+        || {
+            let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
+            set_alice_free_balance(initial_balance);
+
+            // alice's handle already taken
+            <members::Handles<Test>>::insert(get_alice_info().handle.unwrap(), 1);
+
+            // should not be allowed to buy membership with that handle
+            assert!(buy_default_membership_as_alice().is_err());
+        },
+    );
 }
 
 #[test]
@@ -162,44 +200,66 @@ fn update_profile() {
     const DEFAULT_FEE: u32 = 500;
     const SURPLUS_BALANCE: u32 = 500;
 
-    with_externalities(&mut ExtBuilder::default()
-        .default_paid_membership_fee(DEFAULT_FEE).build(), ||
-    {
-        let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
-        set_alice_free_balance(initial_balance);
-
-        assert_ok!(buy_default_membership_as_alice());
-
-        assert_ok!(Members::update_profile(Origin::signed(ALICE_ACCOUNT_ID), get_bob_info()));
-
-        let member_id = assert_ok_unwrap(Members::member_id_by_account_id(&ALICE_ACCOUNT_ID), "member id not assigned");
-
-        let profile = assert_ok_unwrap(Members::member_profile(&member_id), "member profile created");
-
-        assert_eq!(Some(profile.handle), get_bob_info().handle);
-        assert_eq!(Some(profile.avatar_uri), get_bob_info().avatar_uri);
-        assert_eq!(Some(profile.about), get_bob_info().about);
-
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .default_paid_membership_fee(DEFAULT_FEE)
+            .build(),
+        || {
+            let initial_balance = DEFAULT_FEE + SURPLUS_BALANCE;
+            set_alice_free_balance(initial_balance);
+
+            assert_ok!(buy_default_membership_as_alice());
+
+            assert_ok!(Members::update_profile(
+                Origin::signed(ALICE_ACCOUNT_ID),
+                get_bob_info()
+            ));
+
+            let member_id = assert_ok_unwrap(
+                Members::member_id_by_account_id(&ALICE_ACCOUNT_ID),
+                "member id not assigned",
+            );
+
+            let profile = assert_ok_unwrap(
+                Members::member_profile(&member_id),
+                "member profile created",
+            );
+
+            assert_eq!(Some(profile.handle), get_bob_info().handle);
+            assert_eq!(Some(profile.avatar_uri), get_bob_info().avatar_uri);
+            assert_eq!(Some(profile.about), get_bob_info().about);
+        },
+    );
 }
 
 #[test]
 fn add_screened_member() {
-    with_externalities(&mut ExtBuilder::default().build(), ||
-    {
+    with_externalities(&mut ExtBuilder::default().build(), || {
         let screening_authority = 5;
         <members::ScreeningAuthority<Test>>::put(&screening_authority);
 
-        assert_ok!(Members::add_screened_member(Origin::signed(screening_authority), ALICE_ACCOUNT_ID, get_alice_info()));
+        assert_ok!(Members::add_screened_member(
+            Origin::signed(screening_authority),
+            ALICE_ACCOUNT_ID,
+            get_alice_info()
+        ));
 
-        let member_id = assert_ok_unwrap(Members::member_id_by_account_id(&ALICE_ACCOUNT_ID), "member id not assigned");
+        let member_id = assert_ok_unwrap(
+            Members::member_id_by_account_id(&ALICE_ACCOUNT_ID),
+            "member id not assigned",
+        );
 
-        let profile = assert_ok_unwrap(Members::member_profile(&member_id), "member profile created");
+        let profile = assert_ok_unwrap(
+            Members::member_profile(&member_id),
+            "member profile created",
+        );
 
         assert_eq!(Some(profile.handle), get_alice_info().handle);
         assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
         assert_eq!(Some(profile.about), get_alice_info().about);
-        assert_eq!(members::EntryMethod::Screening(screening_authority), profile.entry);
-
+        assert_eq!(
+            members::EntryMethod::Screening(screening_authority),
+            profile.entry
+        );
     });
 }

+ 5 - 5
src/memo.rs

@@ -1,11 +1,11 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use srml_support::{StorageMap, dispatch::Result, decl_module, decl_storage, decl_event, ensure};
-use srml_support::traits::{Currency};
-use runtime_primitives::traits::{Zero};
-use system::{self, ensure_signed};
-use rstd::prelude::*;
 use crate::governance::GovernanceCurrency;
+use rstd::prelude::*;
+use runtime_primitives::traits::Zero;
+use srml_support::traits::Currency;
+use srml_support::{decl_event, decl_module, decl_storage, ensure, StorageMap};
+use system::{self, ensure_signed};
 
 pub trait Trait: system::Trait + GovernanceCurrency {
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;

+ 30 - 22
src/migration.rs

@@ -1,14 +1,14 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use srml_support::{StorageValue, dispatch::Result, decl_module, decl_storage, decl_event, ensure};
-use system;
-use rstd::prelude::*;
-use runtime_io::print;
-use crate::{VERSION};
+use crate::governance::BalanceOf;
 use crate::membership::members;
 use crate::roles::actors;
-use crate::governance::{GovernanceCurrency, BalanceOf };
-use runtime_primitives::traits::{Zero, Bounded, SimpleArithmetic, As};
+use crate::VERSION;
+use rstd::prelude::*;
+use runtime_io::print;
+use runtime_primitives::traits::As;
+use srml_support::{decl_event, decl_module, decl_storage, StorageValue};
+use system;
 
 // When preparing a new major runtime release version bump this value to match it and update
 // the initialization code in runtime_initialization(). Because of the way substrate runs runtime code
@@ -19,34 +19,42 @@ const MIGRATION_FOR_SPEC_VERSION: u32 = 5;
 
 impl<T: Trait> Module<T> {
     fn runtime_initialization() {
-        if VERSION.spec_version != MIGRATION_FOR_SPEC_VERSION { return }
+        if VERSION.spec_version != MIGRATION_FOR_SPEC_VERSION {
+            return;
+        }
 
         print("running runtime initializers");
 
         <members::Module<T>>::initialize_storage();
 
         // Initialize Storage provider role parameters
-        <actors::Module<T>>::set_role_parameters(actors::Role::Storage, actors::RoleParameters {
-            min_stake: BalanceOf::<T>::sa(3000),
-            max_actors: 10,
-            reward: BalanceOf::<T>::sa(10),
-            reward_period: T::BlockNumber::sa(600),
-            unbonding_period: T::BlockNumber::sa(600),
-            entry_request_fee: BalanceOf::<T>::sa(50),
+        <actors::Module<T>>::set_role_parameters(
+            actors::Role::Storage,
+            actors::RoleParameters {
+                min_stake: BalanceOf::<T>::sa(3000),
+                max_actors: 10,
+                reward: BalanceOf::<T>::sa(10),
+                reward_period: T::BlockNumber::sa(600),
+                unbonding_period: T::BlockNumber::sa(600),
+                entry_request_fee: BalanceOf::<T>::sa(50),
 
-            // not currently used
-            min_actors: 5,
-            bonding_period: T::BlockNumber::sa(600),
-            min_service_period: T::BlockNumber::sa(600),
-            startup_grace_period: T::BlockNumber::sa(600),
-        });
+                // not currently used
+                min_actors: 5,
+                bonding_period: T::BlockNumber::sa(600),
+                min_service_period: T::BlockNumber::sa(600),
+                startup_grace_period: T::BlockNumber::sa(600),
+            },
+        );
         <actors::Module<T>>::set_available_roles(vec![actors::Role::Storage]);
 
         // ...
         // add initialization of other modules introduced in this runtime
         // ...
 
-        Self::deposit_event(RawEvent::Migrated(<system::Module<T>>::block_number(), VERSION.spec_version));
+        Self::deposit_event(RawEvent::Migrated(
+            <system::Module<T>>::block_number(),
+            VERSION.spec_version,
+        ));
     }
 }
 

+ 8 - 5
src/roles/actors.rs

@@ -3,10 +3,8 @@
 use crate::governance::{BalanceOf, GovernanceCurrency};
 use parity_codec_derive::{Decode, Encode};
 use rstd::prelude::*;
-use runtime_primitives::traits::{
-    As, Bounded, MaybeDebug, Zero,
-};
-use srml_support::traits::{Currency};
+use srml_support::traits::Currency;
+use runtime_primitives::traits::{As, Bounded, MaybeDebug, Zero};
 use srml_support::{
     decl_event, decl_module, decl_storage, dispatch, ensure, StorageMap, StorageValue,
 };
@@ -72,7 +70,12 @@ pub trait Trait: system::Trait + GovernanceCurrency + MaybeDebug {
 
 pub type MemberId<T> = <<T as Trait>::Members as Members<T>>::Id;
 // actor account, memberid, role, expires
-pub type Request<T> = (<T as system::Trait>::AccountId, MemberId<T>, Role, <T as system::Trait>::BlockNumber);
+pub type Request<T> = (
+    <T as system::Trait>::AccountId,
+    MemberId<T>,
+    Role,
+    <T as system::Trait>::BlockNumber,
+);
 pub type Requests<T> = Vec<Request<T>>;
 
 pub const REQUEST_LIFETIME: u64 = 300;

+ 201 - 0
src/storage/content_directory.rs

@@ -0,0 +1,201 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use crate::storage::data_object_type_registry::Trait as DOTRTrait;
+use crate::traits::Members;
+use parity_codec::Codec;
+use parity_codec_derive::{Decode, Encode};
+use rstd::prelude::*;
+use runtime_primitives::traits::{As, MaybeDebug, MaybeSerializeDebug, Member, SimpleArithmetic};
+use srml_support::{
+    decl_event, decl_module, decl_storage, ensure, Parameter, StorageMap, StorageValue,
+};
+use system::{self, ensure_signed};
+
+pub trait Trait: timestamp::Trait + system::Trait + DOTRTrait + MaybeDebug {
+    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
+    type MetadataId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    // Schema ID should be defined in a different Trait in future
+    type SchemaId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    type Members: Members<Self>;
+}
+
+static MSG_CREATOR_MUST_BE_MEMBER: &str = "Only active members may create content!";
+// TODO for future: static MSG_INVALID_SCHEMA_ID: &str = "The metadata schema is not known or invalid!";
+static MSG_METADATA_NOT_FOUND: &str = "Metadata with the given ID cannot be found!";
+static MSG_ONLY_OWNER_MAY_PUBLISH: &str = "Only metadata owners may publish their metadata!";
+
+const DEFAULT_FIRST_METADATA_ID: u64 = 1;
+
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub enum MetadataState {
+    Draft,
+    Published,
+}
+
+impl Default for MetadataState {
+    fn default() -> Self {
+        MetadataState::Draft
+    }
+}
+
+// Content metadata contains two fields: one is the numeric ID of the metadata
+// scheme to use, which is currently related to off-chain definitions ONLY.
+// The other is the serialized metadata.
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub struct ContentMetadata<T: Trait> {
+    pub schema: T::SchemaId,
+    pub metadata: Vec<u8>,
+    pub origin: T::AccountId,
+    pub state: MetadataState,
+}
+
+decl_event! {
+    pub enum Event<T> where
+        <T as Trait>::MetadataId
+    {
+        MetadataDraftCreated(MetadataId),
+        MetadataPublished(MetadataId),
+    }
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as ContentDirectory {
+        // Start at this value
+        pub FirstMetadataId get(first_metadata_id) config(first_metadata_id): T::MetadataId = T::MetadataId::sa(DEFAULT_FIRST_METADATA_ID);
+
+        // Increment
+        pub NextMetadataId get(next_metadata_id) build(|config: &GenesisConfig<T>| config.first_metadata_id): T::MetadataId = T::MetadataId::sa(DEFAULT_FIRST_METADATA_ID);
+
+        // Mapping of Data object types
+        pub MetadataMap get(metadata): map T::MetadataId => Option<ContentMetadata<T>>;
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+        fn deposit_event<T>() = default;
+
+        pub fn add_metadata(origin, schema: T::SchemaId, metadata: Vec<u8>)
+        {
+            // Origin has to be a member
+            let who = ensure_signed(origin)?;
+            ensure!(T::Members::is_active_member(&who), MSG_CREATOR_MUST_BE_MEMBER);
+
+            // TODO in future, we want the schema IDs to correspond to a schema
+            // registry, and validate the data. For now, we just allow the
+            // following schema IDs:
+            // 1 - Video (schema TBD, i.e. handled in UI only)
+            // 2 - Podcast (schema TBD, i.e. handled in UI only)
+            // Pseudocode
+            // if schema not in (1, 2) {
+            //     return Err(MSG_INVALID_SCHEMA_ID);
+            // }
+            //
+            // See https://github.com/Joystream/substrate-runtime-joystream/issues/20
+
+            // New and data
+            let new_id = Self::next_metadata_id();
+            let data: ContentMetadata<T> = ContentMetadata {
+                origin: who.clone(),
+                schema: schema,
+                metadata: metadata.clone(),
+                state: MetadataState::Draft,
+            };
+
+            // Store
+            <MetadataMap<T>>::insert(new_id, data);
+            <NextMetadataId<T>>::mutate(|n| { *n += T::MetadataId::sa(1); });
+
+            // Publish event
+            Self::deposit_event(RawEvent::MetadataDraftCreated(new_id));
+        }
+
+        pub fn publish_metadata(origin, id: T::MetadataId)
+        {
+            // Ensure signed account
+            let who = ensure_signed(origin)?;
+
+            // Try t find metadata
+            let mut data = Self::metadata(id).ok_or(MSG_METADATA_NOT_FOUND)?;
+
+            // Ensure it's the metadata creator who publishes
+            ensure!(data.origin == who, MSG_ONLY_OWNER_MAY_PUBLISH);
+
+            // Modify
+            data.state = MetadataState::Published;
+
+            // Store
+            <MetadataMap<T>>::insert(id, data);
+
+            // Publish event
+            Self::deposit_event(RawEvent::MetadataPublished(id));
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::storage::mock::*;
+
+    #[test]
+    fn add_metadata() {
+        with_default_mock_builder(|| {
+            let res =
+                TestContentDirectory::add_metadata(Origin::signed(1), 1, "foo".as_bytes().to_vec());
+            assert!(res.is_ok());
+        });
+    }
+
+    #[test]
+    fn publish_metadata() {
+        with_default_mock_builder(|| {
+            let res =
+                TestContentDirectory::add_metadata(Origin::signed(1), 1, "foo".as_bytes().to_vec());
+            assert!(res.is_ok());
+
+            // Grab ID from event
+            let metadata_id = match System::events().last().unwrap().event {
+                MetaEvent::content_directory(
+                    content_directory::RawEvent::MetadataDraftCreated(metadata_id),
+                ) => metadata_id,
+                _ => 0xdeadbeefu64, // invalid value, unlikely to match
+            };
+            assert_ne!(metadata_id, 0xdeadbeefu64);
+
+            // Publishing a bad ID should fail
+            let res = TestContentDirectory::publish_metadata(Origin::signed(1), metadata_id + 1);
+            assert!(res.is_err());
+
+            // Publishing should not work for non-owners
+            let res = TestContentDirectory::publish_metadata(Origin::signed(2), metadata_id);
+            assert!(res.is_err());
+
+            // For the owner, it should work however
+            let res = TestContentDirectory::publish_metadata(Origin::signed(1), metadata_id);
+            assert!(res.is_ok());
+        });
+    }
+}

+ 250 - 0
src/storage/data_directory.rs

@@ -0,0 +1,250 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use crate::storage::data_object_type_registry::Trait as DOTRTrait;
+use crate::traits::{ContentIdExists, IsActiveDataObjectType, Members};
+use parity_codec::Codec;
+use parity_codec_derive::{Decode, Encode};
+use primitives::ed25519::Signature as Ed25519Signature;
+use rstd::prelude::*;
+use runtime_primitives::traits::{As, MaybeDebug, MaybeSerializeDebug, Member, SimpleArithmetic, Verify};
+use srml_support::{
+    decl_event, decl_module, decl_storage, dispatch, ensure, Parameter, StorageMap, StorageValue,
+};
+use system::{self, ensure_signed};
+
+pub type Ed25519AuthorityId = <Ed25519Signature as Verify>::Signer;
+
+pub trait Trait: timestamp::Trait + system::Trait + DOTRTrait + MaybeDebug {
+    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
+    type ContentId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    type Members: Members<Self>;
+    type IsActiveDataObjectType: IsActiveDataObjectType<Self>;
+}
+
+static MSG_CID_NOT_FOUND: &str = "Content with this ID not found.";
+static MSG_LIAISON_REQUIRED: &str = "Only the liaison for the content may modify its status.";
+static MSG_CREATOR_MUST_BE_MEMBER: &str = "Only active members may create content.";
+static MSG_DO_TYPE_MUST_BE_ACTIVE: &str =
+    "Cannot create content for inactive or missing data object type.";
+
+const DEFAULT_FIRST_CONTENT_ID: u64 = 1;
+
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub enum LiaisonJudgement {
+    Pending,
+    Rejected,
+    Accepted,
+}
+
+impl Default for LiaisonJudgement {
+    fn default() -> Self {
+        LiaisonJudgement::Pending
+    }
+}
+
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub struct DataObject<T: Trait> {
+    pub data_object_type: <T as DOTRTrait>::DataObjectTypeId,
+    pub signing_key: Option<Ed25519AuthorityId>,
+    pub size: u64,
+    pub added_at_block: T::BlockNumber,
+    pub added_at_time: T::Moment,
+    pub owner: T::AccountId,
+    pub liaison: T::AccountId,
+    pub liaison_judgement: LiaisonJudgement,
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as DataDirectory {
+        // Start at this value
+        pub FirstContentId get(first_content_id) config(first_content_id): T::ContentId = T::ContentId::sa(DEFAULT_FIRST_CONTENT_ID);
+
+        // Increment
+        pub NextContentId get(next_content_id) build(|config: &GenesisConfig<T>| config.first_content_id): T::ContentId = T::ContentId::sa(DEFAULT_FIRST_CONTENT_ID);
+
+        // Mapping of Content ID to Data Object
+        pub Contents get(contents): map T::ContentId => Option<DataObject<T>>;
+    }
+}
+
+decl_event! {
+    pub enum Event<T> where
+        <T as Trait>::ContentId,
+        <T as system::Trait>::AccountId
+    {
+        // The account is the Liaison that was selected
+        ContentAdded(ContentId, AccountId),
+
+        // The account is the liaison again - only they can reject or accept
+        ContentAccepted(ContentId, AccountId),
+        ContentRejected(ContentId, AccountId),
+    }
+}
+
+impl<T: Trait> ContentIdExists<T> for Module<T> {
+    fn has_content(which: &T::ContentId) -> bool {
+        Self::contents(which.clone()).is_some()
+    }
+
+    fn get_data_object(which: &T::ContentId) -> Result<DataObject<T>, &'static str> {
+        match Self::contents(which.clone()) {
+            None => Err(MSG_CID_NOT_FOUND),
+            Some(data) => Ok(data),
+        }
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+        fn deposit_event<T>() = default;
+
+        pub fn add_content(origin, data_object_type_id: <T as DOTRTrait>::DataObjectTypeId,
+                           size: u64, signing_key: Option<Ed25519AuthorityId>) {
+            // Origin has to be a member
+            let who = ensure_signed(origin)?;
+            ensure!(T::Members::is_active_member(&who), MSG_CREATOR_MUST_BE_MEMBER);
+
+            // Data object type has to be active
+            ensure!(T::IsActiveDataObjectType::is_active_data_object_type(&data_object_type_id), MSG_DO_TYPE_MUST_BE_ACTIVE);
+
+            // 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's create the entry then
+            let new_id = Self::next_content_id();
+            let data: DataObject<T> = DataObject {
+                data_object_type: data_object_type_id,
+                signing_key: signing_key,
+                size: size,
+                added_at_block: <system::Module<T>>::block_number(),
+                added_at_time: <timestamp::Module<T>>::now(),
+                owner: who,
+                liaison: liaison.clone(),
+                liaison_judgement: LiaisonJudgement::Pending,
+            };
+
+            // If we've constructed the data, we can store it and send an event.
+            <Contents<T>>::insert(new_id, data);
+            <NextContentId<T>>::mutate(|n| { *n += T::ContentId::sa(1); });
+
+            Self::deposit_event(RawEvent::ContentAdded(new_id, liaison));
+        }
+
+        // The LiaisonJudgement can be updated, but only by the liaison.
+        fn accept_content(origin, id: T::ContentId) {
+            let who = ensure_signed(origin)?;
+            Self::update_content_judgement(&who, id.clone(), LiaisonJudgement::Accepted)?;
+            Self::deposit_event(RawEvent::ContentAccepted(id, who));
+        }
+
+        fn reject_content(origin, id: T::ContentId) {
+            let who = ensure_signed(origin)?;
+            Self::update_content_judgement(&who, id.clone(), LiaisonJudgement::Rejected)?;
+            Self::deposit_event(RawEvent::ContentRejected(id, who));
+        }
+    }
+}
+
+impl<T: Trait> Module<T> {
+    fn update_content_judgement(
+        who: &T::AccountId,
+        id: T::ContentId,
+        judgement: LiaisonJudgement,
+    ) -> dispatch::Result {
+        // Find the data
+        let mut data = Self::contents(&id).ok_or(MSG_CID_NOT_FOUND)?;
+
+        // Make sure the liaison matches
+        ensure!(data.liaison == *who, MSG_LIAISON_REQUIRED);
+
+        // At this point we can update the data.
+        data.liaison_judgement = judgement;
+
+        // Update and send event.
+        <Contents<T>>::insert(id, data);
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::storage::mock::*;
+
+    #[test]
+    fn succeed_adding_content() {
+        with_default_mock_builder(|| {
+            // Register a content with 1234 bytes of type 1, which should be recognized.
+            let res = TestDataDirectory::add_content(Origin::signed(1), 1, 1234, None);
+            assert!(res.is_ok());
+        });
+    }
+
+    #[test]
+    fn accept_content_as_liaison() {
+        with_default_mock_builder(|| {
+            let res = TestDataDirectory::add_content(Origin::signed(1), 1, 1234, None);
+            assert!(res.is_ok());
+
+            // An appropriate event should have been fired.
+            let (content_id, liaison) = match System::events().last().unwrap().event {
+                MetaEvent::data_directory(data_directory::RawEvent::ContentAdded(
+                    content_id,
+                    liaison,
+                )) => (content_id, liaison),
+                _ => (0u64, 0xdeadbeefu64), // invalid value, unlikely to match
+            };
+            assert_ne!(liaison, 0xdeadbeefu64);
+
+            // Accepting content should not work with some random origin
+            let res = TestDataDirectory::accept_content(Origin::signed(42), content_id);
+            assert!(res.is_err());
+
+            // However, with the liaison as origin it should.
+            let res = TestDataDirectory::accept_content(Origin::signed(liaison), content_id);
+            assert!(res.is_ok());
+        });
+    }
+
+    #[test]
+    fn reject_content_as_liaison() {
+        with_default_mock_builder(|| {
+            let res = TestDataDirectory::add_content(Origin::signed(1), 1, 1234, None);
+            assert!(res.is_ok());
+
+            // An appropriate event should have been fired.
+            let (content_id, liaison) = match System::events().last().unwrap().event {
+                MetaEvent::data_directory(data_directory::RawEvent::ContentAdded(
+                    content_id,
+                    liaison,
+                )) => (content_id, liaison),
+                _ => (0u64, 0xdeadbeefu64), // invalid value, unlikely to match
+            };
+            assert_ne!(liaison, 0xdeadbeefu64);
+
+            // Rejecting content should not work with some random origin
+            let res = TestDataDirectory::reject_content(Origin::signed(42), content_id);
+            assert!(res.is_err());
+
+            // However, with the liaison as origin it should.
+            let res = TestDataDirectory::reject_content(Origin::signed(liaison), content_id);
+            assert!(res.is_ok());
+        });
+    }
+}

+ 250 - 0
src/storage/data_object_storage_registry.rs

@@ -0,0 +1,250 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use crate::storage::data_directory::Trait as DDTrait;
+use crate::traits::{ContentHasStorage, ContentIdExists, Members};
+use parity_codec::Codec;
+use parity_codec_derive::{Decode, Encode};
+use rstd::prelude::*;
+use runtime_primitives::traits::{As, MaybeDebug, MaybeSerializeDebug, Member, SimpleArithmetic};
+use srml_support::{
+    decl_event, decl_module, decl_storage, ensure, Parameter, StorageMap, StorageValue,
+};
+use system::{self, ensure_signed};
+
+pub trait Trait: timestamp::Trait + system::Trait + DDTrait + MaybeDebug {
+    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
+    type DataObjectStorageRelationshipId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    type Members: Members<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_CLAIM_READY: &str =
+    "Only the storage provider in a DOSR can decide whether they're ready.";
+
+const DEFAULT_FIRST_RELATIONSHIP_ID: u64 = 1;
+
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub struct DataObjectStorageRelationship<T: Trait> {
+    pub content_id: <T as DDTrait>::ContentId,
+    pub storage_provider: T::AccountId,
+    pub ready: bool,
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as DataObjectStorageRegistry {
+        // Start at this value
+        pub FirstRelationshipId get(first_relationship_id) config(first_relationship_id): T::DataObjectStorageRelationshipId = T::DataObjectStorageRelationshipId::sa(DEFAULT_FIRST_RELATIONSHIP_ID);
+
+        // Increment
+        pub NextRelationshipId get(next_relationship_id) build(|config: &GenesisConfig<T>| config.first_relationship_id): T::DataObjectStorageRelationshipId = T::DataObjectStorageRelationshipId::sa(DEFAULT_FIRST_RELATIONSHIP_ID);
+
+        // Mapping of Data object types
+        pub Relationships get(relationships): map T::DataObjectStorageRelationshipId => Option<DataObjectStorageRelationship<T>>;
+
+        // Keep a list of storage relationships per CID
+        pub RelationshipsByContentId get(relationships_by_content_id): map T::ContentId => Vec<T::DataObjectStorageRelationshipId>;
+    }
+}
+
+decl_event! {
+    pub enum Event<T> where
+        <T as DDTrait>::ContentId,
+        <T as Trait>::DataObjectStorageRelationshipId,
+        <T as system::Trait>::AccountId
+    {
+        DataObjectStorageRelationshipAdded(DataObjectStorageRelationshipId, ContentId, AccountId),
+        DataObjectStorageRelationshipReadyUpdated(DataObjectStorageRelationshipId, bool),
+    }
+}
+
+impl<T: Trait> ContentHasStorage<T> for Module<T> {
+    fn has_storage_provider(which: &T::ContentId) -> bool {
+        let dosr_list = Self::relationships_by_content_id(which);
+        return dosr_list.iter().any(|&dosr_id| {
+            let res = Self::relationships(dosr_id);
+            if res.is_none() {
+                return false;
+            }
+            let dosr = res.unwrap();
+            dosr.ready
+        });
+    }
+
+    fn is_ready_at_storage_provider(which: &T::ContentId, provider: &T::AccountId) -> bool {
+        let dosr_list = Self::relationships_by_content_id(which);
+        return dosr_list.iter().any(|&dosr_id| {
+            let res = Self::relationships(dosr_id);
+            if res.is_none() {
+                return false;
+            }
+            let dosr = res.unwrap();
+            dosr.storage_provider == *provider && dosr.ready
+        });
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+        fn deposit_event<T>() = default;
+
+        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);
+            // }
+
+            // Content ID must exist
+            ensure!(T::ContentIdExists::has_content(&cid), MSG_CID_NOT_FOUND);
+
+            // Create new ID, data.
+            let new_id = Self::next_relationship_id();
+            let dosr: DataObjectStorageRelationship<T> = DataObjectStorageRelationship {
+                content_id: cid.clone(),
+                storage_provider: who.clone(),
+                ready: false,
+            };
+
+            <Relationships<T>>::insert(new_id, dosr);
+            <NextRelationshipId<T>>::mutate(|n| { *n += T::DataObjectStorageRelationshipId::sa(1); });
+
+            // Also add the DOSR to the list of DOSRs for the CID. Uniqueness is guaranteed
+            // by the map, so we can just append the new_id to the list.
+            let mut dosr_list = Self::relationships_by_content_id(cid.clone());
+            dosr_list.push(new_id);
+            <RelationshipsByContentId<T>>::insert(cid.clone(), dosr_list);
+
+            // Emit event
+            Self::deposit_event(RawEvent::DataObjectStorageRelationshipAdded(new_id, cid, who));
+        }
+
+        // A storage provider may flip their own ready state, but nobody else.
+        pub fn set_relationship_ready(origin, id: T::DataObjectStorageRelationshipId) {
+            Self::toggle_dosr_ready(origin, id, true)?;
+        }
+
+        pub fn unset_relationship_ready(origin, id: T::DataObjectStorageRelationshipId) {
+            Self::toggle_dosr_ready(origin, id, false)?;
+        }
+    }
+}
+
+impl<T: Trait> Module<T> {
+    fn toggle_dosr_ready(
+        origin: T::Origin,
+        id: T::DataObjectStorageRelationshipId,
+        ready: bool,
+    ) -> Result<(), &'static str> {
+        // Origin has to be the storage provider mentioned in the DOSR
+        let who = ensure_signed(origin)?;
+
+        // For that, we need to fetch the identified DOSR
+        let mut dosr = Self::relationships(id).ok_or(MSG_DOSR_NOT_FOUND)?;
+        ensure!(
+            dosr.storage_provider == who,
+            MSG_ONLY_STORAGE_PROVIDER_MAY_CLAIM_READY
+        );
+
+        // Flip to ready
+        dosr.ready = ready;
+
+        // Update DOSR and fire event.
+        <Relationships<T>>::insert(id, dosr);
+        Self::deposit_event(RawEvent::DataObjectStorageRelationshipReadyUpdated(
+            id, true,
+        ));
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::storage::mock::*;
+
+    #[test]
+    fn initial_state() {
+        with_default_mock_builder(|| {
+            assert_eq!(
+                TestDataObjectStorageRegistry::first_relationship_id(),
+                TEST_FIRST_RELATIONSHIP_ID
+            );
+        });
+    }
+
+    #[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);
+            assert!(res.is_ok());
+        });
+    }
+
+    #[test]
+    fn test_fail_adding_relationship_with_bad_content() {
+        with_default_mock_builder(|| {
+            let res = TestDataObjectStorageRegistry::add_relationship(Origin::signed(1), 24);
+            assert!(res.is_err());
+        });
+    }
+
+    #[test]
+    fn test_toggle_ready() {
+        with_default_mock_builder(|| {
+            // Create a DOSR
+            let res = TestDataObjectStorageRegistry::add_relationship(Origin::signed(1), 42);
+            assert!(res.is_ok());
+
+            // Grab DOSR ID from event
+            let dosr_id = match System::events().last().unwrap().event {
+                MetaEvent::data_object_storage_registry(
+                    data_object_storage_registry::RawEvent::DataObjectStorageRelationshipAdded(
+                        dosr_id,
+                        _content_id,
+                        _account_id,
+                    ),
+                ) => dosr_id,
+                _ => 0xdeadbeefu64, // invalid value, unlikely to match
+            };
+            assert_ne!(dosr_id, 0xdeadbeefu64);
+
+            // Toggling from a different account should fail
+            let res =
+                TestDataObjectStorageRegistry::set_relationship_ready(Origin::signed(2), dosr_id);
+            assert!(res.is_err());
+
+            // Toggling with the wrong ID should fail.
+            let res = TestDataObjectStorageRegistry::set_relationship_ready(
+                Origin::signed(1),
+                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);
+            assert!(res.is_ok());
+            assert_eq!(System::events().last().unwrap().event,
+                MetaEvent::data_object_storage_registry(data_object_storage_registry::RawEvent::DataObjectStorageRelationshipReadyUpdated(
+                    dosr_id,
+                    true,
+                )));
+        });
+    }
+}

+ 181 - 29
src/storage/data_object_type_registry.rs

@@ -1,34 +1,37 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use rstd::prelude::*;
+use crate::traits;
 use parity_codec::Codec;
-use parity_codec_derive::{Encode, Decode};
-use srml_support::{StorageMap, StorageValue, decl_module, decl_storage, decl_event, ensure, Parameter};
-use runtime_primitives::traits::{SimpleArithmetic, As, Member, MaybeSerializeDebug, MaybeDebug};
+use parity_codec_derive::{Decode, Encode};
+use rstd::prelude::*;
+use runtime_primitives::traits::{As, MaybeDebug, MaybeSerializeDebug, Member, SimpleArithmetic};
+use srml_support::{decl_event, decl_module, decl_storage, Parameter, StorageMap, StorageValue};
 use system::{self, ensure_root};
-use crate::traits;
 
 pub trait Trait: system::Trait + MaybeDebug {
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 
-    type DataObjectTypeId: Parameter + Member + SimpleArithmetic + Codec + Default + Copy
-        + As<usize> + As<u64> + MaybeSerializeDebug + PartialEq;
+    type DataObjectTypeId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
 }
 
-
-static MSG_REQUIRE_NEW_DO_TYPE: &str = "New Data Object Type required; the provided one seems to be in use already!";
-static MSG_DO_TYPE_NOT_FOUND: &str = "Data Object Type with the given ID not found!";
-static MSG_REQUIRE_DO_TYPE_ID: &str = "Can only update Data Object Types that are already registered (with an ID)!";
+static MSG_DO_TYPE_NOT_FOUND: &str = "Data Object Type with the given ID not found.";
 
 const DEFAULT_FIRST_DATA_OBJECT_TYPE_ID: u64 = 1;
 
 #[derive(Clone, Encode, Decode, PartialEq)]
 #[cfg_attr(feature = "std", derive(Debug))]
-pub struct DataObjectType<T: Trait> {
-    pub id: Option<T::DataObjectTypeId>,
+pub struct DataObjectType {
     pub description: Vec<u8>,
     pub active: bool,
-
     // TODO in future releases
     // - maximum size
     // - replication factor
@@ -44,7 +47,7 @@ decl_storage! {
         pub NextDataObjectTypeId get(next_data_object_type_id) build(|config: &GenesisConfig<T>| config.first_data_object_type_id): T::DataObjectTypeId = T::DataObjectTypeId::sa(DEFAULT_FIRST_DATA_OBJECT_TYPE_ID);
 
         // Mapping of Data object types
-        pub DataObjectTypeMap get(data_object_type): map T::DataObjectTypeId => Option<DataObjectType<T>>;
+        pub DataObjectTypeMap get(data_object_type): map T::DataObjectTypeId => Option<DataObjectType>;
     }
 }
 
@@ -56,29 +59,24 @@ decl_event! {
     }
 }
 
-
-
 impl<T: Trait> traits::IsActiveDataObjectType<T> for Module<T> {
     fn is_active_data_object_type(which: &T::DataObjectTypeId) -> bool {
         match Self::ensure_data_object_type(*which) {
             Ok(do_type) => do_type.active,
-            Err(_err) => false
+            Err(_err) => false,
         }
     }
 }
 
-
 decl_module! {
     pub struct Module<T: Trait> for enum Call where origin: T::Origin {
         fn deposit_event<T>() = default;
 
-        pub fn register_data_object_type(origin, data_object_type: DataObjectType<T>) {
+        pub fn register_data_object_type(origin, data_object_type: DataObjectType) {
             ensure_root(origin)?;
-            ensure!(data_object_type.id.is_none(), MSG_REQUIRE_NEW_DO_TYPE);
 
             let new_do_type_id = Self::next_data_object_type_id();
-            let do_type: DataObjectType<T> = DataObjectType {
-                id: Some(new_do_type_id),
+            let do_type: DataObjectType = DataObjectType {
                 description: data_object_type.description.clone(),
                 active: data_object_type.active,
             };
@@ -89,11 +87,8 @@ decl_module! {
             Self::deposit_event(RawEvent::DataObjectTypeRegistered(new_do_type_id));
         }
 
-        pub fn update_data_object_type(origin, data_object_type: DataObjectType<T>) {
+        pub fn update_data_object_type(origin, id: T::DataObjectTypeId, data_object_type: DataObjectType) {
             ensure_root(origin)?;
-            ensure!(data_object_type.id.is_some(), MSG_REQUIRE_DO_TYPE_ID);
-
-            let id = data_object_type.id.unwrap();
             let mut do_type = Self::ensure_data_object_type(id)?;
 
             do_type.description = data_object_type.description.clone();
@@ -132,8 +127,165 @@ decl_module! {
     }
 }
 
-impl <T: Trait> Module<T> {
-    fn ensure_data_object_type(id: T::DataObjectTypeId) -> Result<DataObjectType<T>, &'static str> {
+impl<T: Trait> Module<T> {
+    fn ensure_data_object_type(id: T::DataObjectTypeId) -> Result<DataObjectType, &'static str> {
         return Self::data_object_type(&id).ok_or(MSG_DO_TYPE_NOT_FOUND);
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::storage::mock::*;
+
+    use system::{self, EventRecord, Phase};
+
+    #[test]
+    fn initial_state() {
+        with_default_mock_builder(|| {
+            assert_eq!(
+                TestDataObjectTypeRegistry::first_data_object_type_id(),
+                TEST_FIRST_DATA_OBJECT_TYPE_ID
+            );
+        });
+    }
+
+    #[test]
+    fn fail_register_without_root() {
+        with_default_mock_builder(|| {
+            let data: TestDataObjectType = TestDataObjectType {
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let res =
+                TestDataObjectTypeRegistry::register_data_object_type(Origin::signed(1), data);
+            assert!(res.is_err());
+        });
+    }
+
+    #[test]
+    fn succeed_register_as_root() {
+        with_default_mock_builder(|| {
+            let data: TestDataObjectType = TestDataObjectType {
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
+            assert!(res.is_ok());
+        });
+    }
+
+    #[test]
+    fn update_existing() {
+        with_default_mock_builder(|| {
+            // First register a type
+            let data: TestDataObjectType = TestDataObjectType {
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let id_res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
+            assert!(id_res.is_ok());
+
+            let dot_id = match System::events().last().unwrap().event {
+                MetaEvent::data_object_type_registry(
+                    data_object_type_registry::RawEvent::DataObjectTypeRegistered(dot_id),
+                ) => dot_id,
+                _ => 0xdeadbeefu64, // unlikely value
+            };
+            assert_ne!(dot_id, 0xdeadbeefu64);
+
+            // Now update it with new data - we need the ID to be the same as in
+            // returned by the previous call. First, though, try and fail with a bad ID
+            let updated1: TestDataObjectType = TestDataObjectType {
+                description: "bar".as_bytes().to_vec(),
+                active: false,
+            };
+            let res = TestDataObjectTypeRegistry::update_data_object_type(
+                Origin::ROOT,
+                dot_id + 1,
+                updated1,
+            );
+            assert!(res.is_err());
+
+            // Finally with an existing ID, it should work.
+            let updated3: TestDataObjectType = TestDataObjectType {
+                description: "bar".as_bytes().to_vec(),
+                active: false,
+            };
+            let res =
+                TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, dot_id, updated3);
+            assert!(res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeUpdated(dot_id)
+                    ),
+                }
+            );
+        });
+    }
+
+    #[test]
+    fn activate_existing() {
+        with_default_mock_builder(|| {
+            // First register a type
+            let data: TestDataObjectType = TestDataObjectType {
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let id_res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
+            assert!(id_res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeRegistered(
+                            TEST_FIRST_DATA_OBJECT_TYPE_ID
+                        )
+                    ),
+                }
+            );
+
+            // Retrieve, and ensure it's not active.
+            let data = TestDataObjectTypeRegistry::data_object_type(TEST_FIRST_DATA_OBJECT_TYPE_ID);
+            assert!(data.is_some());
+            assert!(!data.unwrap().active);
+
+            // Now activate the data object type
+            let res = TestDataObjectTypeRegistry::activate_data_object_type(
+                Origin::ROOT,
+                TEST_FIRST_DATA_OBJECT_TYPE_ID,
+            );
+            assert!(res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeUpdated(
+                            TEST_FIRST_DATA_OBJECT_TYPE_ID
+                        )
+                    ),
+                }
+            );
+
+            // Ensure that the item is actually activated.
+            let data = TestDataObjectTypeRegistry::data_object_type(TEST_FIRST_DATA_OBJECT_TYPE_ID);
+            assert!(data.is_some());
+            assert!(data.unwrap().active);
+
+            // Deactivate again.
+            let res = TestDataObjectTypeRegistry::deactivate_data_object_type(
+                Origin::ROOT,
+                TEST_FIRST_DATA_OBJECT_TYPE_ID,
+            );
+            assert!(res.is_ok());
+            let data = TestDataObjectTypeRegistry::data_object_type(TEST_FIRST_DATA_OBJECT_TYPE_ID);
+            assert!(data.is_some());
+            assert!(!data.unwrap().active);
+        });
+    }
+}

+ 174 - 0
src/storage/downloads.rs

@@ -0,0 +1,174 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+/*
+ * XXX This module is not really supposed to be used this way, and therefore also lacks tests.
+ *
+ *     This is a straightforward implementation of the whitepaper's specs, and is intended to
+ *     be iterated over. Please don't use it as-is.
+ */
+
+use crate::storage::data_directory::Trait as DDTrait;
+use crate::storage::data_object_storage_registry::Trait as DOSRTrait;
+use crate::traits::{ContentHasStorage, ContentIdExists};
+use parity_codec::Codec;
+use parity_codec_derive::{Decode, Encode};
+use rstd::prelude::*;
+use runtime_primitives::traits::{As, MaybeSerializeDebug, Member, SimpleArithmetic};
+use srml_support::{
+    decl_event, decl_module, decl_storage, ensure, Parameter, StorageMap, StorageValue,
+};
+use system::{self, ensure_signed};
+
+pub trait Trait: timestamp::Trait + system::Trait + DOSRTrait + DDTrait {
+    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
+    type DownloadSessionId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
+
+    type ContentHasStorage: ContentHasStorage<Self>;
+}
+
+static MSG_SESSION_NOT_FOUND: &str = "Download session with the given ID not found.";
+static MSG_SESSION_HAS_ENDED: &str = "Download session with the given ID has already ended.";
+static MSG_CONSUMER_REQUIRED: &str = "Download session can only be modified by the downloader.";
+static MSG_INVALID_TRANSMITTED_VALUE: &str = "Invalid update to transmitted bytes value.";
+static MSG_NEED_STORAGE_PROVIDER: &str =
+    "Cannnot download without at least one active storage relationship.";
+
+const DEFAULT_FIRST_DOWNLOAD_SESSION_ID: u64 = 1;
+
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub enum DownloadState {
+    Started,
+    Ended,
+}
+
+impl Default for DownloadState {
+    fn default() -> Self {
+        DownloadState::Started
+    }
+}
+
+#[derive(Clone, Encode, Decode, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub struct DownloadSession<T: Trait> {
+    pub content_id: <T as DDTrait>::ContentId,
+    pub consumer: T::AccountId,
+    pub distributor: T::AccountId,
+    pub initiated_at_block: T::BlockNumber,
+    pub initiated_at_time: T::Moment,
+    pub state: DownloadState,
+    pub transmitted_bytes: u64,
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as DownloadSessions {
+        // Start at this value
+        pub FirstDownloadSessionId get(first_download_session_id) config(first_download_session_id): T::DownloadSessionId = T::DownloadSessionId::sa(DEFAULT_FIRST_DOWNLOAD_SESSION_ID);
+
+        // Increment
+        pub NextDownloadSessionId get(next_download_session_id) build(|config: &GenesisConfig<T>| config.first_download_session_id): T::DownloadSessionId = T::DownloadSessionId::sa(DEFAULT_FIRST_DOWNLOAD_SESSION_ID);
+
+        // Mapping of Data object types
+        pub DownloadSessions get(download_sessions): map T::DownloadSessionId => Option<DownloadSession<T>>;
+    }
+}
+
+decl_event! {
+    pub enum Event<T> where
+        <T as DDTrait>::ContentId
+    {
+        // We send the content ID *only* because while we already leak download
+        // session information by storing it on the public chain, there's no
+        // need to advertise each download even more.
+        DownloadStarted(ContentId),
+
+        // Transmitted size is included in the ended event.
+        DownloadEnded(ContentId, u64),
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+        fn deposit_event<T>() = default;
+
+        // Origin starts a download from distributor. It's the origin's responsibility to
+        // start this, and hand the session ID to the distributor as proof they did.
+        pub fn start_download(origin, content_id: <T as DDTrait>::ContentId, from: T::AccountId) {
+            // Origin can be anyone, it doesn't have to be a member.
+            let who = ensure_signed(origin)?;
+
+            // There has to be a storage relationship for the content ID and storage provider.
+            ensure!(T::ContentHasStorage::is_ready_at_storage_provider(&content_id, &from), MSG_NEED_STORAGE_PROVIDER);
+
+            // Let's create the entry then
+            let new_id = Self::next_download_session_id();
+            let session: DownloadSession<T> = DownloadSession {
+                content_id: content_id.clone(),
+                consumer: who,
+                distributor: from.clone(),
+                initiated_at_block: <system::Module<T>>::block_number(),
+                initiated_at_time: <timestamp::Module<T>>::now(),
+                state: DownloadState::Started,
+                transmitted_bytes: 0u64,
+            };
+
+            <DownloadSessions<T>>::insert(new_id, session);
+            <NextDownloadSessionId<T>>::mutate(|n| { *n += T::DownloadSessionId::sa(1); });
+
+            // Fire off event
+            Self::deposit_event(RawEvent::DownloadStarted(content_id));
+        }
+
+        // The downloader can also update the transmitted size, as long as it's
+        // strictly larger.
+        pub fn update_transmitted(origin, session_id: T::DownloadSessionId, transmitted_bytes: u64)
+        {
+            // Origin can be anyone, it doesn't have to be a member.
+            let who = ensure_signed(origin)?;
+
+            // Get session
+            let mut session = Self::download_sessions(session_id).ok_or(MSG_SESSION_NOT_FOUND)?;
+
+            // Ensure that the session hasn't ended yet.
+            ensure!(session.state == DownloadState::Started, MSG_SESSION_HAS_ENDED);
+
+            // Ensure that the origin is the consumer
+            ensure!(session.consumer == who, MSG_CONSUMER_REQUIRED);
+
+            // Ensure that the new transmitted size is larger than the old one
+            ensure!(transmitted_bytes > session.transmitted_bytes, MSG_INVALID_TRANSMITTED_VALUE);
+
+            // By fetching the content information, we can ensure that the transmitted
+            // field also does not exceed the content size. Furthermore, we can
+            // automatically detect when the download ended.
+            let data_object = T::ContentIdExists::get_data_object(&session.content_id)?;
+            ensure!(transmitted_bytes <= data_object.size, MSG_INVALID_TRANSMITTED_VALUE);
+
+            let finished = transmitted_bytes == data_object.size;
+
+            // Ok we can update the data.
+            session.transmitted_bytes = transmitted_bytes;
+            session.state = match finished {
+                true => DownloadState::Ended,
+                false => DownloadState::Started,
+            };
+            let content_id = session.content_id.clone();
+            <DownloadSessions<T>>::insert(session_id, session);
+
+            // Also announce if the download finished
+            if finished {
+                Self::deposit_event(RawEvent::DownloadEnded(content_id, transmitted_bytes));
+            }
+        }
+    }
+}

+ 174 - 12
src/storage/mock.rs

@@ -1,17 +1,20 @@
 #![cfg(test)]
 
-use rstd::prelude::*;
-pub use super::{data_object_type_registry};
+pub use super::{
+    content_directory, data_directory, data_object_storage_registry, data_object_type_registry,
+};
+use crate::traits;
+use runtime_io::with_externalities;
 pub use system;
 
-pub use primitives::{H256, Blake2Hasher};
+pub use primitives::{Blake2Hasher, H256};
 pub use runtime_primitives::{
+    testing::{Digest, DigestItem, Header, UintAuthorityId},
+    traits::{BlakeTwo256, IdentityLookup, OnFinalise},
     BuildStorage,
-    traits::{BlakeTwo256, OnFinalise, IdentityLookup},
-    testing::{Digest, DigestItem, Header, UintAuthorityId}
 };
 
-use srml_support::{impl_outer_origin, impl_outer_event};
+use srml_support::{impl_outer_event, impl_outer_origin};
 
 impl_outer_origin! {
     pub enum Origin for Test {}
@@ -20,6 +23,60 @@ impl_outer_origin! {
 impl_outer_event! {
     pub enum MetaEvent for Test {
         data_object_type_registry<T>,
+        data_directory<T>,
+        data_object_storage_registry<T>,
+        content_directory<T>,
+    }
+}
+
+pub struct MockMembers {}
+impl<T: system::Trait> traits::Members<T> for MockMembers {
+    type Id = u64;
+
+    fn is_active_member(_who: &T::AccountId) -> bool {
+        true
+    }
+
+    fn lookup_member_id(_account_id: &T::AccountId) -> Result<Self::Id, &'static str> {
+        Err("not implemented for tests")
+    }
+
+    fn lookup_account_by_member_id(_member_id: Self::Id) -> Result<T::AccountId, &'static str> {
+        Err("not implemented for tests")
+    }
+}
+
+pub struct AnyDataObjectTypeIsActive {}
+impl<T: data_object_type_registry::Trait> traits::IsActiveDataObjectType<T>
+    for AnyDataObjectTypeIsActive
+{
+    fn is_active_data_object_type(_which: &T::DataObjectTypeId) -> bool {
+        true
+    }
+}
+
+pub struct MockContent {}
+impl traits::ContentIdExists<Test> for MockContent {
+    fn has_content(which: &<Test as data_directory::Trait>::ContentId) -> bool {
+        *which == 42
+    }
+
+    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 {
+                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_judgement: data_directory::LiaisonJudgement::Pending,
+            }),
+            _ => Err("nope, missing"),
+        }
     }
 }
 
@@ -41,19 +98,58 @@ impl system::Trait for Test {
     type Log = DigestItem;
     type Lookup = IdentityLookup<u64>;
 }
+
 impl data_object_type_registry::Trait for Test {
     type Event = MetaEvent;
     type DataObjectTypeId = u64;
 }
 
+impl data_directory::Trait for Test {
+    type Event = MetaEvent;
+    type ContentId = u64;
+    type Members = MockMembers;
+    type IsActiveDataObjectType = AnyDataObjectTypeIsActive;
+}
+
+impl data_object_storage_registry::Trait for Test {
+    type Event = MetaEvent;
+    type DataObjectStorageRelationshipId = u64;
+    type Members = MockMembers;
+    type ContentIdExists = MockContent;
+}
+
+impl content_directory::Trait for Test {
+    type Event = MetaEvent;
+    type MetadataId = u64;
+    type SchemaId = u64;
+    type Members = MockMembers;
+}
+
+impl timestamp::Trait for Test {
+    type Moment = u64;
+    type OnTimestampSet = ();
+}
+
+impl consensus::Trait for Test {
+    type SessionKey = UintAuthorityId;
+    type InherentOfflineReport = ();
+    type Log = DigestItem;
+}
+
 pub struct ExtBuilder {
     first_data_object_type_id: u64,
+    first_content_id: u64,
+    first_relationship_id: u64,
+    first_metadata_id: u64,
 }
 
 impl Default for ExtBuilder {
     fn default() -> Self {
         Self {
             first_data_object_type_id: 1,
+            first_content_id: 2,
+            first_relationship_id: 3,
+            first_metadata_id: 4,
         }
     }
 }
@@ -63,18 +159,84 @@ impl ExtBuilder {
         self.first_data_object_type_id = first_data_object_type_id;
         self
     }
+    pub fn first_content_id(mut self, first_content_id: u64) -> Self {
+        self.first_content_id = first_content_id;
+        self
+    }
+    pub fn first_relationship_id(mut self, first_relationship_id: u64) -> Self {
+        self.first_relationship_id = first_relationship_id;
+        self
+    }
+    pub fn first_metadata_id(mut self, first_metadata_id: u64) -> Self {
+        self.first_metadata_id = first_metadata_id;
+        self
+    }
     pub fn build(self) -> runtime_io::TestExternalities<Blake2Hasher> {
-        let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
+        let mut t = system::GenesisConfig::<Test>::default()
+            .build_storage()
+            .unwrap()
+            .0;
+
+        t.extend(
+            data_object_type_registry::GenesisConfig::<Test> {
+                first_data_object_type_id: self.first_data_object_type_id,
+            }
+            .build_storage()
+            .unwrap()
+            .0,
+        );
 
-        t.extend(data_object_type_registry::GenesisConfig::<Test>{
-            first_data_object_type_id: self.first_data_object_type_id,
-        }.build_storage().unwrap().0);
+        t.extend(
+            data_directory::GenesisConfig::<Test> {
+                first_content_id: self.first_content_id,
+            }
+            .build_storage()
+            .unwrap()
+            .0,
+        );
+
+        t.extend(
+            data_object_storage_registry::GenesisConfig::<Test> {
+                first_relationship_id: self.first_relationship_id,
+            }
+            .build_storage()
+            .unwrap()
+            .0,
+        );
+
+        t.extend(
+            content_directory::GenesisConfig::<Test> {
+                first_metadata_id: self.first_metadata_id,
+            }
+            .build_storage()
+            .unwrap()
+            .0,
+        );
 
         t.into()
     }
 }
 
-
 pub type System = system::Module<Test>;
 pub type TestDataObjectTypeRegistry = data_object_type_registry::Module<Test>;
-pub type TestDataObjectType = data_object_type_registry::DataObjectType<Test>;
+pub type TestDataObjectType = data_object_type_registry::DataObjectType;
+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 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()
+            .first_data_object_type_id(TEST_FIRST_DATA_OBJECT_TYPE_ID)
+            .first_content_id(TEST_FIRST_CONTENT_ID)
+            .first_relationship_id(TEST_FIRST_RELATIONSHIP_ID)
+            .first_metadata_id(TEST_FIRST_METADATA_ID)
+            .build(),
+        || f(),
+    )
+}

+ 4 - 1
src/storage/mod.rs

@@ -1,6 +1,9 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
+pub mod content_directory;
+pub mod data_directory;
+pub mod data_object_storage_registry;
 pub mod data_object_type_registry;
+pub mod downloads;
 
 mod mock;
-mod tests;

+ 171 - 124
src/storage/tests.rs

@@ -1,159 +1,206 @@
 #![cfg(test)]
 
-use super::*;
 use super::mock::*;
+use super::*;
 
 use runtime_io::with_externalities;
-use srml_support::*;
-use system::{self, Phase, EventRecord};
+use system::{self, EventRecord, Phase};
 
 #[test]
 fn initial_state() {
     const DEFAULT_FIRST_ID: u64 = 1000;
 
-    with_externalities(&mut ExtBuilder::default()
-        .first_data_object_type_id(DEFAULT_FIRST_ID).build(), || {
-        assert_eq!(TestDataObjectTypeRegistry::first_data_object_type_id(), DEFAULT_FIRST_ID);
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .first_data_object_type_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            assert_eq!(
+                TestDataObjectTypeRegistry::first_data_object_type_id(),
+                DEFAULT_FIRST_ID
+            );
+        },
+    );
 }
 
 #[test]
 fn fail_register_without_root() {
     const DEFAULT_FIRST_ID: u64 = 1000;
 
-    with_externalities(&mut ExtBuilder::default()
-        .first_data_object_type_id(DEFAULT_FIRST_ID).build(), || {
-        let data: TestDataObjectType = TestDataObjectType {
-            id: None,
-            description: "foo".as_bytes().to_vec(),
-            active: false,
-        };
-        let res = TestDataObjectTypeRegistry::register_data_object_type(Origin::signed(1), data);
-        assert!(res.is_err());
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .first_data_object_type_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            let data: TestDataObjectType = TestDataObjectType {
+                id: None,
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let res =
+                TestDataObjectTypeRegistry::register_data_object_type(Origin::signed(1), data);
+            assert!(res.is_err());
+        },
+    );
 }
 
 #[test]
 fn succeed_register_as_root() {
     const DEFAULT_FIRST_ID: u64 = 1000;
 
-    with_externalities(&mut ExtBuilder::default()
-        .first_data_object_type_id(DEFAULT_FIRST_ID).build(), || {
-        let data: TestDataObjectType = TestDataObjectType {
-            id: None,
-            description: "foo".as_bytes().to_vec(),
-            active: false,
-        };
-        let res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
-        assert!(res.is_ok());
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .first_data_object_type_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            let data: TestDataObjectType = TestDataObjectType {
+                id: None,
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
+            assert!(res.is_ok());
+        },
+    );
 }
 
 #[test]
 fn update_existing() {
     const DEFAULT_FIRST_ID: u64 = 1000;
 
-    with_externalities(&mut ExtBuilder::default()
-        .first_data_object_type_id(DEFAULT_FIRST_ID).build(), || {
-        // First register a type
-        let data: TestDataObjectType = TestDataObjectType {
-            id: None,
-            description: "foo".as_bytes().to_vec(),
-            active: false,
-        };
-        let id_res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
-        assert!(id_res.is_ok());
-        assert_eq!(*System::events().last().unwrap(),
-            EventRecord {
-                phase: Phase::ApplyExtrinsic(0),
-                event: MetaEvent::data_object_type_registry(data_object_type_registry::RawEvent::DataObjectTypeRegistered(DEFAULT_FIRST_ID)),
-            }
-        );
-
-
-        // Now update it with new data - we need the ID to be the same as in
-        // returned by the previous call. First, though, try and fail without
-        let updated1: TestDataObjectType = TestDataObjectType {
-            id: None,
-            description: "bar".as_bytes().to_vec(),
-            active: false,
-        };
-        let res = TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, updated1);
-        assert!(res.is_err());
-
-        // Now try with a bad ID
-        let updated2: TestDataObjectType = TestDataObjectType {
-            id: Some(DEFAULT_FIRST_ID + 1),
-            description: "bar".as_bytes().to_vec(),
-            active: false,
-        };
-        let res = TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, updated2);
-        assert!(res.is_err());
-
-        // Finally with an existing ID, it should work.
-        let updated3: TestDataObjectType = TestDataObjectType {
-            id: Some(DEFAULT_FIRST_ID),
-            description: "bar".as_bytes().to_vec(),
-            active: false,
-        };
-        let res = TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, updated3);
-        assert!(res.is_ok());
-        assert_eq!(*System::events().last().unwrap(),
-            EventRecord {
-                phase: Phase::ApplyExtrinsic(0),
-                event: MetaEvent::data_object_type_registry(data_object_type_registry::RawEvent::DataObjectTypeUpdated(DEFAULT_FIRST_ID)),
-            }
-        );
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .first_data_object_type_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            // First register a type
+            let data: TestDataObjectType = TestDataObjectType {
+                id: None,
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let id_res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
+            assert!(id_res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeRegistered(
+                            DEFAULT_FIRST_ID
+                        )
+                    ),
+                }
+            );
+
+            // Now update it with new data - we need the ID to be the same as in
+            // returned by the previous call. First, though, try and fail without
+            let updated1: TestDataObjectType = TestDataObjectType {
+                id: None,
+                description: "bar".as_bytes().to_vec(),
+                active: false,
+            };
+            let res = TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, updated1);
+            assert!(res.is_err());
+
+            // Now try with a bad ID
+            let updated2: TestDataObjectType = TestDataObjectType {
+                id: Some(DEFAULT_FIRST_ID + 1),
+                description: "bar".as_bytes().to_vec(),
+                active: false,
+            };
+            let res = TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, updated2);
+            assert!(res.is_err());
+
+            // Finally with an existing ID, it should work.
+            let updated3: TestDataObjectType = TestDataObjectType {
+                id: Some(DEFAULT_FIRST_ID),
+                description: "bar".as_bytes().to_vec(),
+                active: false,
+            };
+            let res = TestDataObjectTypeRegistry::update_data_object_type(Origin::ROOT, updated3);
+            assert!(res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeUpdated(
+                            DEFAULT_FIRST_ID
+                        )
+                    ),
+                }
+            );
+        },
+    );
 }
 
-
 #[test]
 fn activate_existing() {
     const DEFAULT_FIRST_ID: u64 = 1000;
 
-    with_externalities(&mut ExtBuilder::default()
-        .first_data_object_type_id(DEFAULT_FIRST_ID).build(), || {
-        // First register a type
-        let data: TestDataObjectType = TestDataObjectType {
-            id: None,
-            description: "foo".as_bytes().to_vec(),
-            active: false,
-        };
-        let id_res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
-        assert!(id_res.is_ok());
-        assert_eq!(*System::events().last().unwrap(),
-            EventRecord {
-                phase: Phase::ApplyExtrinsic(0),
-                event: MetaEvent::data_object_type_registry(data_object_type_registry::RawEvent::DataObjectTypeRegistered(DEFAULT_FIRST_ID)),
-            }
-        );
-
-        // Retrieve, and ensure it's not active.
-        let data = TestDataObjectTypeRegistry::data_object_type(DEFAULT_FIRST_ID);
-        assert!(data.is_some());
-        assert!(!data.unwrap().active);
-
-        // Now activate the data object type
-        let res = TestDataObjectTypeRegistry::activate_data_object_type(Origin::ROOT, DEFAULT_FIRST_ID);
-        assert!(res.is_ok());
-        assert_eq!(*System::events().last().unwrap(),
-            EventRecord {
-                phase: Phase::ApplyExtrinsic(0),
-                event: MetaEvent::data_object_type_registry(data_object_type_registry::RawEvent::DataObjectTypeUpdated(DEFAULT_FIRST_ID)),
-            }
-        );
-
-        // Ensure that the item is actually activated.
-        let data = TestDataObjectTypeRegistry::data_object_type(DEFAULT_FIRST_ID);
-        assert!(data.is_some());
-        assert!(data.unwrap().active);
-
-        // Deactivate again.
-        let res = TestDataObjectTypeRegistry::deactivate_data_object_type(Origin::ROOT, DEFAULT_FIRST_ID);
-        assert!(res.is_ok());
-        let data = TestDataObjectTypeRegistry::data_object_type(DEFAULT_FIRST_ID);
-        assert!(data.is_some());
-        assert!(!data.unwrap().active);
-    });
+    with_externalities(
+        &mut ExtBuilder::default()
+            .first_data_object_type_id(DEFAULT_FIRST_ID)
+            .build(),
+        || {
+            // First register a type
+            let data: TestDataObjectType = TestDataObjectType {
+                id: None,
+                description: "foo".as_bytes().to_vec(),
+                active: false,
+            };
+            let id_res = TestDataObjectTypeRegistry::register_data_object_type(Origin::ROOT, data);
+            assert!(id_res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeRegistered(
+                            DEFAULT_FIRST_ID
+                        )
+                    ),
+                }
+            );
+
+            // Retrieve, and ensure it's not active.
+            let data = TestDataObjectTypeRegistry::data_object_type(DEFAULT_FIRST_ID);
+            assert!(data.is_some());
+            assert!(!data.unwrap().active);
+
+            // Now activate the data object type
+            let res = TestDataObjectTypeRegistry::activate_data_object_type(
+                Origin::ROOT,
+                DEFAULT_FIRST_ID,
+            );
+            assert!(res.is_ok());
+            assert_eq!(
+                *System::events().last().unwrap(),
+                EventRecord {
+                    phase: Phase::ApplyExtrinsic(0),
+                    event: MetaEvent::data_object_type_registry(
+                        data_object_type_registry::RawEvent::DataObjectTypeUpdated(
+                            DEFAULT_FIRST_ID
+                        )
+                    ),
+                }
+            );
+
+            // Ensure that the item is actually activated.
+            let data = TestDataObjectTypeRegistry::data_object_type(DEFAULT_FIRST_ID);
+            assert!(data.is_some());
+            assert!(data.unwrap().active);
+
+            // Deactivate again.
+            let res = TestDataObjectTypeRegistry::deactivate_data_object_type(
+                Origin::ROOT,
+                DEFAULT_FIRST_ID,
+            );
+            assert!(res.is_ok());
+            let data = TestDataObjectTypeRegistry::data_object_type(DEFAULT_FIRST_ID);
+            assert!(data.is_some());
+            assert!(!data.unwrap().active);
+        },
+    );
 }

+ 31 - 15
src/traits.rs

@@ -1,15 +1,23 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use crate::storage::data_object_type_registry;
-use system;
+use crate::storage::{data_directory, data_object_storage_registry, data_object_type_registry};
 use parity_codec::Codec;
-use srml_support::{Parameter};
-use runtime_primitives::traits::{SimpleArithmetic, As, Member, MaybeSerializeDebug};
+use runtime_primitives::traits::{As, MaybeSerializeDebug, Member, SimpleArithmetic};
+use srml_support::Parameter;
+use system;
 
 // Members
 pub trait Members<T: system::Trait> {
-    type Id : Parameter + Member + SimpleArithmetic + Codec + Default + Copy
-        + As<usize> + As<u64> + MaybeSerializeDebug + PartialEq;
+    type Id: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + As<usize>
+        + As<u64>
+        + MaybeSerializeDebug
+        + PartialEq;
 
     fn is_active_member(account_id: &T::AccountId) -> bool;
 
@@ -37,18 +45,26 @@ pub trait Roles<T: system::Trait> {
 }
 
 impl<T: system::Trait> Roles<T> for () {
-	fn is_role_account(_who: &T::AccountId) -> bool { false }
+    fn is_role_account(_who: &T::AccountId) -> bool {
+        false
+    }
 }
 
-// Data Object Types
+// Storage
 pub trait IsActiveDataObjectType<T: data_object_type_registry::Trait> {
-    fn is_active_data_object_type(which: &T::DataObjectTypeId) -> bool {
-        false
-    }
+    fn is_active_data_object_type(_which: &T::DataObjectTypeId) -> bool;
 }
 
-pub trait IsActiveMember<T: system::Trait> {
-    fn is_active_member(account_id: &T::AccountId) -> bool {
-        false
-    }
+pub trait ContentIdExists<T: data_directory::Trait> {
+    fn has_content(_which: &T::ContentId) -> bool;
+
+    fn get_data_object(
+        _which: &T::ContentId,
+    ) -> Result<data_directory::DataObject<T>, &'static str>;
+}
+
+pub trait ContentHasStorage<T: data_object_storage_registry::Trait> {
+    fn has_storage_provider(_which: &T::ContentId) -> bool;
+
+    fn is_ready_at_storage_provider(_which: &T::ContentId, _provider: &T::AccountId) -> bool;
 }

Some files were not shown because too many files changed in this diff