123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841 |
- //! # Content Directory Module
- //!
- //! The content directory is an on-chain index of all content and metadata,
- //! and related concepts - such as channels and playlists.
- //!
- //! - [`substrate_content_directory_module::Trait`](./trait.Trait.html)
- //! - [`Call`](./enum.Call.html)
- //! - [`Module`](./struct.Module.html)
- //!
- //! ## Overview
- //!
- //! The content directory provides functions for:
- //!
- //! - Creating/removal and managing curator groups
- //! - Creating classes and managing their permissions
- //! - Adding schemas to the class
- //! - Creating and removal of entities and managing their permissions
- //! - Adding schemas support to the respective class entities
- //! - Transfering entities ownership
- //! - Updating entity property values
- //!
- //! ## Terminology
- //!
- //! ### Class
- //!
- //! - **Class Properties:** All properties that have been used on this class across different class schemas.
- //! Unlikely to be more than roughly 20 properties per class, often less.
- //! For Person, think "height", "weight", etc.
- //!
- //! - **Schemas:** All schemas, that are available for this class, think v0.0 Person, v.1.0 Person, etc.
- //!
- //! ### Entity
- //!
- //! - **Supported Schemas:** What schemas under which entity of the respective class is available, think
- //! v.2.0 Person schema for John, v3.0 Person schema for John
- //! Unlikely to be more than roughly 20ish, assuming schemas for a given class eventually stableize,
- //! or that very old schema are eventually removed.
- //!
- //! - **Property Values:** Values for properties, declared on class level,
- //! that are used in respective Class Entity after adding Schema support.
- //!
- //! ## Interface
- //!
- //! ### Dispatchable Functions
- //!
- //! #### Curator groups
- //!
- //! - `add_curator_group` - Add new curator group to the runtime storage
- //! - `remove_curator_group` - Remove curator group under given `curator_group_id` from runtime storage.
- //! The origin of this call must be a blog owner.
- //! - `set_curator_group_status` - Set activity status for curator group under given `curator_group_id`
- //! - `add_curator_to_group` - Add curator to curator group under given `curator_group_id`
- //! - `remove_curator_from_group` - Remove curator from a given curator group.
- //!
- //! #### Classes
- //!
- //! - `create_class` - Create new class with provided parameters
- //! - `add_maintainer_to_class` - Add curator group under given curator_group_id as class maintainer
- //! - `remove_maintainer_from_class` - Remove curator group under given curator_group_id from class maintainers set
- //! - `update_class_permissions` - Update class permissions under specific class_id
- //! - `add_class_schema` - Create new class schema from existing property ids and new properties
- //! - `update_class_schema_status` - Update schema status under specific schema_id in class
- //!
- //! #### Entities
- //!
- //! - `create_entity` - Create new entity of respective class
- //! - `remove_entity` - Remove entity under provided entity_id
- //! - `update_entity_permissions` - Update entity permissions
- //! - `add_schema_support_to_entity` - add schema support to entity under given schema_id and provided property values
- //! - `update_entity_property_values` - Update entity property values with provided ones
- //! - `clear_entity_property_vector` - Clear property value vector under given entity_id & in class schema property id
- //! - `remove_at_entity_property_vector` - Remove value at given index_in_property_vector
- //! from property values vector under in_class schema property id
- //! - `insert_at_entity_property_vector` - Insert single input property values at given index in property vector
- //! into property values vector under in class schema property id
- //!
- //! #### Others
- //!
- //! - `update_entity_creation_voucher` - Update/create new entity creation voucher for given entity controller with individual limit
- //! - `transaction` - This extrinsic allows a batch operation, which is atomic, over the following operations:
- //! **Entity creation**
- //! **Adding schema support to the entity**
- //! **Update property values of the entity**
- //!
- //! ## Usage
- //!
- //! The following example shows how to use the content directory module in your custom module.
- //!
- //! ### Prerequisites
- //!
- //! Import the content directory module into your custom module and derive the module configuration
- //! trait from the content directory trait.
- //!
- //! ### Add curator group
- //!
- //! ```
- //! use frame_support::{decl_module, assert_ok};
- //! use system::{self as system, ensure_signed};
- //!
- //! pub trait Trait: pallet_content_directory::Trait {}
- //!
- //! decl_module! {
- //! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
- //! #[weight = 10_000_000]
- //! pub fn add_curator_group(origin) -> Result<(), &'static str> {
- //! <pallet_content_directory::Module<T>>::add_curator_group(origin)?;
- //! Ok(())
- //! }
- //! }
- //! }
- //! # fn main() {}
- //! ```
- // Ensure we're `no_std` when compiling for Wasm.
- #![cfg_attr(not(feature = "std"), no_std)]
- #![recursion_limit = "256"]
- #[cfg(test)]
- mod tests;
- mod class;
- mod entity;
- mod errors;
- mod helpers;
- mod mock;
- mod operations;
- mod permissions;
- mod schema;
- pub use class::*;
- pub use entity::*;
- pub use errors::*;
- pub use helpers::*;
- pub use operations::*;
- pub use permissions::*;
- pub use schema::*;
- use core::hash::Hash;
- use core::ops::AddAssign;
- use codec::{Codec, Decode, Encode};
- use frame_support::storage::IterableStorageMap;
- use frame_support::{
- decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure, traits::Get, Parameter,
- };
- #[cfg(feature = "std")]
- pub use serde::{Deserialize, Serialize};
- use sp_arithmetic::traits::{BaseArithmetic, One, Zero};
- use sp_runtime::traits::{MaybeSerializeDeserialize, Member};
- use sp_std::borrow::ToOwned;
- use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
- use sp_std::vec;
- use sp_std::vec::Vec;
- use system::ensure_signed;
- pub use errors::Error;
- use core::debug_assert;
- /// Type, used in diffrent numeric constraints representations
- pub type MaxNumber = u32;
- /// Module configuration trait for this Substrate module.
- pub trait Trait: system::Trait + ActorAuthenticator + Clone {
- /// The overarching event type.
- type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
- /// Nonce type is used to avoid data race update conditions, when performing property value vector operations
- type Nonce: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + Clone
- + MaybeSerializeDeserialize
- + Eq
- + PartialEq
- + Ord
- + From<u32>;
- /// Type of identifier for classes
- type ClassId: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + Clone
- + Hash
- + MaybeSerializeDeserialize
- + Eq
- + PartialEq
- + Ord;
- /// Type of identifier for entities
- type EntityId: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + Clone
- + Hash
- + MaybeSerializeDeserialize
- + Eq
- + PartialEq
- + Ord;
- /// Security/configuration constraints
- /// Type, representing min & max property name length constraints
- type PropertyNameLengthConstraint: Get<InputValidationLengthConstraint>;
- /// Type, representing min & max property description length constraints
- type PropertyDescriptionLengthConstraint: Get<InputValidationLengthConstraint>;
- /// Type, representing min & max class name length constraints
- type ClassNameLengthConstraint: Get<InputValidationLengthConstraint>;
- /// Type, representing min & max class description length constraints
- type ClassDescriptionLengthConstraint: Get<InputValidationLengthConstraint>;
- /// The maximum number of classes
- type MaxNumberOfClasses: Get<MaxNumber>;
- /// The maximum number of maintainers per class constraint
- type MaxNumberOfMaintainersPerClass: Get<MaxNumber>;
- /// The maximum number of curators per group constraint
- type MaxNumberOfCuratorsPerGroup: Get<MaxNumber>;
- /// The maximum number of schemas per class constraint
- type MaxNumberOfSchemasPerClass: Get<MaxNumber>;
- /// The maximum number of properties per class constraint
- type MaxNumberOfPropertiesPerSchema: Get<MaxNumber>;
- /// The maximum number of operations during single invocation of `transaction`
- type MaxNumberOfOperationsDuringAtomicBatching: Get<MaxNumber>;
- /// The maximum length of vector property value constarint
- type VecMaxLengthConstraint: Get<VecMaxLength>;
- /// The maximum length of text property value constarint
- type TextMaxLengthConstraint: Get<TextMaxLength>;
- /// The maximum length of text, that will be hashed property value constarint
- type HashedTextMaxLengthConstraint: Get<HashedTextMaxLength>;
- /// Entities creation constraint per class
- type MaxNumberOfEntitiesPerClass: Get<Self::EntityId>;
- /// Entities creation constraint per individual
- type IndividualEntitiesCreationLimit: Get<Self::EntityId>;
- }
- decl_storage! {
- trait Store for Module<T: Trait> as ContentDirectory {
- /// Map, representing ClassId -> Class relation
- pub ClassById get(fn class_by_id) config(): map hasher(blake2_128_concat) T::ClassId => Class<T::EntityId, T::ClassId, T::CuratorGroupId>;
- /// Map, representing EntityId -> Entity relation
- pub EntityById get(fn entity_by_id) config(): map hasher(blake2_128_concat) T::EntityId => Entity<T::ClassId, T::MemberId, T::Hash, T::EntityId, T::Nonce>;
- /// Map, representing CuratorGroupId -> CuratorGroup relation
- pub CuratorGroupById get(fn curator_group_by_id) config(): map hasher(blake2_128_concat) T::CuratorGroupId => CuratorGroup<T>;
- /// Mapping of class id and its property id to the respective entity id and property value hash.
- pub UniquePropertyValueHashes get(fn unique_property_value_hashes): double_map hasher(blake2_128_concat) (T::ClassId, PropertyId), hasher(blake2_128_concat) T::Hash => ();
- /// Next runtime storage values used to maintain next id value, used on creation of respective curator groups, classes and entities
- pub NextClassId get(fn next_class_id) config(): T::ClassId;
- pub NextEntityId get(fn next_entity_id) config(): T::EntityId;
- pub NextCuratorGroupId get(fn next_curator_group_id) config(): T::CuratorGroupId;
- // The voucher associated with entity creation for a given class and controller.
- // Is updated whenever an entity is created in a given class by a given controller.
- // Constraint is updated by Root, an initial value comes from `ClassPermissions::default_entity_creation_voucher_upper_bound`.
- pub EntityCreationVouchers get(fn entity_creation_vouchers):
- double_map hasher(blake2_128_concat) T::ClassId, hasher(blake2_128_concat) EntityController<T::MemberId> => EntityCreationVoucher<T>;
- }
- }
- decl_module! {
- pub struct Module<T: Trait> for enum Call where origin: T::Origin {
- // ======
- // Next set of extrinsics can only be invoked by lead.
- // ======
- /// Initializing events
- fn deposit_event() = default;
- /// Add new curator group to runtime storage
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn add_curator_group(
- origin,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- //
- // == MUTATION SAFE ==
- //
- let curator_group_id = Self::next_curator_group_id();
- // Insert empty curator group with `active` parameter set to false
- <CuratorGroupById<T>>::insert(curator_group_id, CuratorGroup::<T>::default());
- // Increment the next curator curator_group_id:
- <NextCuratorGroupId<T>>::mutate(|n| *n += T::CuratorGroupId::one());
- // Trigger event
- Self::deposit_event(RawEvent::CuratorGroupAdded(curator_group_id));
- Ok(())
- }
- /// Remove curator group under given `curator_group_id` from runtime storage
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn remove_curator_group(
- origin,
- curator_group_id: T::CuratorGroupId,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure CuratorGroup under given curator_group_id exists
- let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
- // We should previously ensure that curator_group maintains no classes to be able to remove it
- curator_group.ensure_curator_group_maintains_no_classes()?;
- //
- // == MUTATION SAFE ==
- //
- // Remove curator group under given curator group id from runtime storage
- <CuratorGroupById<T>>::remove(curator_group_id);
- // Trigger event
- Self::deposit_event(RawEvent::CuratorGroupRemoved(curator_group_id));
- Ok(())
- }
- /// Set `is_active` status for curator group under given `curator_group_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn set_curator_group_status(
- origin,
- curator_group_id: T::CuratorGroupId,
- is_active: bool,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure curator group under provided curator_group_id already exist
- Self::ensure_curator_group_under_given_id_exists(&curator_group_id)?;
- //
- // == MUTATION SAFE ==
- //
- // Set `is_active` status for curator group under given `curator_group_id`
- <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
- curator_group.set_status(is_active)
- });
- // Trigger event
- Self::deposit_event(RawEvent::CuratorGroupStatusSet(curator_group_id, is_active));
- Ok(())
- }
- /// Add curator to curator group under given `curator_group_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn add_curator_to_group(
- origin,
- curator_group_id: T::CuratorGroupId,
- curator_id: T::CuratorId,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure curator group under provided curator_group_id already exist, retrieve corresponding one
- let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
- // Ensure max number of curators per group limit not reached yet
- curator_group.ensure_max_number_of_curators_limit_not_reached()?;
- //
- // == MUTATION SAFE ==
- //
- // Insert curator_id into curator_group under given curator_group_id
- <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
- curator_group.get_curators_mut().insert(curator_id);
- });
- // Trigger event
- Self::deposit_event(RawEvent::CuratorAdded(curator_group_id, curator_id));
- Ok(())
- }
- /// Remove curator from a given curator group
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn remove_curator_from_group(
- origin,
- curator_group_id: T::CuratorGroupId,
- curator_id: T::CuratorId,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure curator group under provided curator_group_id already exist, retrieve corresponding one
- let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
- // Ensure curator under provided curator_id is CuratorGroup member
- curator_group.ensure_curator_in_group_exists(&curator_id)?;
- //
- // == MUTATION SAFE ==
- //
- // Remove curator_id from curator_group under given curator_group_id
- <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
- curator_group.get_curators_mut().remove(&curator_id);
- });
- // Trigger event
- Self::deposit_event(RawEvent::CuratorRemoved(curator_group_id, curator_id));
- Ok(())
- }
- /// Updates or creates new `EntityCreationVoucher` for given `EntityController` with individual limit
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn update_entity_creation_voucher(
- origin,
- class_id: T::ClassId,
- controller: EntityController<T::MemberId>,
- maximum_entities_count: T::EntityId
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Class under given id exists, return corresponding one
- Self::ensure_known_class_id(class_id)?;
- // Ensure maximum_entities_count does not exceed individual entities creation limit
- Self::ensure_valid_number_of_class_entities_per_actor_constraint(maximum_entities_count)?;
- // Check voucher existance
- let voucher_exists = <EntityCreationVouchers<T>>::contains_key(class_id, &controller);
- //
- // == MUTATION SAFE ==
- //
- if voucher_exists {
- // Set new maximum_entities_count limit for selected voucher
- let mut entity_creation_voucher = Self::entity_creation_vouchers(class_id, &controller);
- entity_creation_voucher.set_maximum_entities_count(maximum_entities_count);
- <EntityCreationVouchers<T>>::insert(class_id, controller.clone(), entity_creation_voucher.clone());
- // Trigger event
- Self::deposit_event(RawEvent::EntityCreationVoucherUpdated(controller, entity_creation_voucher))
- } else {
- // Create new EntityCreationVoucher instance with provided maximum_entities_count
- let entity_creation_voucher = EntityCreationVoucher::new(maximum_entities_count);
- // Add newly created `EntityCreationVoucher` into `EntityCreationVouchers`
- // runtime storage under given `class_id`, `controller` key
- <EntityCreationVouchers<T>>::insert(class_id, controller.clone(), entity_creation_voucher.clone());
- // Trigger event
- Self::deposit_event(RawEvent::EntityCreationVoucherCreated(controller, entity_creation_voucher));
- }
- Ok(())
- }
- /// Create new `Class` with provided parameters
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn create_class(
- origin,
- name: Vec<u8>,
- description: Vec<u8>,
- class_permissions: ClassPermissions<T::CuratorGroupId>,
- maximum_entities_count: T::EntityId,
- default_entity_creation_voucher_upper_bound: T::EntityId
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure, that all entities creation limits, defined for a given Class, are valid
- Self::ensure_entities_creation_limits_are_valid(maximum_entities_count, default_entity_creation_voucher_upper_bound)?;
- // Ensure max number of classes limit not reached
- Self::ensure_class_limit_not_reached()?;
- // Ensure ClassNameLengthConstraint conditions satisfied
- Self::ensure_class_name_is_valid(&name)?;
- // Ensure ClassDescriptionLengthConstraint conditions satisfied
- Self::ensure_class_description_is_valid(&description)?;
- // Perform required checks to ensure class_maintainers under provided class_permissions are valid
- let class_maintainers = class_permissions.get_maintainers();
- Self::ensure_class_maintainers_are_valid(class_maintainers)?;
- //
- // == MUTATION SAFE ==
- //
- // Create new Class instance from provided values
- let class = Class::new(
- class_permissions, name, description, maximum_entities_count, default_entity_creation_voucher_upper_bound
- );
- let class_id = Self::next_class_id();
- // Add new `Class` to runtime storage
- <ClassById<T>>::insert(&class_id, class);
- // Increment the next class id:
- <NextClassId<T>>::mutate(|n| *n += T::ClassId::one());
- // Trigger event
- Self::deposit_event(RawEvent::ClassCreated(class_id));
- Ok(())
- }
- /// Add curator group under given `curator_group_id` as `Class` maintainer
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn add_maintainer_to_class(
- origin,
- class_id: T::ClassId,
- curator_group_id: T::CuratorGroupId,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Class under provided class_id exist, retrieve corresponding one
- let class = Self::ensure_known_class_id(class_id)?;
- // Ensure CuratorGroup under provided curator_group_id exist, retrieve corresponding one
- Self::ensure_curator_group_under_given_id_exists(&curator_group_id)?;
- // Ensure the max number of maintainers per Class limit not reached
- let class_permissions = class.get_permissions_ref();
- // Ensure max number of maintainers per Class constraint satisfied
- Self::ensure_maintainers_limit_not_reached(class_permissions.get_maintainers())?;
- // Ensure maintainer under provided curator_group_id is not added to the Class maintainers set yet
- class_permissions.ensure_maintainer_does_not_exist::<T>(&curator_group_id)?;
- //
- // == MUTATION SAFE ==
- //
- // Insert `curator_group_id` into `maintainers` set, associated with given `Class`
- <ClassById<T>>::mutate(class_id, |class|
- class.get_permissions_mut().get_maintainers_mut().insert(curator_group_id)
- );
- // Increment the number of classes, curator group under given `curator_group_id` maintains
- <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
- curator_group.increment_number_of_classes_maintained_count();
- });
- // Trigger event
- Self::deposit_event(RawEvent::MaintainerAdded(class_id, curator_group_id));
- Ok(())
- }
- /// Remove curator group under given `curator_group_id` from `Class` maintainers set
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn remove_maintainer_from_class(
- origin,
- class_id: T::ClassId,
- curator_group_id: T::CuratorGroupId,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Class under given id exists, return corresponding one
- let class = Self::ensure_known_class_id(class_id)?;
- // Ensure maintainer under provided curator_group_id was previously added
- // to the maintainers set, associated with corresponding Class
- class.get_permissions_ref().ensure_maintainer_exists::<T>(&curator_group_id)?;
- //
- // == MUTATION SAFE ==
- //
- // Remove `curator_group_id` from `maintainers` set, associated with given `Class`
- <ClassById<T>>::mutate(class_id, |class|
- class.get_permissions_mut().get_maintainers_mut().remove(&curator_group_id)
- );
- // Decrement the number of classes, curator group under given `curator_group_id` maintains
- <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
- curator_group.decrement_number_of_classes_maintained_count();
- });
- // Trigger event
- Self::deposit_event(RawEvent::MaintainerRemoved(class_id, curator_group_id));
- Ok(())
- }
- /// Update `ClassPermissions` under specific `class_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn update_class_permissions(
- origin,
- class_id: T::ClassId,
- updated_any_member: Option<bool>,
- updated_entity_creation_blocked: Option<bool>,
- updated_all_entity_property_values_locked: Option<bool>,
- updated_maintainers: Option<BTreeSet<T::CuratorGroupId>>,
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Class under given id exists, return corresponding one
- let class = Self::ensure_known_class_id(class_id)?;
- // Perform required checks to ensure class_maintainers are valid
- if let Some(ref updated_maintainers) = updated_maintainers {
- Self::ensure_class_maintainers_are_valid(updated_maintainers)?;
- }
- //
- // == MUTATION SAFE ==
- //
- let class_permissions = class.get_permissions();
- // Make updated class_permissions from parameters provided
- let updated_class_permissions = Self::make_updated_class_permissions(
- class_permissions, updated_any_member, updated_entity_creation_blocked,
- updated_all_entity_property_values_locked, updated_maintainers
- );
- // If class_permissions update has been performed
- if let Some(updated_class_permissions) = updated_class_permissions {
- // Update `class_permissions` under given class id
- <ClassById<T>>::mutate(class_id, |class| {
- class.update_permissions(updated_class_permissions)
- });
- // Trigger event
- Self::deposit_event(RawEvent::ClassPermissionsUpdated(class_id));
- }
- Ok(())
- }
- /// Create new class schema from existing property ids and new properties
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn add_class_schema(
- origin,
- class_id: T::ClassId,
- existing_properties: BTreeSet<PropertyId>,
- new_properties: Vec<Property<T::ClassId>>
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Class under given id exists, return corresponding one
- let class = Self::ensure_known_class_id(class_id)?;
- // Ensure Schemas limit per Class not reached
- class.ensure_schemas_limit_not_reached::<T>()?;
- // Ensure both existing and new properties for future Schema are not empty
- Self::ensure_non_empty_schema(&existing_properties, &new_properties)?;
- // Ensure max number of properties per Schema limit not reached
- class.ensure_properties_limit_not_reached::<T>(&new_properties)?;
- // Complete all checks to ensure all provided new_properties are valid
- Self::ensure_all_properties_are_valid(&new_properties)?;
- // Id of next Class Schema being added
- let schema_id = class.get_schemas().len() as SchemaId;
- let class_properties = class.get_properties();
- // Ensure all Property names are unique within Class
- Self::ensure_all_property_names_are_unique(&class_properties, &new_properties)?;
- // Ensure existing_properties are valid indices of properties, corresponding to chosen Class
- Self::ensure_schema_properties_are_valid_indices(&existing_properties, &class_properties)?;
- //
- // == MUTATION SAFE ==
- //
- // Create `Schema` instance from existing and new property ids
- let schema = Self::create_class_schema(existing_properties, &class_properties, &new_properties);
- // Update class properties after new `Schema` added
- let updated_class_properties = Self::make_updated_class_properties(class_properties, new_properties);
- // Update Class properties and schemas
- <ClassById<T>>::mutate(class_id, |class| {
- class.set_properties(updated_class_properties);
- class.get_schemas_mut().push(schema);
- });
- // Trigger event
- Self::deposit_event(RawEvent::ClassSchemaAdded(class_id, schema_id));
- Ok(())
- }
- /// Update `schema_status` under specific `schema_id` in `Class`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn update_class_schema_status(
- origin,
- class_id: T::ClassId,
- schema_id: SchemaId,
- schema_status: bool
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Class under given id exists, return corresponding one
- let class = Self::ensure_known_class_id(class_id)?;
- // Ensure Class already contains schema under provided schema_id
- class.ensure_schema_id_exists::<T>(schema_id)?;
- //
- // == MUTATION SAFE ==
- //
- // Update class schema status
- <ClassById<T>>::mutate(class_id, |class| {
- class.update_schema_status(schema_id, schema_status)
- });
- // Trigger event
- Self::deposit_event(RawEvent::ClassSchemaStatusUpdated(class_id, schema_id, schema_status));
- Ok(())
- }
- /// Update entity permissions
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn update_entity_permissions(
- origin,
- entity_id: T::EntityId,
- updated_frozen: Option<bool>,
- updated_referenceable: Option<bool>
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Entity under given id exists, return corresponding one
- let entity = Self::ensure_known_entity_id(entity_id)?;
- //
- // == MUTATION SAFE ==
- //
- // Make updated entity_permissions from parameters provided
- let entity_permissions = entity.get_permissions();
- let updated_entity_permissions =
- Self::make_updated_entity_permissions(entity_permissions, updated_frozen, updated_referenceable);
- // Update entity permissions under given entity id
- if let Some(updated_entity_permissions) = updated_entity_permissions {
- <EntityById<T>>::mutate(entity_id, |entity| {
- entity.update_permissions(updated_entity_permissions)
- });
- // Trigger event
- Self::deposit_event(RawEvent::EntityPermissionsUpdated(entity_id));
- }
- Ok(())
- }
- /// Transfer ownership to new `EntityController` for `Entity` under given `entity_id`
- /// `new_property_value_references_with_same_owner_flag_set` should be provided manually
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn transfer_entity_ownership(
- origin,
- entity_id: T::EntityId,
- new_controller: EntityController<T::MemberId>,
- new_property_value_references_with_same_owner_flag_set: BTreeMap<PropertyId, InputPropertyValue<T>>
- ) -> DispatchResult {
- // Ensure given origin is lead
- ensure_is_lead::<T>(origin)?;
- // Ensure Entity under given entity_id exists, retrieve corresponding Entity & Class
- let (entity, class) = Self::ensure_known_entity_and_class(entity_id)?;
- // Ensure provided new_entity_controller is not equal to current one
- entity.get_permissions_ref().ensure_controllers_are_not_equal::<T>(&new_controller)?;
- // Ensure any inbound InputPropertyValue::Reference with same_owner flag set points to the given Entity
- entity.ensure_inbound_same_owner_rc_is_zero::<T>()?;
- let class_properties = class.get_properties();
- let class_id = entity.get_class_id();
- let entity_property_values = entity.get_values();
- // Create wrapper structure from provided entity_property_values and their corresponding Class properties
- let values_for_existing_properties = match StoredValuesForExistingProperties::from(&class_properties, &entity_property_values) {
- Ok(values_for_existing_properties) => values_for_existing_properties,
- Err(e) => {
- debug_assert!(false, "Should not fail! {:?}", e);
- return Err(e.into())
- }
- };
- // Filter provided values_for_existing_properties, leaving only `Reference`'s with `SameOwner` flag set
- // Retrieve the set of corresponding property ids
- let entity_property_id_references_with_same_owner_flag_set =
- Self::get_property_id_references_with_same_owner_flag_set(values_for_existing_properties);
- // Ensure all ids of provided `new_property_value_references_with_same_owner_flag_set`
- // corresponding to property ids of respective Class Property references with same owner flag set
- Self::ensure_only_reference_ids_with_same_owner_flag_set_provided(
- &entity_property_id_references_with_same_owner_flag_set,
- &new_property_value_references_with_same_owner_flag_set
- )?;
- // Retrieve ids of all entity property values, that are references with same owner flag set and which are not provided
- // in new property value references with same owner flag set
- let unused_property_id_references_with_same_owner_flag_set = Self::compute_unused_property_ids(
- &new_property_value_references_with_same_owner_flag_set, &entity_property_id_references_with_same_owner_flag_set
- );
- // Perform checks to ensure all required property_values under provided unused_schema_property_ids provided
- Self::ensure_all_required_properties_provided(&class_properties, &unused_property_id_references_with_same_owner_flag_set)?;
- // Create wrapper structure from provided new_property_value_references_with_same_owner_flag_set and their corresponding Class properties
- let new_values_for_existing_properties = InputValuesForExistingProperties::from(
- &class_properties, &new_property_value_references_with_same_owner_flag_set
- )?;
- // Ensure all provided `new_property_value_references_with_same_owner_flag_set` are valid
- Self::ensure_are_valid_references_with_same_owner_flag_set(
- new_values_for_existing_properties, &new_controller
- )?;
- let new_output_property_value_references_with_same_owner_flag_set = Self::make_output_property_values(new_property_value_references_with_same_owner_flag_set);
- // Compute StoredPropertyValues, which respective Properties have unique flag set
- // (skip PropertyIds, which respective property values under this Entity are default and non required)
- let new_output_values_for_existing_properties =
- StoredValuesForExistingProperties::from(&class_properties, &new_output_property_value_references_with_same_owner_flag_set)?;
- // Compute new unique property value hashes.
- // Ensure new property value hashes with `unique` flag set are `unique` on `Class` level
- let new_unique_hashes = Self::ensure_new_property_values_respect_uniquness(
- class_id, new_output_values_for_existing_properties,
- )?;
- //
- // == MUTATION SAFE ==
- //
- // Used to compute old unique hashes, that should be substituted with new ones.
- let old_unique_hashes =
- Self::compute_old_unique_hashes(&new_output_property_value_references_with_same_owner_flag_set, &entity_property_values);
- // Add property values, that should be unique on Class level
- Self::add_unique_property_value_hashes(class_id, new_unique_hashes);
- // Remove unique hashes, that were substituted with new ones.
- Self::remove_unique_property_value_hashes(class_id, old_unique_hashes);
- // Make updated entity_property_values from parameters provided
- let entity_property_values_updated =
- Self::make_updated_property_value_references_with_same_owner_flag_set(
- unused_property_id_references_with_same_owner_flag_set, &entity_property_values,
- &new_output_property_value_references_with_same_owner_flag_set,
- );
- // Transfer entity ownership
- let entities_inbound_rcs_delta = if let Some(entity_property_values_updated) = entity_property_values_updated {
- // Calculate entities reference counter side effects for current operation
- let entities_inbound_rcs_delta =
- Self::get_updated_inbound_rcs_delta(
- entity_id, class_properties, entity_property_values, new_output_property_value_references_with_same_owner_flag_set
- )?;
- // Update InboundReferenceCounter, based on previously calculated ReferenceCounterSideEffects, for each Entity involved
- Self::update_entities_rcs(&entities_inbound_rcs_delta);
- <EntityById<T>>::mutate(entity_id, |entity| {
- // Update current Entity property values with updated ones
- entity.set_values(entity_property_values_updated);
- // Set up new controller for the current Entity instance
- entity.get_permissions_mut().set_conroller(new_controller.clone());
- });
- entities_inbound_rcs_delta
- } else {
- // Set up new controller for the current Entity instance
- <EntityById<T>>::mutate(entity_id, |entity| {
- entity.get_permissions_mut().set_conroller(new_controller.clone());
- });
- None
- };
- // Trigger event
- Self::deposit_event(RawEvent::EntityOwnershipTransfered(entity_id, new_controller, entities_inbound_rcs_delta));
- Ok(())
- }
- // ======
- // The next set of extrinsics can be invoked by anyone who can properly sign for provided value of `Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>`.
- // ======
- /// Create entity.
- /// If someone is making an entity of this class for first time,
- /// then a voucher is also added with the class limit as the default limit value.
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn create_entity(
- origin,
- class_id: T::ClassId,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Ensure Class under given id exists, return corresponding one
- let class = Self::ensure_class_exists(class_id)?;
- // Ensure maximum entities limit per class not reached
- class.ensure_maximum_entities_count_limit_not_reached::<T>()?;
- let class_permissions = class.get_permissions_ref();
- // Ensure entities creation is not blocked on Class level
- class_permissions.ensure_entity_creation_not_blocked::<T>()?;
- // Ensure actor can create entities
- Self::ensure_can_create_entities(&class_permissions, &account_id, &actor)?;
- let entity_controller = EntityController::<T::MemberId>::from_actor::<T>(&actor);
- // Check if entity creation voucher exists
- let voucher_exists = if <EntityCreationVouchers<T>>::contains_key(class_id, &entity_controller) {
- // Ensure voucher limit not reached
- Self::entity_creation_vouchers(class_id, &entity_controller).ensure_voucher_limit_not_reached()?;
- true
- } else {
- false
- };
- //
- // == MUTATION SAFE ==
- //
- // Create voucher, update if exists
- if voucher_exists {
- // Increment number of created entities count, if specified voucher already exist
- <EntityCreationVouchers<T>>::mutate(class_id, &entity_controller, |entity_creation_voucher| {
- entity_creation_voucher.increment_created_entities_count()
- });
- } else {
- // Create new voucher for given entity creator with default limit
- let mut entity_creation_voucher = EntityCreationVoucher::new(class.get_default_entity_creation_voucher_upper_bound());
- // Increase created entities count by 1 to maintain valid entity_creation_voucher state after following Entity added
- entity_creation_voucher.increment_created_entities_count();
- <EntityCreationVouchers<T>>::insert(class_id, entity_controller.clone(), entity_creation_voucher);
- }
- // Create new entity
- let entity_id = Self::next_entity_id();
- let new_entity = Entity::<T::ClassId, T::MemberId, T::Hash, T::EntityId, T::Nonce>::new(
- entity_controller,
- class_id,
- BTreeSet::new(),
- BTreeMap::new(),
- );
- // Save newly created entity:
- <EntityById<T>>::insert(entity_id, new_entity);
- // Increment the next entity id:
- <NextEntityId<T>>::mutate(|n| *n += T::EntityId::one());
- // Increment number of entities, associated with this class
- <ClassById<T>>::mutate(class_id, |class| {
- class.increment_entities_count();
- });
- // Trigger event
- Self::deposit_event(RawEvent::EntityCreated(actor, entity_id));
- Ok(())
- }
- /// Remove `Entity` under provided `entity_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn remove_entity(
- origin,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- entity_id: T::EntityId,
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Retrieve Class, Entity and EntityAccessLevel for the actor, attemting to perform operation
- let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(account_id, entity_id, &actor)?;
- // Ensure actor with given EntityAccessLevel can remove entity
- EntityPermissions::<T::MemberId>::ensure_group_can_remove_entity::<T>(access_level)?;
- // Ensure any inbound InputPropertyValue::Reference points to the given Entity
- entity.ensure_rc_is_zero::<T>()?;
- let class_properties = class.get_properties();
- let class_id = entity.get_class_id();
- let entity_values = entity.get_values();
- let unique_property_value_hashes = match StoredValuesForExistingProperties::<T>::from(&class_properties, &entity_values) {
- Ok(values_for_existing_properties) => values_for_existing_properties.compute_unique_hashes(),
- Err(e) => {
- debug_assert!(false, "Should not fail! {:?}", e);
- return Err(e.into())
- }
- };
- //
- // == MUTATION SAFE ==
- //
- // Remove property value entries, that should be unique on Class level
- Self::remove_unique_property_value_hashes(class_id, unique_property_value_hashes);
- // Remove entity
- <EntityById<T>>::remove(entity_id);
- // Decrement class entities counter
- <ClassById<T>>::mutate(class_id, |class| class.decrement_entities_count());
- let entity_controller = EntityController::<T::MemberId>::from_actor::<T>(&actor);
- // Decrement entity_creation_voucher after entity removal perfomed
- <EntityCreationVouchers<T>>::mutate(class_id, entity_controller, |entity_creation_voucher| {
- entity_creation_voucher.decrement_created_entities_count();
- });
- // Trigger event
- Self::deposit_event(RawEvent::EntityRemoved(actor, entity_id));
- Ok(())
- }
- /// Add schema support to entity under given `schema_id` and provided `property_values`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn add_schema_support_to_entity(
- origin,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- entity_id: T::EntityId,
- schema_id: SchemaId,
- new_property_values: BTreeMap<PropertyId, InputPropertyValue<T>>
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Retrieve Class, Entity and EntityAccessLevel for the actor, attemting to perform operation
- let (class, entity, _) = Self::ensure_class_entity_and_access_level(account_id, entity_id, &actor)?;
- // Ensure Class Schema under given index exists, return corresponding Schema
- let schema = class.ensure_schema_exists::<T>(schema_id)?.to_owned();
- let class_properties = class.get_properties();
- // Create wrapper structure from provided new_property_values and their corresponding Class properties
- let new_values_for_existing_properties = InputValuesForExistingProperties::from(&class_properties, &new_property_values)?;
- // Ensure Schema under given id is not added to given Entity yet
- entity.ensure_schema_id_is_not_added::<T>(schema_id)?;
- // Ensure provided new_property_values are not added to the Entity values map yet
- entity.ensure_property_values_are_not_added(&new_property_values)?;
- // Ensure provided schema can be added to the Entity
- schema.ensure_is_active::<T>()?;
- // Ensure all provided new property values are for properties in the given schema
- schema.ensure_has_properties(&new_property_values)?;
- // Retrieve Schema property ids, which are not provided in new_property_values
- let unused_schema_property_ids = Self::compute_unused_property_ids(&new_property_values, schema.get_properties());
- // Perform checks to ensure all required property_values under provided unused_schema_property_ids provided
- Self::ensure_all_required_properties_provided(&class_properties, &unused_schema_property_ids)?;
- // Ensure all property_values under given Schema property ids are valid
- let entity_controller = entity.get_permissions_ref().get_controller();
- // Validate all values, provided in new_values_for_existing_properties,
- // against the type of its Property and check any additional constraints
- Self::ensure_property_values_are_valid(&entity_controller, &new_values_for_existing_properties)?;
- let class_id = entity.get_class_id();
- let entity_property_values = entity.get_values();
- let new_output_property_values = Self::make_output_property_values(new_property_values);
- // Compute updated entity values, after new schema support added
- let entity_values_updated = Self::make_updated_entity_property_values(
- schema, entity_property_values, &new_output_property_values
- );
- let new_output_values_for_existing_properties = StoredValuesForExistingProperties::from(&class_properties, &new_output_property_values)?;
- // Retrieve StoredPropertyValues, which respective Properties have unique flag set
- // (skip PropertyIds, which respective property values under this Entity are default and non required)
- let new_unique_property_value_hashes = new_output_values_for_existing_properties.compute_unique_hashes();
- // Ensure all provided Properties with unique flag set are unique on Class level
- Self::ensure_property_value_hashes_unique_option_satisfied(class_id, &new_unique_property_value_hashes)?;
- //
- // == MUTATION SAFE ==
- //
- // Add property value hashes, that should be unique on Class level
- Self::add_unique_property_value_hashes(class_id, new_unique_property_value_hashes);
- // Calculate entities reference counter side effects for current operation
- let entities_inbound_rcs_delta = Self::calculate_entities_inbound_rcs_delta(
- entity_id, new_output_values_for_existing_properties, DeltaMode::Increment
- );
- // Update InboundReferenceCounter, based on previously calculated entities_inbound_rcs_delta, for each Entity involved
- Self::update_entities_rcs(&entities_inbound_rcs_delta);
- // Add schema support to `Entity` under given `entity_id`
- <EntityById<T>>::mutate(entity_id, |entity| {
- // Add a new schema to the list of schemas supported by this entity.
- entity.get_supported_schemas_mut().insert(schema_id);
- // Update entity values only if new properties have been added.
- if entity_values_updated.len() > entity.get_values_ref().len() {
- entity.set_values(entity_values_updated);
- }
- });
- // Trigger event
- Self::deposit_event(RawEvent::EntitySchemaSupportAdded(actor, entity_id, schema_id, entities_inbound_rcs_delta));
- Ok(())
- }
- /// Update `Entity` `InputPropertyValue`'s with provided ones
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn update_entity_property_values(
- origin,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- entity_id: T::EntityId,
- new_property_values: BTreeMap<PropertyId, InputPropertyValue<T>>
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Retrieve Class, Entity and EntityAccessLevel for the actor, attemting to perform operation
- let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(account_id, entity_id, &actor)?;
- // Ensure property values were not locked on Class level
- class.ensure_property_values_unlocked::<T>()?;
- let entity_values_ref = entity.get_values_ref();
- // Filter new_property_values, that are identical to entity_property_values.
- // Get `new_property_values`, that are not in `entity_property_values`
- let new_property_values = Self::try_filter_identical_property_values(entity_values_ref, new_property_values);
- // Ensure all provided new_property_values are already added to the current Entity instance
- Self::ensure_all_property_values_are_already_added(entity_values_ref, &new_property_values)?;
- let class_properties = class.get_properties();
- // Create wrapper structure from new_property_values and their corresponding Class properties
- let new_values_for_existing_properties = InputValuesForExistingProperties::from(&class_properties, &new_property_values)?;
- // Ensure all provided property values are unlocked for the actor with given access_level
- Self::ensure_all_property_values_are_unlocked_from(&new_values_for_existing_properties, access_level)?;
- let entity_controller = entity.get_permissions_ref().get_controller();
- // Validate all values, provided in values_for_existing_properties,
- // against the type of its Property and check any additional constraints
- Self::ensure_property_values_are_valid(&entity_controller, &new_values_for_existing_properties)?;
- let class_id = entity.get_class_id();
- // Get current property values of an Entity
- let entity_property_values = entity.get_values();
- let new_output_property_values = Self::make_output_property_values(new_property_values);
- // Compute StoredPropertyValues, which respective Properties have unique flag set
- // (skip PropertyIds, which respective property values under this Entity are default and non required)
- let new_output_values_for_existing_properties =
- StoredValuesForExistingProperties::from(&class_properties, &new_output_property_values)?;
- // Compute new unique property value hashes.
- // Ensure new property value hashes with `unique` flag set are `unique` on `Class` level
- let new_unique_hashes = Self::ensure_new_property_values_respect_uniquness(
- class_id, new_output_values_for_existing_properties,
- )?;
- //
- // == MUTATION SAFE ==
- //
- // Used to compute old unique hashes, that should be substituted with new ones.
- let old_unique_hashes =
- Self::compute_old_unique_hashes(&new_output_property_values, &entity_property_values);
- // Add property value hashes, that should be unique on Class level
- Self::add_unique_property_value_hashes(class_id, new_unique_hashes);
- // Remove unique hashes, that were substituted with new ones. (if some).
- Self::remove_unique_property_value_hashes(class_id, old_unique_hashes);
- // Make updated entity_property_values from current entity_property_values and new_output_property_values provided
- let entity_property_values_updated =
- Self::make_updated_property_values(&entity_property_values, &new_output_property_values);
- // If property values should be updated
- if let Some(entity_property_values_updated) = entity_property_values_updated {
- // Calculate entities reference counter side effects for current operation (should always be safe)
- let entities_inbound_rcs_delta =
- Self::get_updated_inbound_rcs_delta(entity_id, class_properties, entity_property_values, new_output_property_values)?;
- // Update InboundReferenceCounter, based on previously calculated entities_inbound_rcs_delta, for each Entity involved
- Self::update_entities_rcs(&entities_inbound_rcs_delta);
- // Update entity property values
- <EntityById<T>>::mutate(entity_id, |entity| {
- entity.set_values(entity_property_values_updated);
- });
- // Trigger event
- Self::deposit_event(RawEvent::EntityPropertyValuesUpdated(actor, entity_id, entities_inbound_rcs_delta));
- }
- Ok(())
- }
- /// Clear `PropertyValueVec` under given `entity_id` & `in_class_schema_property_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn clear_entity_property_vector(
- origin,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- entity_id: T::EntityId,
- in_class_schema_property_id: PropertyId
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Retrieve Class, Entity and EntityAccessLevel for the actor, attemting to perform operation
- let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(account_id, entity_id, &actor)?;
- // Ensure Property under given PropertyId is unlocked from actor with given EntityAccessLevel
- // Retrieve corresponding Property by value
- let property = class.ensure_class_property_type_unlocked_from::<T>(
- in_class_schema_property_id,
- access_level,
- )?;
- // Ensure InputPropertyValue under given in_class_schema_property_id is Vector
- let property_value_vector =
- entity.ensure_property_value_is_vec::<T>(in_class_schema_property_id)?;
- // Calculate side effects for clear_property_vector operation, based on property_value_vector provided and its respective property.
- let entities_inbound_rcs_delta = Self::make_side_effects_for_clear_property_vector_operation(&property_value_vector, &property);
- // Clear property_value_vector.
- let empty_property_value_vector = Self::clear_property_vector(property_value_vector.clone());
- let class_id = entity.get_class_id();
- // Compute old and new vec unique property value hash.
- // Ensure new property value hash with `unique` flag set is `unique` on `Class` level
- let vec_property_value_hashes = if property.unique {
- Some(
- Self::ensure_vec_property_value_hashes(class_id, in_class_schema_property_id, &empty_property_value_vector, property_value_vector)?
- )
- } else {
- None
- };
- //
- // == MUTATION SAFE ==
- //
- if let Some((new_property_value_hash, old_property_value_hash)) = vec_property_value_hashes {
- // Add property value hash, that should be unique on `Class` level
- Self::add_unique_property_value_hash(class_id, in_class_schema_property_id, new_property_value_hash);
- // Remove property value hash, that should be unique on `Class` level
- Self::remove_unique_property_value_hash(class_id, in_class_schema_property_id, old_property_value_hash);
- }
- // Decrease reference counters of involved entities (if some)
- Self::update_entities_rcs(&entities_inbound_rcs_delta);
- // Insert empty_property_value_vector into entity_property_values mapping at in_class_schema_property_id.
- // Retrieve updated entity_property_values
- let entity_values_updated = Self::insert_at_in_class_schema_property_id(
- entity.get_values(), in_class_schema_property_id, empty_property_value_vector
- );
- // Update entity property values
- <EntityById<T>>::mutate(entity_id, |entity| {
- entity.set_values(entity_values_updated);
- });
- // Trigger event
- Self::deposit_event(
- RawEvent::VectorCleared(
- actor, entity_id, in_class_schema_property_id, entities_inbound_rcs_delta
- )
- );
- Ok(())
- }
- /// Remove value at given `index_in_property_vector`
- /// from `PropertyValueVec` under `in_class_schema_property_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn remove_at_entity_property_vector(
- origin,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- entity_id: T::EntityId,
- in_class_schema_property_id: PropertyId,
- index_in_property_vector: VecMaxLength,
- nonce: T::Nonce
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Retrieve Class, Entity and EntityAccessLevel for the actor, attemting to perform operation
- let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(account_id, entity_id, &actor)?;
- // Ensure Property under given PropertyId is unlocked from actor with given EntityAccessLevel
- // Retrieve corresponding Property by value
- let property = class.ensure_class_property_type_unlocked_from::<T>(
- in_class_schema_property_id,
- access_level,
- )?;
- // Ensure InputPropertyValue under given in_class_schema_property_id is Vector
- let property_value_vector =
- entity.ensure_property_value_is_vec::<T>(in_class_schema_property_id)?;
- // Ensure `VecInputPropertyValue` nonce is equal to the provided one.
- // Used to to avoid possible data races, when performing vector specific operations
- property_value_vector.ensure_nonce_equality::<T>(nonce)?;
- // Ensure, provided index_in_property_vec is valid index of VecInputValue
- property_value_vector
- .ensure_index_in_property_vector_is_valid::<T>(index_in_property_vector)?;
- let involved_entity_id = property_value_vector
- .get_vec_value_ref()
- .get_involved_entities()
- .and_then(|involved_entities| involved_entities.get(index_in_property_vector as usize).copied());
- // Remove value at in_class_schema_property_id in property value vector
- // Get VecInputPropertyValue wrapped in InputPropertyValue
- let property_value_vector_updated = Self::remove_at_index_in_property_vector(
- property_value_vector.clone(), index_in_property_vector
- );
- let class_id = entity.get_class_id();
- // Compute old and new vec unique property value hash.
- // Ensure new property value hash with `unique` flag set is `unique` on `Class` level
- let vec_property_value_hashes = if property.unique {
- Some(
- Self::ensure_vec_property_value_hashes(class_id, in_class_schema_property_id, &property_value_vector_updated, property_value_vector)?
- )
- } else {
- None
- };
- //
- // == MUTATION SAFE ==
- //
- if let Some((new_property_value_hash, old_property_value_hash)) = vec_property_value_hashes {
- // Add property value hash, that should be unique on `Class` level
- Self::add_unique_property_value_hash(class_id, in_class_schema_property_id, new_property_value_hash);
- // Remove property value hash, that should be unique on `Class` level
- Self::remove_unique_property_value_hash(class_id, in_class_schema_property_id, old_property_value_hash);
- }
- // Insert updated propery value into entity_property_values mapping at in_class_schema_property_id.
- let entity_values_updated = Self::insert_at_in_class_schema_property_id(
- entity.get_values(), in_class_schema_property_id, property_value_vector_updated
- );
- let involved_entity_and_side_effect = if let Some(involved_entity_id) = involved_entity_id {
- // Decrease reference counter of involved entity (if some)
- let same_controller_status = property.property_type.same_controller_status();
- let rc_delta = EntityReferenceCounterSideEffect::atomic(same_controller_status, DeltaMode::Decrement);
- // Update InboundReferenceCounter of involved entity, based on previously calculated rc_delta
- Self::update_entity_rc(involved_entity_id, rc_delta);
- Some((involved_entity_id, rc_delta))
- } else {
- None
- };
- // Update entity property values
- <EntityById<T>>::mutate(entity_id, |entity| {
- entity.set_values(entity_values_updated);
- });
- // Trigger event
- Self::deposit_event(
- RawEvent::RemovedAtVectorIndex(
- actor, entity_id, in_class_schema_property_id, index_in_property_vector,
- nonce + T::Nonce::one(), involved_entity_and_side_effect
- )
- );
- Ok(())
- }
- /// Insert `SingleInputPropertyValue` at given `index_in_property_vector`
- /// into `PropertyValueVec` under `in_class_schema_property_id`
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn insert_at_entity_property_vector(
- origin,
- actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- entity_id: T::EntityId,
- in_class_schema_property_id: PropertyId,
- index_in_property_vector: VecMaxLength,
- value: InputValue<T>,
- nonce: T::Nonce
- ) -> DispatchResult {
- let account_id = ensure_signed(origin)?;
- // Retrieve Class, Entity and EntityAccessLevel for the actor, attemting to perform operation
- let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(account_id, entity_id, &actor)?;
- // Ensure Property under given PropertyId is unlocked from actor with given EntityAccessLevel
- // Retrieve corresponding Property by value
- let property = class.ensure_class_property_type_unlocked_from::<T>(
- in_class_schema_property_id,
- access_level,
- )?;
- // Ensure InputPropertyValue under given in_class_schema_property_id is Vector
- let property_value_vector =
- entity.ensure_property_value_is_vec::<T>(in_class_schema_property_id)?;
- // Ensure `VecInputPropertyValue` nonce is equal to the provided one.
- // Used to to avoid possible data races, when performing vector specific operations
- property_value_vector.ensure_nonce_equality::<T>(nonce)?;
- let entity_controller = entity.get_permissions_ref().get_controller();
- // Ensure property_value type is equal to the property_value_vector type and check all constraints
- Property::<T::ClassId>::ensure_property_value_can_be_inserted_at_property_vector(
- &property,
- &value,
- &property_value_vector,
- index_in_property_vector,
- entity_controller,
- )?;
- let involved_entity = value.get_involved_entity();
- // Insert SingleInputPropertyValue at in_class_schema_property_id into property value vector
- // Get VecInputPropertyValue wrapped in InputPropertyValue
- let property_value_vector_updated = Self::insert_at_index_in_property_vector(
- property_value_vector.clone(), index_in_property_vector, value
- );
- let class_id = entity.get_class_id();
- // Compute old and new vec unique property value hash.
- // Ensure new property value hash with `unique` flag set is `unique` on `Class` level
- let vec_property_value_hashes = if property.unique {
- Some(
- Self::ensure_vec_property_value_hashes(class_id, in_class_schema_property_id, &property_value_vector_updated, property_value_vector)?
- )
- } else {
- None
- };
- //
- // == MUTATION SAFE ==
- //
- if let Some((new_property_value_hash, old_property_value_hash)) = vec_property_value_hashes {
- // Add property value hash, that should be unique on `Class` level
- Self::add_unique_property_value_hash(class_id, in_class_schema_property_id, new_property_value_hash);
- // Remove property value hash, that should be unique on `Class` level
- Self::remove_unique_property_value_hash(class_id, in_class_schema_property_id, old_property_value_hash);
- }
- // Insert updated property value into entity_property_values mapping at in_class_schema_property_id.
- // Retrieve updated entity_property_values
- let entity_values_updated = Self::insert_at_in_class_schema_property_id(
- entity.get_values(), in_class_schema_property_id, property_value_vector_updated
- );
- // Increase reference counter of involved entity (if some)
- let involved_entity_and_side_effect = if let Some(entity_rc_to_increment) = involved_entity {
- let same_controller_status = property.property_type.same_controller_status();
- let rc_delta = EntityReferenceCounterSideEffect::atomic(same_controller_status, DeltaMode::Increment);
- // Update InboundReferenceCounter of involved entity, based on previously calculated ReferenceCounterSideEffect
- Self::update_entity_rc(entity_rc_to_increment, rc_delta);
- Some((entity_rc_to_increment, rc_delta))
- } else {
- None
- };
- // Update entity property values
- <EntityById<T>>::mutate(entity_id, |entity| {
- entity.set_values(entity_values_updated);
- });
- // Trigger event
- Self::deposit_event(
- RawEvent::InsertedAtVectorIndex(
- actor, entity_id, in_class_schema_property_id, index_in_property_vector,
- nonce + T::Nonce::one(), involved_entity_and_side_effect
- )
- );
- Ok(())
- }
- /// Batch transaction
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn transaction(origin, actor: Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>, operations: Vec<OperationType<T>>) -> DispatchResult {
- // Ensure maximum number of operations during atomic batching limit not reached
- Self::ensure_number_of_operations_during_atomic_batching_limit_not_reached(&operations)?;
- //
- // == MUTATION SAFE ==
- //
- // This BTreeMap holds the T::EntityId of the entity created as a result of executing a `CreateEntity` `Operation`
- let mut entity_created_in_operation = BTreeMap::new();
- // Create raw origin
- let raw_origin = origin.into().map_err(|_| Error::<T>::OriginCanNotBeMadeIntoRawOrigin)?;
- for (index, operation_type) in operations.into_iter().enumerate() {
- let origin = T::Origin::from(raw_origin.clone());
- let actor = actor.clone();
- match operation_type {
- OperationType::CreateEntity(create_entity_operation) => {
- Self::create_entity(origin, create_entity_operation.class_id, actor)?;
- // entity id of newly created entity
- let entity_id = Self::next_entity_id() - T::EntityId::one();
- entity_created_in_operation.insert(index, entity_id);
- },
- OperationType::AddSchemaSupportToEntity(add_schema_support_to_entity_operation) => {
- let entity_id = operations::parametrized_entity_to_entity_id(
- &entity_created_in_operation, add_schema_support_to_entity_operation.entity_id
- )?;
- let schema_id = add_schema_support_to_entity_operation.schema_id;
- let property_values = operations::parametrized_property_values_to_property_values(
- &entity_created_in_operation, add_schema_support_to_entity_operation.parametrized_property_values
- )?;
- Self::add_schema_support_to_entity(origin, actor, entity_id, schema_id, property_values)?;
- },
- OperationType::UpdatePropertyValues(update_property_values_operation) => {
- let entity_id = operations::parametrized_entity_to_entity_id(
- &entity_created_in_operation, update_property_values_operation.entity_id
- )?;
- let property_values = operations::parametrized_property_values_to_property_values(
- &entity_created_in_operation, update_property_values_operation.new_parametrized_property_values
- )?;
- Self::update_entity_property_values(origin, actor, entity_id, property_values)?;
- },
- }
- }
- // Trigger event
- Self::deposit_event(RawEvent::TransactionCompleted(actor));
- Ok(())
- }
- }
- }
- impl<T: Trait> Module<T> {
- /// Updates corresponding `Entity` `reference_counter` by `reference_counter_delta`.
- fn update_entity_rc(
- entity_id: T::EntityId,
- reference_counter_delta: EntityReferenceCounterSideEffect,
- ) {
- // Update both `total` and `same owner` number of inbound references for the Entity instance under given `entity_id`
- <EntityById<T>>::mutate(entity_id, |entity| {
- let entity_inbound_rc = entity.get_reference_counter_mut();
- entity_inbound_rc.total =
- (entity_inbound_rc.total as i32 + reference_counter_delta.total) as u32;
- entity_inbound_rc.same_owner =
- (entity_inbound_rc.same_owner as i32 + reference_counter_delta.same_owner) as u32;
- })
- }
- /// Add property value hash, that should be unique on `Class` level
- pub fn add_unique_property_value_hash(
- class_id: T::ClassId,
- property_id: PropertyId,
- hash: T::Hash,
- ) {
- <UniquePropertyValueHashes<T>>::insert((class_id, property_id), hash, ());
- }
- /// Remove property value hash, that should be unique on `Class` level
- pub fn remove_unique_property_value_hash(
- class_id: T::ClassId,
- property_id: PropertyId,
- hash: T::Hash,
- ) {
- <UniquePropertyValueHashes<T>>::remove((class_id, property_id), hash);
- }
- /// Add property value hashes, that should be unique on `Class` level
- pub fn add_unique_property_value_hashes(
- class_id: T::ClassId,
- unique_property_value_hashes: BTreeMap<PropertyId, T::Hash>,
- ) {
- unique_property_value_hashes
- .into_iter()
- .for_each(|(property_id, hash)| {
- Self::add_unique_property_value_hash(class_id, property_id, hash);
- });
- }
- /// Remove property value hashes, that should be unique on `Class` level
- pub fn remove_unique_property_value_hashes(
- class_id: T::ClassId,
- unique_property_value_hashes: BTreeMap<PropertyId, T::Hash>,
- ) {
- unique_property_value_hashes
- .into_iter()
- .for_each(|(property_id, hash)| {
- Self::remove_unique_property_value_hash(class_id, property_id, hash);
- });
- }
- /// Convert all provided `InputPropertyValue`'s into `StoredPropertyValue`'s
- pub fn make_output_property_values(
- input_property_values: BTreeMap<PropertyId, InputPropertyValue<T>>,
- ) -> BTreeMap<PropertyId, StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>> {
- input_property_values
- .into_iter()
- .map(|(property_id, property_value)| (property_id, property_value.into()))
- .collect()
- }
- /// Update `entity_property_values` with `property_values`
- /// Returns updated `entity_property_values`
- fn make_updated_entity_property_values(
- schema: Schema,
- entity_property_values: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- output_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- ) -> BTreeMap<PropertyId, StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>> {
- // Concatenate existing `entity_property_values` with `property_values`, provided, when adding `Schema` support.
- let updated_entity_property_values: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- > = entity_property_values
- .into_iter()
- .chain(output_property_values.to_owned().into_iter())
- .collect();
- // Write all missing non required `Schema` `property_values` as `InputPropertyValue::default()`
- let non_required_property_values: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- > = schema
- .get_properties()
- .iter()
- .filter_map(|property_id| {
- if !updated_entity_property_values.contains_key(property_id) {
- Some((*property_id, StoredPropertyValue::default()))
- } else {
- None
- }
- })
- .collect();
- // Extend updated_entity_property_values with given Schema non_required_property_values
- updated_entity_property_values
- .into_iter()
- .chain(non_required_property_values.into_iter())
- .collect()
- }
- /// Calculate side effects for clear_property_vector operation, based on `property_value_vector` provided and its respective `property`.
- /// Returns calculated `ReferenceCounterSideEffects`
- pub fn make_side_effects_for_clear_property_vector_operation(
- property_value_vector: &VecStoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- property: &Property<T::ClassId>,
- ) -> Option<ReferenceCounterSideEffects<T>> {
- let entity_ids_to_decrease_rc = property_value_vector
- .get_vec_value_ref()
- .get_involved_entities();
- if let Some(entity_ids_to_decrease_rcs) = entity_ids_to_decrease_rc {
- // Calculate `ReferenceCounterSideEffects`, based on entity_ids involved, same_controller_status and chosen `DeltaMode`
- let same_controller_status = property.property_type.same_controller_status();
- let entities_inbound_rcs_delta = Self::perform_entities_inbound_rcs_delta_calculation(
- ReferenceCounterSideEffects::<T>::default(),
- entity_ids_to_decrease_rcs,
- same_controller_status,
- DeltaMode::Decrement,
- );
- if !entities_inbound_rcs_delta.is_empty() {
- Some(entities_inbound_rcs_delta)
- } else {
- None
- }
- } else {
- None
- }
- }
- /// Update `inbound_rcs_delta`, based on `involved_entity_ids`, `same_controller_status` provided and chosen `DeltaMode`
- /// Returns updated `inbound_rcs_delta`
- fn perform_entities_inbound_rcs_delta_calculation(
- mut inbound_rcs_delta: ReferenceCounterSideEffects<T>,
- involved_entity_ids: Vec<T::EntityId>,
- same_controller_status: bool,
- delta_mode: DeltaMode,
- ) -> ReferenceCounterSideEffects<T> {
- for involved_entity_id in involved_entity_ids {
- // If inbound_rcs_delta already contains entry for the given involved_entity_id, increment it
- // with atomic EntityReferenceCounterSideEffect instance, based on same_owner flag provided and DeltaMode,
- // otherwise create new atomic EntityReferenceCounterSideEffect instance
- if let Some(inbound_rc_delta) = inbound_rcs_delta.get_mut(&involved_entity_id) {
- *inbound_rc_delta +=
- EntityReferenceCounterSideEffect::atomic(same_controller_status, delta_mode);
- } else {
- inbound_rcs_delta.insert(
- involved_entity_id,
- EntityReferenceCounterSideEffect::atomic(same_controller_status, delta_mode),
- );
- }
- }
- inbound_rcs_delta
- }
- /// Filter references, pointing to the same `Entity`
- fn filter_references_to_the_same_entity(
- current_entity_id: T::EntityId,
- involved_entity_ids: Vec<T::EntityId>,
- ) -> Vec<T::EntityId> {
- involved_entity_ids
- .into_iter()
- .filter(|involved_entity_id| current_entity_id != *involved_entity_id)
- .collect()
- }
- /// Calculate `ReferenceCounterSideEffects`, based on `values_for_existing_properties` provided and chosen `DeltaMode`
- /// Returns calculated `ReferenceCounterSideEffects`
- fn calculate_entities_inbound_rcs_delta(
- current_entity_id: T::EntityId,
- values_for_existing_properties: StoredValuesForExistingProperties<T>,
- delta_mode: DeltaMode,
- ) -> Option<ReferenceCounterSideEffects<T>> {
- let entities_inbound_rcs_delta = values_for_existing_properties
- .values()
- .map(|value_for_existing_property| value_for_existing_property.unzip())
- .filter_map(|(property, value)| {
- let involved_entity_ids =
- value.get_involved_entities().map(|involved_entity_ids| {
- Self::filter_references_to_the_same_entity(
- current_entity_id,
- involved_entity_ids,
- )
- });
- match involved_entity_ids {
- Some(involved_entity_ids) if !involved_entity_ids.is_empty() => Some((
- involved_entity_ids,
- property.property_type.same_controller_status(),
- )),
- _ => None,
- }
- })
- // Aggeregate all sideffects on a single entity together into one side effect map
- .fold(
- ReferenceCounterSideEffects::default(),
- |inbound_rcs_delta, (involved_entity_ids, same_controller_status)| {
- Self::perform_entities_inbound_rcs_delta_calculation(
- inbound_rcs_delta,
- involved_entity_ids,
- same_controller_status,
- delta_mode,
- )
- },
- );
- if !entities_inbound_rcs_delta.is_empty() {
- Some(entities_inbound_rcs_delta)
- } else {
- None
- }
- }
- /// Compute `ReferenceCounterSideEffects`, based on `InputPropertyValue` `Reference`'s involved into update process.
- /// Returns updated `ReferenceCounterSideEffects`
- pub fn get_updated_inbound_rcs_delta(
- current_entity_id: T::EntityId,
- class_properties: Vec<Property<T::ClassId>>,
- entity_property_values: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- new_output_property_values: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- ) -> Result<Option<ReferenceCounterSideEffects<T>>, Error<T>> {
- // Filter entity_property_values to get only those, which will be substituted with new_property_values
- let entity_property_values_to_update: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- > = entity_property_values
- .into_iter()
- .filter(|(entity_id, _)| new_output_property_values.contains_key(entity_id))
- .collect();
- // Calculate entities reference counter side effects for update operation
- let stored_values_for_entity_property_values_to_update =
- match StoredValuesForExistingProperties::from(
- &class_properties,
- &entity_property_values_to_update,
- ) {
- Ok(stored_values_for_entity_property_values_to_update) => {
- stored_values_for_entity_property_values_to_update
- }
- Err(e) => {
- debug_assert!(false, "Should not fail! {:?}", e);
- return Err(e);
- }
- };
- // Calculate entities inbound reference counter delta with Decrement DeltaMode for entity_property_values_to_update,
- // as involved InputPropertyValue References will be substituted with new ones
- let decremental_reference_counter_side_effects = Self::calculate_entities_inbound_rcs_delta(
- current_entity_id,
- stored_values_for_entity_property_values_to_update,
- DeltaMode::Decrement,
- );
- // Calculate entities inbound reference counter delta with Increment DeltaMode for new_property_values,
- // as involved InputPropertyValue References will substitute the old ones
- let incremental_reference_counter_side_effects = Self::calculate_entities_inbound_rcs_delta(
- current_entity_id,
- StoredValuesForExistingProperties::from(
- &class_properties,
- &new_output_property_values,
- )?,
- DeltaMode::Increment,
- );
- // Add up both net decremental_reference_counter_side_effects and incremental_reference_counter_side_effects
- // to get one net sideffect per entity.
- Ok(Self::calculate_updated_inbound_rcs_delta(
- decremental_reference_counter_side_effects,
- incremental_reference_counter_side_effects,
- ))
- }
- /// Add up both net first_reference_counter_side_effects and second_reference_counter_side_effects (if some)
- /// to get one net sideffect per entity.
- /// Returns updated `ReferenceCounterSideEffects`
- pub fn calculate_updated_inbound_rcs_delta(
- first_reference_counter_side_effects: Option<ReferenceCounterSideEffects<T>>,
- second_reference_counter_side_effects: Option<ReferenceCounterSideEffects<T>>,
- ) -> Option<ReferenceCounterSideEffects<T>> {
- match (
- first_reference_counter_side_effects,
- second_reference_counter_side_effects,
- ) {
- (
- Some(first_reference_counter_side_effects),
- Some(second_reference_counter_side_effects),
- ) => {
- let reference_counter_side_effects = first_reference_counter_side_effects
- .update(second_reference_counter_side_effects);
- Some(reference_counter_side_effects)
- }
- (Some(first_reference_counter_side_effects), _) => {
- Some(first_reference_counter_side_effects)
- }
- (_, Some(second_reference_counter_side_effects)) => {
- Some(second_reference_counter_side_effects)
- }
- _ => None,
- }
- }
- /// Used to update `class_permissions` with parameters provided.
- /// Returns updated `class_permissions` if update performed
- pub fn make_updated_class_permissions(
- class_permissions: ClassPermissions<T::CuratorGroupId>,
- updated_any_member: Option<bool>,
- updated_entity_creation_blocked: Option<bool>,
- updated_all_entity_property_values_locked: Option<bool>,
- updated_maintainers: Option<BTreeSet<T::CuratorGroupId>>,
- ) -> Option<ClassPermissions<T::CuratorGroupId>> {
- // Used to check if update performed
- let mut updated_class_permissions = class_permissions.clone();
- if let Some(updated_any_member) = updated_any_member {
- updated_class_permissions.set_any_member_status(updated_any_member);
- }
- if let Some(updated_entity_creation_blocked) = updated_entity_creation_blocked {
- updated_class_permissions.set_entity_creation_blocked(updated_entity_creation_blocked);
- }
- if let Some(updated_all_entity_property_values_locked) =
- updated_all_entity_property_values_locked
- {
- updated_class_permissions
- .set_all_entity_property_values_locked(updated_all_entity_property_values_locked);
- }
- if let Some(updated_maintainers) = updated_maintainers {
- updated_class_permissions.set_maintainers(updated_maintainers);
- }
- if updated_class_permissions != class_permissions {
- Some(updated_class_permissions)
- } else {
- None
- }
- }
- /// Used to update `entity_permissions` with parameters provided.
- /// Returns updated `entity_permissions` if update performed
- pub fn make_updated_entity_permissions(
- entity_permissions: EntityPermissions<T::MemberId>,
- updated_frozen: Option<bool>,
- updated_referenceable: Option<bool>,
- ) -> Option<EntityPermissions<T::MemberId>> {
- // Used to check if update performed
- let mut updated_entity_permissions = entity_permissions.clone();
- if let Some(updated_frozen) = updated_frozen {
- updated_entity_permissions.set_frozen(updated_frozen);
- }
- if let Some(updated_referenceable) = updated_referenceable {
- updated_entity_permissions.set_referencable(updated_referenceable);
- }
- if updated_entity_permissions != entity_permissions {
- Some(updated_entity_permissions)
- } else {
- None
- }
- }
- /// Ensure property value hash with `unique` flag set is `unique` on `Class` level
- pub fn ensure_property_value_hash_unique_option_satisfied(
- class_id: T::ClassId,
- property_id: PropertyId,
- unique_property_value_hash: &T::Hash,
- ) -> Result<(), Error<T>> {
- ensure!(
- !<UniquePropertyValueHashes<T>>::contains_key(
- (class_id, property_id),
- unique_property_value_hash
- ),
- Error::<T>::PropertyValueShouldBeUnique
- );
- Ok(())
- }
- /// Ensure all property value hashes with `unique` flag set are `unique` on `Class` level
- pub fn ensure_property_value_hashes_unique_option_satisfied(
- class_id: T::ClassId,
- unique_property_value_hashes: &BTreeMap<PropertyId, T::Hash>,
- ) -> Result<(), Error<T>> {
- for (&property_id, unique_property_value_hash) in unique_property_value_hashes {
- Self::ensure_property_value_hash_unique_option_satisfied(
- class_id,
- property_id,
- unique_property_value_hash,
- )?;
- }
- Ok(())
- }
- /// Compute old and new vec unique property value hash.
- /// Ensure new property value hash with `unique` flag set is `unique` on `Class` level
- pub fn ensure_vec_property_value_hashes(
- class_id: T::ClassId,
- in_class_schema_property_id: PropertyId,
- property_value_vector_updated: &StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- property_value_vector: VecStoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- ) -> Result<(T::Hash, T::Hash), Error<T>> {
- // Compute new hash from unique property value and its respective property id
- let new_property_value_hash =
- property_value_vector_updated.compute_unique_hash::<T>(in_class_schema_property_id);
- // Ensure `Property` with `unique` flag set is `unique` on `Class` level
- Self::ensure_property_value_hash_unique_option_satisfied(
- class_id,
- in_class_schema_property_id,
- &new_property_value_hash,
- )?;
- // Compute old hash from the old unique property value and its respective property id
- let old_property_value_hash =
- property_value_vector.compute_unique_hash::<T>(in_class_schema_property_id);
- Ok((new_property_value_hash, old_property_value_hash))
- }
- /// Compute new unique property value hashes.
- /// Ensure new property value hashes with `unique` flag set are `unique` on `Class` level
- pub fn ensure_new_property_values_respect_uniquness(
- class_id: T::ClassId,
- new_output_values_for_existing_properties: StoredValuesForExistingProperties<T>,
- ) -> Result<BTreeMap<PropertyId, T::Hash>, Error<T>> {
- let new_unique_property_value_hashes =
- new_output_values_for_existing_properties.compute_unique_hashes();
- // Ensure all provided Properties with unique flag set are unique on Class level
- Self::ensure_property_value_hashes_unique_option_satisfied(
- class_id,
- &new_unique_property_value_hashes,
- )?;
- Ok(new_unique_property_value_hashes)
- }
- /// Returns the stored `Class` if exist, error otherwise.
- fn ensure_class_exists(
- class_id: T::ClassId,
- ) -> Result<Class<T::EntityId, T::ClassId, T::CuratorGroupId>, Error<T>> {
- ensure!(
- <ClassById<T>>::contains_key(class_id),
- Error::<T>::ClassNotFound
- );
- Ok(Self::class_by_id(class_id))
- }
- /// Returns `Class` and `Entity` under given id, if exists, and `EntityAccessLevel` corresponding to `origin`, if permitted
- fn ensure_class_entity_and_access_level(
- account_id: T::AccountId,
- entity_id: T::EntityId,
- actor: &Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- ) -> Result<
- (
- Class<T::EntityId, T::ClassId, T::CuratorGroupId>,
- Entity<T::ClassId, T::MemberId, T::Hash, T::EntityId, T::Nonce>,
- EntityAccessLevel,
- ),
- Error<T>,
- > {
- // Ensure Entity under given id exists, retrieve corresponding one
- let entity = Self::ensure_known_entity_id(entity_id)?;
- // Retrieve corresponding Class
- let class = Self::class_by_id(entity.get_class_id());
- // Derive EntityAccessLevel for the actor, attempting to act.
- let access_level = EntityAccessLevel::derive(
- &account_id,
- entity.get_permissions_ref(),
- class.get_permissions_ref(),
- actor,
- )?;
- Ok((class, entity, access_level))
- }
- /// Ensure `Entity` under given `entity_id` exists, retrieve corresponding `Entity` & `Class`
- pub fn ensure_known_entity_and_class(
- entity_id: T::EntityId,
- ) -> Result<
- (
- Entity<T::ClassId, T::MemberId, T::Hash, T::EntityId, T::Nonce>,
- Class<T::EntityId, T::ClassId, T::CuratorGroupId>,
- ),
- Error<T>,
- > {
- // Ensure Entity under given id exists, retrieve corresponding one
- let entity = Self::ensure_known_entity_id(entity_id)?;
- let class = ClassById::<T>::get(entity.get_class_id());
- Ok((entity, class))
- }
- /// Filter `provided values_for_existing_properties`, leaving only `Reference`'s with `SameOwner` flag set
- /// Returns the set of corresponding property ids
- pub fn get_property_id_references_with_same_owner_flag_set(
- values_for_existing_properties: StoredValuesForExistingProperties<T>,
- ) -> BTreeSet<PropertyId> {
- values_for_existing_properties
- // Iterate over the PropertyId's
- .keys()
- // Filter provided values_for_existing_properties, leaving only `Reference`'s with `SameOwner` flag set
- .filter(|property_id| {
- if let Some(value_for_existing_property) =
- values_for_existing_properties.get(property_id)
- {
- value_for_existing_property
- .get_property()
- .property_type
- .same_controller_status()
- } else {
- false
- }
- })
- .copied()
- .collect()
- }
- /// Ensure all ids of provided `new_property_value_references_with_same_owner_flag_set`
- /// corresponding to property ids of respective Class Property references with same owner flag set
- pub fn ensure_only_reference_ids_with_same_owner_flag_set_provided(
- entity_property_id_references_with_same_owner_flag_set: &BTreeSet<PropertyId>,
- new_property_value_references_with_same_owner_flag_set: &BTreeMap<
- PropertyId,
- InputPropertyValue<T>,
- >,
- ) -> Result<(), Error<T>> {
- let new_property_value_id_references_with_same_owner_flag_set: BTreeSet<PropertyId> =
- new_property_value_references_with_same_owner_flag_set
- .keys()
- .copied()
- .collect();
- ensure!(
- new_property_value_id_references_with_same_owner_flag_set
- .is_subset(entity_property_id_references_with_same_owner_flag_set),
- Error::<T>::AllProvidedPropertyValueIdsMustBeReferencesWithSameOwnerFlagSet
- );
- Ok(())
- }
- /// Ensure provided actor can create entities of current `Class`
- pub fn ensure_can_create_entities(
- class_permissions: &ClassPermissions<T::CuratorGroupId>,
- account_id: &T::AccountId,
- actor: &Actor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
- ) -> Result<(), Error<T>> {
- let can_create = match &actor {
- Actor::Lead => {
- // Ensure lead authorization performed succesfully
- ensure_lead_auth_success::<T>(account_id)?;
- true
- }
- Actor::Member(member_id) if class_permissions.any_member_status() => {
- // Ensure member authorization performed succesfully
- ensure_member_auth_success::<T>(member_id, account_id)?;
- true
- }
- Actor::Curator(curator_group_id, curator_id)
- if class_permissions.is_maintainer(curator_group_id) =>
- {
- // Authorize curator, performing all checks to ensure curator can act
- CuratorGroup::<T>::perform_curator_in_group_auth(
- curator_id,
- curator_group_id,
- account_id,
- )?;
- true
- }
- _ => false,
- };
- ensure!(can_create, Error::<T>::ActorCanNotCreateEntities);
- Ok(())
- }
- /// Ensure all provided `new_property_value_references_with_same_owner_flag_set` are valid
- fn ensure_are_valid_references_with_same_owner_flag_set(
- new_property_value_references_with_same_owner_flag_set: InputValuesForExistingProperties<T>,
- new_controller: &EntityController<T::MemberId>,
- ) -> Result<(), Error<T>> {
- for updated_value_for_existing_property in
- new_property_value_references_with_same_owner_flag_set.values()
- {
- let (property, value) = updated_value_for_existing_property.unzip();
- // Perform all required checks to ensure provided property values are valid references
- Property::<T::ClassId>::ensure_property_value_is_valid_reference(
- &property,
- value,
- new_controller,
- )?;
- }
- Ok(())
- }
- /// Used to update entity_property_values with parameters provided.
- /// Returns updated `entity_property_values`, if update performed
- pub fn make_updated_property_value_references_with_same_owner_flag_set(
- unused_property_id_references_with_same_owner_flag_set: BTreeSet<PropertyId>,
- entity_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- new_property_value_references_with_same_owner_flag_set: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- ) -> Option<BTreeMap<PropertyId, StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>>> {
- // Used to check if update performed
- let mut entity_property_values_updated = entity_property_values.clone();
- for (property_id, new_property_value_reference_with_same_owner_flag_set) in
- new_property_value_references_with_same_owner_flag_set
- {
- // Update entity_property_values map at property_id with new_property_value_reference_with_same_owner_flag_set
- entity_property_values_updated.insert(
- *property_id,
- new_property_value_reference_with_same_owner_flag_set.to_owned(),
- );
- }
- // Throw away old non required property value references with same owner flag set
- // and replace them with Default ones
- for unused_property_id_reference_with_same_owner_flag_set in
- unused_property_id_references_with_same_owner_flag_set
- {
- entity_property_values_updated.insert(
- unused_property_id_reference_with_same_owner_flag_set,
- StoredPropertyValue::default(),
- );
- }
- if *entity_property_values != entity_property_values_updated {
- Some(entity_property_values_updated)
- } else {
- None
- }
- }
- /// Update InboundReferenceCounter, based on previously calculated entities_inbound_rcs_delta, for each Entity involved
- pub fn update_entities_rcs(
- entities_inbound_rcs_delta: &Option<ReferenceCounterSideEffects<T>>,
- ) {
- if let Some(entities_inbound_rcs_delta) = entities_inbound_rcs_delta {
- entities_inbound_rcs_delta.update_entities_rcs();
- }
- }
- /// Retrieve `property_ids`, that are not in `property_values`
- pub fn compute_unused_property_ids(
- property_values: &BTreeMap<PropertyId, InputPropertyValue<T>>,
- property_ids: &BTreeSet<PropertyId>,
- ) -> BTreeSet<PropertyId> {
- let property_value_indices: BTreeSet<PropertyId> =
- property_values.keys().cloned().collect();
- property_ids
- .difference(&property_value_indices)
- .copied()
- .collect()
- }
- /// Used to compute old unique hashes, that should be substituted with new ones.
- pub fn compute_old_unique_hashes(
- new_output_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- entity_values: &BTreeMap<PropertyId, StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>>,
- ) -> BTreeMap<PropertyId, T::Hash> {
- entity_values
- .iter()
- .filter(|(property_id, _)| new_output_property_values.contains_key(property_id))
- .map(|(&property_id, property_value)| {
- (
- property_id,
- property_value.compute_unique_hash::<T>(property_id),
- )
- })
- .collect()
- }
- /// Perform checks to ensure all required `property_values` under provided `unused_schema_property_ids` provided
- pub fn ensure_all_required_properties_provided(
- class_properties: &[Property<T::ClassId>],
- unused_schema_property_ids: &BTreeSet<PropertyId>,
- ) -> Result<(), Error<T>> {
- for &unused_schema_property_id in unused_schema_property_ids {
- let class_property = &class_properties
- .get(unused_schema_property_id as usize)
- .ok_or(Error::<T>::ClassPropertyNotFound)?;
- // All required property values should be provided
- ensure!(
- !class_property.required,
- Error::<T>::MissingRequiredProperty
- );
- }
- Ok(())
- }
- /// Validate all values, provided in `values_for_existing_properties`, against the type of its `Property`
- /// and check any additional constraints
- pub fn ensure_property_values_are_valid(
- entity_controller: &EntityController<T::MemberId>,
- values_for_existing_properties: &InputValuesForExistingProperties<T>,
- ) -> Result<(), Error<T>> {
- for value_for_existing_property in values_for_existing_properties.values() {
- let (property, value) = value_for_existing_property.unzip();
- // Validate new InputPropertyValue against the type of this Property and check any additional constraints
- Property::<T::ClassId>::ensure_property_value_to_update_is_valid(
- property,
- value,
- entity_controller,
- )?;
- }
- Ok(())
- }
- /// Ensure all provided `new_property_values` are already exist in `entity_property_values` map
- pub fn ensure_all_property_values_are_already_added(
- entity_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- new_property_values: &BTreeMap<PropertyId, InputPropertyValue<T>>,
- ) -> Result<(), Error<T>> {
- ensure!(
- new_property_values
- .keys()
- .all(|key| entity_property_values.contains_key(key)),
- Error::<T>::UnknownEntityPropertyId
- );
- Ok(())
- }
- /// Ensure `new_values_for_existing_properties` are accessible for actor with given `access_level`
- pub fn ensure_all_property_values_are_unlocked_from(
- new_values_for_existing_properties: &InputValuesForExistingProperties<T>,
- access_level: EntityAccessLevel,
- ) -> Result<(), Error<T>> {
- for value_for_new_property in new_values_for_existing_properties.values() {
- // Ensure Property is unlocked from Actor with given EntityAccessLevel
- value_for_new_property
- .get_property()
- .ensure_unlocked_from::<T>(access_level)?;
- }
- Ok(())
- }
- /// Filter `new_property_values` identical to `entity_property_values`.
- /// Return only `new_property_values`, that are not in `entity_property_values`
- pub fn try_filter_identical_property_values(
- entity_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- new_property_values: BTreeMap<PropertyId, InputPropertyValue<T>>,
- ) -> BTreeMap<PropertyId, InputPropertyValue<T>> {
- new_property_values
- .into_iter()
- .filter(|(id, new_property_value)| {
- if let Some(entity_property_value) = entity_property_values.get(id) {
- StoredPropertyValue::<T::Hash, T::EntityId, T::Nonce>::from(
- new_property_value.to_owned(),
- ) != *entity_property_value
- } else {
- true
- }
- })
- .collect()
- }
- /// Update existing `entity_property_values` with `new_property_values`.
- /// if update performed, returns updated entity property values
- pub fn make_updated_property_values(
- entity_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- new_output_property_values: &BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- ) -> Option<BTreeMap<PropertyId, StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>>> {
- // Used to check if updated performed
- let mut entity_property_values_updated = entity_property_values.to_owned();
- new_output_property_values
- .iter()
- .for_each(|(id, new_property_value)| {
- if let Some(entity_property_value) = entity_property_values_updated.get_mut(&id) {
- entity_property_value.update(new_property_value.to_owned());
- }
- });
- if entity_property_values_updated != *entity_property_values {
- Some(entity_property_values_updated)
- } else {
- None
- }
- }
- /// Insert `InputValue` into `VecStoredPropertyValue` at `index_in_property_vector`.
- /// Returns `VecStoredPropertyValue` wrapped in `StoredPropertyValue`
- pub fn insert_at_index_in_property_vector(
- mut property_value_vector: VecStoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- index_in_property_vector: VecMaxLength,
- value: InputValue<T>,
- ) -> StoredPropertyValue<T::Hash, T::EntityId, T::Nonce> {
- property_value_vector.insert_at(index_in_property_vector, value.into());
- StoredPropertyValue::Vector(property_value_vector)
- }
- /// Remove `InputValue` at `index_in_property_vector` in `VecInputPropertyValue`.
- /// Returns `VecInputPropertyValue` wrapped in `InputPropertyValue`
- pub fn remove_at_index_in_property_vector(
- mut property_value_vector: VecStoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- index_in_property_vector: VecMaxLength,
- ) -> StoredPropertyValue<T::Hash, T::EntityId, T::Nonce> {
- property_value_vector.remove_at(index_in_property_vector);
- StoredPropertyValue::Vector(property_value_vector)
- }
- /// Clear `VecStoredPropertyValue`.
- /// Returns empty `VecStoredPropertyValue` wrapped in `StoredPropertyValue`
- pub fn clear_property_vector(
- mut property_value_vector: VecStoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- ) -> StoredPropertyValue<T::Hash, T::EntityId, T::Nonce> {
- property_value_vector.clear();
- StoredPropertyValue::Vector(property_value_vector)
- }
- /// Insert `InputPropertyValue` into `entity_property_values` mapping at `in_class_schema_property_id`.
- /// Returns updated `entity_property_values`
- pub fn insert_at_in_class_schema_property_id(
- mut entity_property_values: BTreeMap<
- PropertyId,
- StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- >,
- in_class_schema_property_id: PropertyId,
- property_value: StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>,
- ) -> BTreeMap<PropertyId, StoredPropertyValue<T::Hash, T::EntityId, T::Nonce>> {
- entity_property_values.insert(in_class_schema_property_id, property_value);
- entity_property_values
- }
- /// Ensure `Class` under given id exists, return corresponding one
- pub fn ensure_known_class_id(
- class_id: T::ClassId,
- ) -> Result<Class<T::EntityId, T::ClassId, T::CuratorGroupId>, Error<T>> {
- ensure!(
- <ClassById<T>>::contains_key(class_id),
- Error::<T>::ClassNotFound
- );
- Ok(Self::class_by_id(class_id))
- }
- /// Ensure `Entity` under given id exists, return corresponding one
- pub fn ensure_known_entity_id(
- entity_id: T::EntityId,
- ) -> Result<Entity<T::ClassId, T::MemberId, T::Hash, T::EntityId, T::Nonce>, Error<T>> {
- ensure!(
- <EntityById<T>>::contains_key(entity_id),
- Error::<T>::EntityNotFound
- );
- Ok(Self::entity_by_id(entity_id))
- }
- /// Ensure `CuratorGroup` under given id exists
- pub fn ensure_curator_group_under_given_id_exists(
- curator_group_id: &T::CuratorGroupId,
- ) -> Result<(), Error<T>> {
- ensure!(
- <CuratorGroupById<T>>::contains_key(curator_group_id),
- Error::<T>::CuratorGroupDoesNotExist
- );
- Ok(())
- }
- /// Ensure `CuratorGroup` under given id exists, return corresponding one
- pub fn ensure_curator_group_exists(
- curator_group_id: &T::CuratorGroupId,
- ) -> Result<CuratorGroup<T>, Error<T>> {
- Self::ensure_curator_group_under_given_id_exists(curator_group_id)?;
- Ok(Self::curator_group_by_id(curator_group_id))
- }
- /// Ensure `MaxNumberOfMaintainersPerClass` constraint satisfied
- pub fn ensure_maintainers_limit_not_reached(
- curator_groups: &BTreeSet<T::CuratorGroupId>,
- ) -> Result<(), Error<T>> {
- ensure!(
- curator_groups.len() < T::MaxNumberOfMaintainersPerClass::get() as usize,
- Error::<T>::ClassMaintainersLimitReached
- );
- Ok(())
- }
- /// Ensure all `CuratorGroup`'s under given ids exist
- pub fn ensure_curator_groups_exist(
- curator_groups: &BTreeSet<T::CuratorGroupId>,
- ) -> Result<(), Error<T>> {
- for curator_group in curator_groups {
- // Ensure CuratorGroup under given id exists
- Self::ensure_curator_group_exists(curator_group)?;
- }
- Ok(())
- }
- /// Perform security checks to ensure provided `class_maintainers` are valid
- pub fn ensure_class_maintainers_are_valid(
- class_maintainers: &BTreeSet<T::CuratorGroupId>,
- ) -> Result<(), Error<T>> {
- // Ensure max number of maintainers per Class constraint satisfied
- ensure!(
- class_maintainers.len() <= T::MaxNumberOfMaintainersPerClass::get() as usize,
- Error::<T>::ClassMaintainersLimitReached
- );
- // Ensure all curator groups provided are already exist in runtime
- Self::ensure_curator_groups_exist(class_maintainers)?;
- Ok(())
- }
- /// Ensure new `Schema` is not empty
- pub fn ensure_non_empty_schema(
- existing_properties: &BTreeSet<PropertyId>,
- new_properties: &[Property<T::ClassId>],
- ) -> Result<(), Error<T>> {
- // Schema is empty if both existing_properties and new_properties are empty
- let non_empty_schema = !existing_properties.is_empty() || !new_properties.is_empty();
- ensure!(non_empty_schema, Error::<T>::NoPropertiesInClassSchema);
- Ok(())
- }
- /// Ensure `ClassNameLengthConstraint` conditions satisfied
- pub fn ensure_class_name_is_valid(text: &[u8]) -> Result<(), Error<T>> {
- T::ClassNameLengthConstraint::get().ensure_valid(
- text.len(),
- Error::<T>::ClassNameTooShort,
- Error::<T>::ClassNameTooLong,
- )
- }
- /// Ensure `ClassDescriptionLengthConstraint` conditions satisfied
- pub fn ensure_class_description_is_valid(text: &[u8]) -> Result<(), Error<T>> {
- T::ClassDescriptionLengthConstraint::get().ensure_valid(
- text.len(),
- Error::<T>::ClassDescriptionTooShort,
- Error::<T>::ClassDescriptionTooLong,
- )
- }
- /// Ensure `MaxNumberOfClasses` constraint satisfied
- pub fn ensure_class_limit_not_reached() -> Result<(), Error<T>> {
- ensure!(
- (<ClassById<T>>::iter().count() as MaxNumber) < T::MaxNumberOfClasses::get(),
- Error::<T>::ClassLimitReached
- );
- Ok(())
- }
- /// Ensure `MaxNumberOfEntitiesPerClass` constraint satisfied
- pub fn ensure_valid_number_of_entities_per_class(
- maximum_entities_count: T::EntityId,
- ) -> Result<(), Error<T>> {
- ensure!(
- maximum_entities_count <= T::MaxNumberOfEntitiesPerClass::get(),
- Error::<T>::EntitiesNumberPerClassConstraintViolated
- );
- Ok(())
- }
- /// Ensure `IndividualEntitiesCreationLimit` constraint satisfied
- pub fn ensure_valid_number_of_class_entities_per_actor_constraint(
- number_of_class_entities_per_actor: T::EntityId,
- ) -> Result<(), Error<T>> {
- ensure!(
- number_of_class_entities_per_actor <= T::IndividualEntitiesCreationLimit::get(),
- Error::<T>::NumberOfClassEntitiesPerActorConstraintViolated
- );
- Ok(())
- }
- /// Ensure all entities creation limits, defined for a given `Class`, are valid
- pub fn ensure_entities_creation_limits_are_valid(
- maximum_entities_count: T::EntityId,
- default_entity_creation_voucher_upper_bound: T::EntityId,
- ) -> Result<(), Error<T>> {
- // Ensure default_entity_creation_voucher_upper_bound does not exceed default_entity_creation_voucher_upper_bound
- ensure!(
- default_entity_creation_voucher_upper_bound <= maximum_entities_count,
- Error::<T>::PerControllerEntitiesCreationLimitExceedsOverallLimit
- );
- // Ensure maximum_entities_count does not exceed MaxNumberOfEntitiesPerClass limit
- Self::ensure_valid_number_of_entities_per_class(maximum_entities_count)?;
- // Ensure default_entity_creation_voucher_upper_bound constraint does not exceed IndividualEntitiesCreationLimit
- Self::ensure_valid_number_of_class_entities_per_actor_constraint(
- default_entity_creation_voucher_upper_bound,
- )
- }
- /// Ensure maximum number of operations during atomic batching constraint satisfied
- pub fn ensure_number_of_operations_during_atomic_batching_limit_not_reached(
- operations: &[OperationType<T>],
- ) -> Result<(), Error<T>> {
- ensure!(
- operations.len() <= T::MaxNumberOfOperationsDuringAtomicBatching::get() as usize,
- Error::<T>::NumberOfOperationsDuringAtomicBatchingLimitReached
- );
- Ok(())
- }
- /// Complete all checks to ensure each `Property` is valid
- pub fn ensure_all_properties_are_valid(
- new_properties: &[Property<T::ClassId>],
- ) -> Result<(), Error<T>> {
- for new_property in new_properties.iter() {
- // Ensure PropertyNameLengthConstraint satisfied
- new_property.ensure_name_is_valid()?;
- // Ensure PropertyDescriptionLengthConstraint satisfied
- new_property.ensure_description_is_valid()?;
- // Ensure Type specific constraints satisfied
- new_property.ensure_property_type_size_is_valid()?;
- // Ensure refers to existing class_id, if If Property Type is Reference,
- Self::ensure_property_type_reference_is_valid(new_property)?;
- }
- Ok(())
- }
- /// Ensure refers to existing `class_id`, if If `Property` `Type` is `Reference`,
- pub fn ensure_property_type_reference_is_valid(
- property: &Property<T::ClassId>,
- ) -> Result<(), Error<T>> {
- let has_unknown_reference =
- if let Type::Reference(other_class_id, _) = property.property_type.get_inner_type() {
- !<ClassById<T>>::contains_key(other_class_id)
- } else {
- false
- };
- ensure!(
- !has_unknown_reference,
- Error::<T>::ClassSchemaRefersUnknownClass
- );
- Ok(())
- }
- /// Ensure all `Property` names are unique within `Class`
- pub fn ensure_all_property_names_are_unique(
- class_properties: &[Property<T::ClassId>],
- new_properties: &[Property<T::ClassId>],
- ) -> Result<(), Error<T>> {
- // Used to ensure all property names are unique within class
- let mut unique_prop_names = BTreeSet::new();
- for property in class_properties.iter() {
- unique_prop_names.insert(property.name.to_owned());
- }
- for new_property in new_properties {
- // Ensure name of a new property is unique within its class.
- ensure!(
- !unique_prop_names.contains(&new_property.name),
- Error::<T>::PropertyNameNotUniqueInAClass
- );
- unique_prop_names.insert(new_property.name.to_owned());
- }
- Ok(())
- }
- /// Ensure provided indices of `existing_properties` are valid indices of `Class` properties
- pub fn ensure_schema_properties_are_valid_indices(
- existing_properties: &BTreeSet<PropertyId>,
- class_properties: &[Property<T::ClassId>],
- ) -> Result<(), Error<T>> {
- let has_unknown_properties = existing_properties
- .iter()
- .any(|&prop_id| prop_id >= class_properties.len() as PropertyId);
- ensure!(
- !has_unknown_properties,
- Error::<T>::ClassSchemaRefersUnknownPropertyIndex
- );
- Ok(())
- }
- /// Create new `Schema` from existing and new property ids
- pub fn create_class_schema(
- existing_properties: BTreeSet<PropertyId>,
- class_properties: &[Property<T::ClassId>],
- new_properties: &[Property<T::ClassId>],
- ) -> Schema {
- // Calcualate new property ids
- let properties = new_properties
- .iter()
- .enumerate()
- .map(|(i, _)| (class_properties.len() + i) as PropertyId)
- // Concatenate them with existing ones
- .chain(existing_properties.into_iter())
- .collect();
- Schema::new(properties)
- }
- /// Update existing `Class` properties with new ones provided, return updated ones
- pub fn make_updated_class_properties(
- class_properties: Vec<Property<T::ClassId>>,
- new_properties: Vec<Property<T::ClassId>>,
- ) -> Vec<Property<T::ClassId>> {
- class_properties
- .into_iter()
- .chain(new_properties.into_iter())
- .collect()
- }
- }
- decl_event!(
- pub enum Event<T>
- where
- CuratorGroupId = <T as ActorAuthenticator>::CuratorGroupId,
- CuratorId = <T as ActorAuthenticator>::CuratorId,
- ClassId = <T as Trait>::ClassId,
- EntityId = <T as Trait>::EntityId,
- EntityController = EntityController<<T as ActorAuthenticator>::MemberId>,
- EntityCreationVoucher = EntityCreationVoucher<T>,
- Status = bool,
- Actor = Actor<
- <T as ActorAuthenticator>::CuratorGroupId,
- <T as ActorAuthenticator>::CuratorId,
- <T as ActorAuthenticator>::MemberId,
- >,
- Nonce = <T as Trait>::Nonce,
- SideEffects = Option<ReferenceCounterSideEffects<T>>,
- SideEffect = Option<(<T as Trait>::EntityId, EntityReferenceCounterSideEffect)>,
- {
- CuratorGroupAdded(CuratorGroupId),
- CuratorGroupRemoved(CuratorGroupId),
- CuratorGroupStatusSet(CuratorGroupId, Status),
- CuratorAdded(CuratorGroupId, CuratorId),
- CuratorRemoved(CuratorGroupId, CuratorId),
- MaintainerAdded(ClassId, CuratorGroupId),
- MaintainerRemoved(ClassId, CuratorGroupId),
- EntityCreationVoucherUpdated(EntityController, EntityCreationVoucher),
- EntityCreationVoucherCreated(EntityController, EntityCreationVoucher),
- ClassCreated(ClassId),
- ClassPermissionsUpdated(ClassId),
- ClassSchemaAdded(ClassId, SchemaId),
- ClassSchemaStatusUpdated(ClassId, SchemaId, Status),
- EntityPermissionsUpdated(EntityId),
- EntityCreated(Actor, EntityId),
- EntityRemoved(Actor, EntityId),
- EntitySchemaSupportAdded(Actor, EntityId, SchemaId, SideEffects),
- EntityPropertyValuesUpdated(Actor, EntityId, SideEffects),
- VectorCleared(Actor, EntityId, PropertyId, SideEffects),
- RemovedAtVectorIndex(Actor, EntityId, PropertyId, VecMaxLength, Nonce, SideEffect),
- InsertedAtVectorIndex(Actor, EntityId, PropertyId, VecMaxLength, Nonce, SideEffect),
- EntityOwnershipTransfered(EntityId, EntityController, SideEffects),
- TransactionCompleted(Actor),
- }
- );
|