Mokhtar Naamani 4 лет назад
Родитель
Сommit
2e3a7d3736

+ 1 - 0
Cargo.lock

@@ -3824,6 +3824,7 @@ dependencies = [
  "pallet-timestamp",
  "pallet-versioned-store",
  "parity-scale-codec",
+ "serde",
  "sp-arithmetic",
  "sp-core",
  "sp-io",

+ 105 - 0
node/src/chain_spec/content_config.rs

@@ -0,0 +1,105 @@
+use node_runtime::{
+    data_directory::DataObject,
+    primitives::{BlockNumber, Credential},
+    versioned_store::{Class, ClassId, Entity, EntityId, InputValidationLengthConstraint},
+    versioned_store_permissions::ClassPermissions,
+    ContentId, Runtime, VersionedStoreConfig,
+};
+
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+pub struct ClassAndPermissions {
+    class: Class,
+    permissions: ClassPermissions<ClassId, Credential, u16, BlockNumber>,
+}
+
+#[derive(Deserialize)]
+pub struct EntityAndMaintainer {
+    entity: Entity,
+    maintainer: Option<Credential>,
+}
+
+#[derive(Deserialize)]
+pub struct DataObjectAndContentId {
+    content_id: ContentId,
+    // data_object: DataObjectInternal<u64, BlockNumber, Moment, u64, ActorId>,
+    data_object: DataObject<Runtime>,
+}
+
+#[derive(Deserialize)]
+pub struct ContentData {
+    /// classes and their associted permissions
+    classes: Vec<ClassAndPermissions>,
+    /// entities and their associated maintainer
+    entities: Vec<EntityAndMaintainer>,
+    /// DataObject(s) and ContentId
+    data_objects: Vec<DataObjectAndContentId>,
+}
+
+use std::{fs, path::Path};
+
+fn parse_content_data(data_file: &Path) -> ContentData {
+    let data = fs::read_to_string(data_file).expect("Failed reading file");
+    serde_json::from_str(&data).expect("failed parsing members data")
+}
+
+pub fn versioned_store_config_from_json(data_file: &Path) -> VersionedStoreConfig {
+    let content = parse_content_data(data_file);
+    let base_config = empty_versioned_store_config();
+    let first_id = 1;
+
+    let next_class_id: ClassId = content
+        .classes
+        .last()
+        .map_or(first_id, |class_and_perm| class_and_perm.class.id + 1);
+    assert_eq!(next_class_id, (content.classes.len() + 1) as ClassId);
+
+    let next_entity_id: EntityId = content
+        .entities
+        .last()
+        .map_or(first_id, |entity_and_maintainer| {
+            entity_and_maintainer.entity.id + 1
+        });
+    assert_eq!(next_entity_id, (content.entities.len() + 1) as EntityId);
+
+    VersionedStoreConfig {
+        class_by_id: content
+            .classes
+            .into_iter()
+            .map(|class_and_permissions| {
+                (class_and_permissions.class.id, class_and_permissions.class)
+            })
+            .collect(),
+        entity_by_id: content
+            .entities
+            .into_iter()
+            .map(|entity_and_maintainer| {
+                (
+                    entity_and_maintainer.entity.id,
+                    entity_and_maintainer.entity,
+                )
+            })
+            .collect(),
+        next_class_id,
+        next_entity_id,
+        ..base_config
+    }
+}
+
+fn new_validation(min: u16, max_min_diff: u16) -> InputValidationLengthConstraint {
+    InputValidationLengthConstraint { min, max_min_diff }
+}
+
+pub fn empty_versioned_store_config() -> VersionedStoreConfig {
+    VersionedStoreConfig {
+        class_by_id: vec![],
+        entity_by_id: vec![],
+        next_class_id: 1,
+        next_entity_id: 1,
+        property_name_constraint: new_validation(1, 99),
+        property_description_constraint: new_validation(1, 999),
+        class_name_constraint: new_validation(1, 99),
+        class_description_constraint: new_validation(1, 999),
+    }
+}

+ 6 - 13
node/src/chain_spec/mod.rs

@@ -40,6 +40,7 @@ use node_runtime::{
 // Exported to be used by chain-spec-builder
 pub use node_runtime::{membership, AccountId, ForumConfig, GenesisConfig, Moment};
 
+pub mod content_config;
 pub mod forum_config;
 pub mod initial_members;
 pub mod proposals_config;
@@ -134,6 +135,7 @@ impl Alternative {
                         proposals_config::development(),
                         initial_members::none(),
                         forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
+                        content_config::empty_versioned_store_config(),
                     )
                 },
                 Vec::new(),
@@ -170,6 +172,7 @@ impl Alternative {
                         proposals_config::development(),
                         initial_members::none(),
                         forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
+                        content_config::empty_versioned_store_config(),
                     )
                 },
                 Vec::new(),
@@ -213,6 +216,7 @@ pub fn testnet_genesis(
     cpcp: node_runtime::ProposalsConfigParameters,
     members: Vec<membership::genesis::Member<u64, AccountId, Moment>>,
     forum_config: ForumConfig,
+    versioned_store_config: VersionedStoreConfig,
 ) -> GenesisConfig {
     const CENTS: Balance = 1;
     const DOLLARS: Balance = 100 * CENTS;
@@ -245,9 +249,7 @@ pub fn testnet_genesis(
             slash_reward_fraction: Perbill::from_percent(10),
             ..Default::default()
         }),
-        pallet_sudo: Some(SudoConfig {
-            key: root_key.clone(),
-        }),
+        pallet_sudo: Some(SudoConfig { key: root_key }),
         pallet_babe: Some(BabeConfig {
             authorities: vec![],
         }),
@@ -303,16 +305,7 @@ pub fn testnet_genesis(
             worker_application_human_readable_text_constraint: default_text_constraint,
             worker_exit_rationale_text_constraint: default_text_constraint,
         }),
-        versioned_store: Some(VersionedStoreConfig {
-            class_by_id: vec![],
-            entity_by_id: vec![],
-            next_class_id: 1,
-            next_entity_id: 1,
-            property_name_constraint: new_vs_validation(1, 99),
-            property_description_constraint: new_vs_validation(1, 999),
-            class_name_constraint: new_vs_validation(1, 99),
-            class_description_constraint: new_vs_validation(1, 999),
-        }),
+        versioned_store: Some(versioned_store_config),
         content_wg: Some(ContentWorkingGroupConfig {
             mint_capacity: 100_000,
             curator_opening_by_id: vec![],

+ 5 - 0
runtime-modules/storage/src/data_directory.rs

@@ -30,6 +30,9 @@ use sp_std::collections::btree_map::BTreeMap;
 use sp_std::vec::Vec;
 use system::ensure_root;
 
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+
 use common::origin::ActorOriginValidator;
 pub(crate) use common::BlockAndTime;
 
@@ -87,6 +90,7 @@ decl_error! {
 }
 
 /// The decision of the storage provider when it acts as liaison.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Clone, Encode, Decode, PartialEq, Debug)]
 pub enum LiaisonJudgement {
     /// Content awaits for a judgment.
@@ -115,6 +119,7 @@ pub type DataObject<T> = DataObjectInternal<
 >;
 
 /// Manages content ids, type and storage provider decision about it.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Clone, Encode, Decode, PartialEq, Debug)]
 pub struct DataObjectInternal<MemberId, BlockNumber, Moment, DataObjectTypeId, StorageProviderId> {
     /// Content owner.

+ 1 - 0
runtime-modules/versioned-store-permissions/Cargo.toml

@@ -5,6 +5,7 @@ authors = ['Joystream contributors']
 edition = '2018'
 
 [dependencies]
+serde = { version = "1.0.101", features = ["derive"] }
 codec = { package = 'parity-scale-codec', version = '1.3.1', default-features = false, features = ['derive'] }
 sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}

+ 4 - 0
runtime-modules/versioned-store-permissions/src/constraint.rs

@@ -1,7 +1,10 @@
 use codec::{Decode, Encode};
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
 use sp_std::collections::btree_set::BTreeSet;
 
 /// Reference to a specific property of a specific class.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
 pub struct PropertyOfClass<ClassId, PropertyIndex> {
     pub class_id: ClassId,
@@ -9,6 +12,7 @@ pub struct PropertyOfClass<ClassId, PropertyIndex> {
 }
 
 /// The type of constraint imposed on referencing a class via class property of type "Internal".
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
 pub enum ReferenceConstraint<ClassId: Ord, PropertyIndex: Ord> {
     /// No property can reference the class.

+ 5 - 1
runtime-modules/versioned-store-permissions/src/credentials.rs

@@ -2,8 +2,12 @@ use codec::{Decode, Encode};
 use sp_std::collections::btree_set::BTreeSet;
 use sp_std::vec::Vec;
 
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct CredentialSet<Credential>(BTreeSet<Credential>);
+pub struct CredentialSet<Credential: Ord>(BTreeSet<Credential>);
 
 impl<Credential> From<Vec<Credential>> for CredentialSet<Credential>
 where

+ 5 - 0
runtime-modules/versioned-store-permissions/src/permissions.rs

@@ -4,7 +4,11 @@ use crate::constraint::*;
 use crate::credentials::*;
 use crate::DispatchResult;
 
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+
 /// Permissions for an instance of a Class in the versioned store.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Eq, PartialEq, Clone, Debug)]
 pub struct ClassPermissions<ClassId, Credential, PropertyIndex, BlockNumber>
 where
@@ -133,6 +137,7 @@ where
     }
 }
 
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
 pub struct EntityPermissions<Credential>
 where

+ 3 - 1
runtime/src/lib.rs

@@ -49,7 +49,7 @@ use integration::proposals::{CouncilManager, ExtrinsicProposalEncoder, Membershi
 
 use content_working_group as content_wg;
 use governance::{council, election};
-use storage::{data_directory, data_object_storage_registry, data_object_type_registry};
+use storage::data_object_storage_registry;
 
 // Node dependencies
 pub use common;
@@ -60,7 +60,9 @@ pub use membership;
 pub use pallet_balances::Call as BalancesCall;
 pub use pallet_staking::StakerStatus;
 pub use proposals_codex::ProposalsConfigParameters;
+pub use storage::{data_directory, data_object_type_registry};
 pub use versioned_store;
+pub use versioned_store_permissions;
 pub use working_group;
 
 /// This runtime version.

+ 36 - 3
utils/chain-spec-builder/src/main.rs

@@ -24,7 +24,8 @@ use rand::{distributions::Alphanumeric, rngs::OsRng, Rng};
 use structopt::StructOpt;
 
 use joystream_node::chain_spec::{
-    self, chain_spec_properties, forum_config, initial_members, proposals_config, AccountId,
+    self, chain_spec_properties, content_config, forum_config, initial_members, proposals_config,
+    AccountId,
 };
 
 use sc_chain_spec::ChainType;
@@ -64,6 +65,9 @@ enum ChainSpecBuilder {
         /// The path to an initial forum data
         #[structopt(long, short)]
         initial_forum_path: Option<PathBuf>,
+        /// The path to an initial content directory data file
+        #[structopt(long, short)]
+        initial_content_path: Option<PathBuf>,
     },
     /// Create a new chain spec with the given number of authorities and endowed
     /// accounts. Random keys will be generated as required.
@@ -90,6 +94,9 @@ enum ChainSpecBuilder {
         /// The path to an initial forum data
         #[structopt(long, short)]
         initial_forum_path: Option<PathBuf>,
+        /// The path to an initial content directory data file
+        #[structopt(long, short)]
+        initial_content_path: Option<PathBuf>,
     },
 }
 
@@ -106,7 +113,7 @@ impl ChainSpecBuilder {
         }
     }
 
-    /// Returns the path where the chain spec should be saved.
+    /// Returns the path to load initial members from
     fn initial_members_path(&self) -> &Option<PathBuf> {
         match self {
             ChainSpecBuilder::New {
@@ -120,7 +127,7 @@ impl ChainSpecBuilder {
         }
     }
 
-    /// Returns the path where the chain spec should be saved.
+    /// Returns the path to load initial forum from
     fn initial_forum_path(&self) -> &Option<PathBuf> {
         match self {
             ChainSpecBuilder::New {
@@ -131,6 +138,20 @@ impl ChainSpecBuilder {
             } => initial_forum_path,
         }
     }
+
+    /// Returns the path to load initial platform content from
+    fn initial_content_path(&self) -> &Option<PathBuf> {
+        match self {
+            ChainSpecBuilder::New {
+                initial_content_path,
+                ..
+            } => initial_content_path,
+            ChainSpecBuilder::Generate {
+                initial_content_path,
+                ..
+            } => initial_content_path,
+        }
+    }
 }
 
 fn genesis_constructor(
@@ -139,6 +160,7 @@ fn genesis_constructor(
     sudo_account: &AccountId,
     initial_members_path: &Option<PathBuf>,
     initial_forum_path: &Option<PathBuf>,
+    initial_content_path: &Option<PathBuf>,
 ) -> chain_spec::GenesisConfig {
     let authorities = authority_seeds
         .iter()
@@ -158,6 +180,12 @@ fn genesis_constructor(
         forum_config::empty(sudo_account.clone())
     };
 
+    let versioned_store_cfg = if let Some(path) = initial_content_path {
+        content_config::versioned_store_config_from_json(path.as_path())
+    } else {
+        content_config::empty_versioned_store_config()
+    };
+
     chain_spec::testnet_genesis(
         authorities,
         sudo_account.clone(),
@@ -165,6 +193,7 @@ fn genesis_constructor(
         proposals_config::default(),
         members,
         forum_cfg,
+        content_config::empty_versioned_store_config(),
     )
 }
 
@@ -174,6 +203,7 @@ fn generate_chain_spec(
     sudo_account: String,
     initial_members_path: Option<PathBuf>,
     initial_forum_path: Option<PathBuf>,
+    initial_content_path: Option<PathBuf>,
 ) -> Result<String, String> {
     let parse_account = |address: &String| {
         AccountId::from_string(address)
@@ -205,6 +235,7 @@ fn generate_chain_spec(
                 &sudo_account,
                 &initial_members_path,
                 &initial_forum_path,
+                &initial_content_path,
             )
         },
         vec![],
@@ -278,6 +309,7 @@ fn main() -> Result<(), String> {
     let chain_spec_path = builder.chain_spec_path().to_path_buf();
     let initial_members_path = builder.initial_members_path().clone();
     let initial_forum_path = builder.initial_forum_path().clone();
+    let initial_content_path = builder.initial_content_path().clone();
 
     let (authority_seeds, endowed_accounts, sudo_account) = match builder {
         ChainSpecBuilder::Generate {
@@ -326,6 +358,7 @@ fn main() -> Result<(), String> {
         sudo_account,
         initial_members_path,
         initial_forum_path,
+        initial_content_path,
     )?;
 
     fs::write(chain_spec_path, json).map_err(|err| err.to_string())