lib.rs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. //! # Proposals codex module
  2. //! Proposals `codex` module for the Joystream platform.
  3. //! Component of the proposals frame_system. It contains preset proposal types.
  4. //!
  5. //! ## Overview
  6. //!
  7. //! The proposals codex module serves as a facade and entry point of the proposals frame_system. It uses
  8. //! proposals `engine` module to maintain a lifecycle of the proposal and to execute proposals.
  9. //! During the proposal creation, `codex` also create a discussion thread using the `discussion`
  10. //! proposals module. `Codex` uses predefined parameters (eg.:`voting_period`) for each proposal and
  11. //! encodes extrinsic calls from dependency modules in order to create proposals inside the `engine`
  12. //! module.
  13. //!
  14. //! To create a proposal you need to call the extrinsic `create_proposal` with the `ProposalDetails` variant
  15. //! corresponding to the proposal you want to create. [See the possible details with their proposal](./enum.ProposalDetails.html)
  16. //!
  17. //! ## Extrinsics
  18. //!
  19. //! - [create_proposal](./struct.Module.html#method.create_proposal) - creates proposal
  20. //!
  21. //! ### Dependencies:
  22. //! - [proposals engine](../substrate_proposals_engine_module/index.html)
  23. //! - [proposals discussion](../substrate_proposals_discussion_module/index.html)
  24. //! - [membership](../substrate_membership_module/index.html)
  25. //! - [council](../substrate_council_module/index.html)
  26. //! - [common](../substrate_common_module/index.html)
  27. //! - [staking](../substrate_staking_module/index.html)
  28. //! - [working_group](../substrate_working_group_module/index.html)
  29. //!
  30. //! ### Notes
  31. //! The module uses [ProposalEncoder](./trait.ProposalEncoder.html) to encode the proposal using its
  32. //! details. Encoded byte vector is passed to the _proposals engine_ as serialized executable code.
  33. // `decl_module!` does a lot of recursion and requires us to increase the limit to 256.
  34. #![recursion_limit = "256"]
  35. // Ensure we're `no_std` when compiling for Wasm.
  36. #![cfg_attr(not(feature = "std"), no_std)]
  37. // Disable this lint warning because Substrate generates function without an alias for
  38. // the ProposalDetailsOf type.
  39. #![allow(clippy::too_many_arguments)]
  40. mod types;
  41. #[cfg(test)]
  42. mod tests;
  43. mod benchmarking;
  44. use frame_support::dispatch::DispatchResult;
  45. use frame_support::traits::Get;
  46. use frame_support::weights::Weight;
  47. use frame_support::{decl_error, decl_event, decl_module, decl_storage, ensure};
  48. use sp_arithmetic::traits::Zero;
  49. use sp_runtime::SaturatedConversion;
  50. use sp_std::clone::Clone;
  51. use sp_std::collections::btree_set::BTreeSet;
  52. use common::membership::MemberOriginValidator;
  53. use common::MemberId;
  54. use proposals_discussion::ThreadMode;
  55. use proposals_engine::{
  56. BalanceOf, ProposalCreationParameters, ProposalObserver, ProposalParameters,
  57. };
  58. pub use types::{
  59. CreateOpeningParameters, FillOpeningParameters, GeneralProposalParams, ProposalDetails,
  60. ProposalDetailsOf, ProposalEncoder, TerminateRoleParameters,
  61. };
  62. // Max allowed value for 'Funding Request' proposal
  63. const MAX_SPENDING_PROPOSAL_VALUE: u32 = 50_000_000_u32;
  64. // Max validator count for the 'Set Max Validator Count' proposal
  65. const MAX_VALIDATOR_COUNT: u32 = 300;
  66. // Max number of account that a fund request accept
  67. const MAX_FUNDING_REQUEST_ACCOUNTS: usize = 100;
  68. /// Proposal codex WeightInfo.
  69. /// Note: This was auto generated through the benchmark CLI using the `--weight-trait` flag
  70. pub trait WeightInfo {
  71. fn create_proposal_signal(i: u32, t: u32, d: u32) -> Weight;
  72. fn create_proposal_runtime_upgrade(i: u32, t: u32, d: u32) -> Weight;
  73. fn create_proposal_funding_request(i: u32, d: u32) -> Weight;
  74. fn create_proposal_set_max_validator_count(t: u32, d: u32) -> Weight;
  75. fn create_proposal_create_working_group_lead_opening(i: u32, t: u32, d: u32) -> Weight;
  76. fn create_proposal_fill_working_group_lead_opening(t: u32, d: u32) -> Weight;
  77. fn create_proposal_update_working_group_budget(t: u32, d: u32) -> Weight;
  78. fn create_proposal_decrease_working_group_lead_stake(t: u32, d: u32) -> Weight;
  79. fn create_proposal_slash_working_group_lead(d: u32) -> Weight;
  80. fn create_proposal_set_working_group_lead_reward(t: u32, d: u32) -> Weight;
  81. fn create_proposal_terminate_working_group_lead(t: u32, d: u32) -> Weight;
  82. fn create_proposal_amend_constitution(i: u32, d: u32) -> Weight;
  83. fn create_proposal_cancel_working_group_lead_opening(d: u32) -> Weight;
  84. fn create_proposal_set_membership_price(t: u32, d: u32) -> Weight;
  85. fn create_proposal_set_council_budget_increment(t: u32, d: u32) -> Weight;
  86. fn create_proposal_set_councilor_reward(t: u32, d: u32) -> Weight;
  87. fn create_proposal_set_initial_invitation_balance(t: u32, d: u32) -> Weight;
  88. fn create_proposal_set_initial_invitation_count(t: u32, d: u32) -> Weight;
  89. fn create_proposal_set_membership_lead_invitation_quota(d: u32) -> Weight;
  90. fn create_proposal_set_referral_cut(t: u32, d: u32) -> Weight;
  91. fn create_proposal_create_blog_post(t: u32, d: u32, h: u32, b: u32) -> Weight;
  92. fn create_proposal_edit_blog_post(t: u32, d: u32, h: u32, b: u32) -> Weight;
  93. fn create_proposal_lock_blog_post(t: u32, d: u32) -> Weight;
  94. fn create_proposal_unlock_blog_post(t: u32, d: u32) -> Weight;
  95. fn create_proposal_veto_proposal(t: u32, d: u32) -> Weight;
  96. }
  97. type WeightInfoCodex<T> = <T as Trait>::WeightInfo;
  98. /// 'Proposals codex' substrate module Trait
  99. pub trait Trait:
  100. frame_system::Trait
  101. + proposals_engine::Trait
  102. + proposals_discussion::Trait
  103. + common::membership::MembershipTypes
  104. + staking::Trait
  105. + proposals_engine::Trait
  106. {
  107. /// Proposal Codex module event type.
  108. type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
  109. /// Validates member id and origin combination.
  110. type MembershipOriginValidator: MemberOriginValidator<
  111. Self::Origin,
  112. MemberId<Self>,
  113. Self::AccountId,
  114. >;
  115. /// Encodes the proposal usint its details.
  116. type ProposalEncoder: ProposalEncoder<Self>;
  117. /// Weight information for extrinsics in this pallet.
  118. type WeightInfo: WeightInfo;
  119. /// 'Set Max Validator Count' proposal parameters.
  120. type SetMaxValidatorCountProposalParameters: Get<
  121. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  122. >;
  123. /// 'Runtime Upgrade' proposal parameters.
  124. type RuntimeUpgradeProposalParameters: Get<
  125. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  126. >;
  127. /// 'Signal' proposal parameters.
  128. type SignalProposalParameters: Get<ProposalParameters<Self::BlockNumber, BalanceOf<Self>>>;
  129. /// 'Funding Request' proposal parameters.
  130. type FundingRequestProposalParameters: Get<
  131. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  132. >;
  133. /// 'Create Working Group Lead Opening' proposal parameters.
  134. type CreateWorkingGroupLeadOpeningProposalParameters: Get<
  135. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  136. >;
  137. /// 'Fill Working Group Lead Opening' proposal parameters.
  138. type FillWorkingGroupLeadOpeningProposalParameters: Get<
  139. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  140. >;
  141. /// 'Update Working Group Budget' proposal parameters.
  142. type UpdateWorkingGroupBudgetProposalParameters: Get<
  143. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  144. >;
  145. /// 'Decrease Working Group Lead Stake' proposal parameters.
  146. type DecreaseWorkingGroupLeadStakeProposalParameters: Get<
  147. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  148. >;
  149. /// 'Slash Working Group Lead Stake' proposal parameters.
  150. type SlashWorkingGroupLeadProposalParameters: Get<
  151. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  152. >;
  153. /// 'Set Working Group Lead Reward' proposal parameters.
  154. type SetWorkingGroupLeadRewardProposalParameters: Get<
  155. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  156. >;
  157. /// 'Terminate Working Group Lead' proposal parameters.
  158. type TerminateWorkingGroupLeadProposalParameters: Get<
  159. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  160. >;
  161. /// 'Amend Constitution' proposal parameters.
  162. type AmendConstitutionProposalParameters: Get<
  163. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  164. >;
  165. /// `Cancel Working Group Lead Opening` proposal parameters.
  166. type CancelWorkingGroupLeadOpeningProposalParameters: Get<
  167. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  168. >;
  169. /// `Set Membership Price Parameters` proposal parameters.
  170. type SetMembershipPriceProposalParameters: Get<
  171. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  172. >;
  173. /// `Set Council Budget Increment` proposal parameters.
  174. type SetCouncilBudgetIncrementProposalParameters: Get<
  175. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  176. >;
  177. /// `Set Councilor Reward` proposal parameters
  178. type SetCouncilorRewardProposalParameters: Get<
  179. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  180. >;
  181. /// `Set Initial Invitation Balance` proposal parameters
  182. type SetInitialInvitationBalanceProposalParameters: Get<
  183. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  184. >;
  185. /// `Set Invitation Count` proposal parameters
  186. type SetInvitationCountProposalParameters: Get<
  187. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  188. >;
  189. /// `Set Membership Lead Invitaiton Quota` proposal parameters
  190. type SetMembershipLeadInvitationQuotaProposalParameters: Get<
  191. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  192. >;
  193. /// `Set Referral Cut` proposal parameters
  194. type SetReferralCutProposalParameters: Get<
  195. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  196. >;
  197. /// `Create Blog Post` proposal parameters
  198. type CreateBlogPostProposalParameters: Get<
  199. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  200. >;
  201. /// `Edit Blog Post` proposal parameters
  202. type EditBlogPostProoposalParamters: Get<ProposalParameters<Self::BlockNumber, BalanceOf<Self>>>;
  203. /// `Lock Blog Post` proposal parameters
  204. type LockBlogPostProposalParameters: Get<ProposalParameters<Self::BlockNumber, BalanceOf<Self>>>;
  205. /// `Unlock Blog Post` proposal parameters
  206. type UnlockBlogPostProposalParameters: Get<
  207. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  208. >;
  209. /// `Veto Proposal` proposal parameters
  210. type VetoProposalProposalParameters: Get<ProposalParameters<Self::BlockNumber, BalanceOf<Self>>>;
  211. }
  212. /// Specialized alias of GeneralProposalParams
  213. pub type GeneralProposalParameters<T> = GeneralProposalParams<
  214. MemberId<T>,
  215. <T as frame_system::Trait>::AccountId,
  216. <T as frame_system::Trait>::BlockNumber,
  217. >;
  218. decl_event! {
  219. pub enum Event<T> where
  220. GeneralProposalParameters = GeneralProposalParameters<T>,
  221. ProposalDetailsOf = ProposalDetailsOf<T>,
  222. <T as proposals_engine::Trait>::ProposalId,
  223. {
  224. /// A proposal was created
  225. /// Params:
  226. /// - Id of a newly created proposal after it was saved in storage.
  227. /// - General proposal parameter. Parameters shared by all proposals
  228. /// - Proposal Details. Parameter of proposal with a variant for each kind of proposal
  229. ProposalCreated(ProposalId, GeneralProposalParameters, ProposalDetailsOf),
  230. }
  231. }
  232. decl_error! {
  233. /// Codex module predefined errors
  234. pub enum Error for Module<T: Trait> {
  235. /// Provided text for text proposal is empty
  236. SignalProposalIsEmpty,
  237. /// Provided WASM code for the runtime upgrade proposal is empty
  238. RuntimeProposalIsEmpty,
  239. /// Invalid balance value for the spending proposal
  240. InvalidFundingRequestProposalBalance,
  241. /// Invalid validator count for the 'set validator count' proposal
  242. InvalidValidatorCount,
  243. /// Require root origin in extrinsics
  244. RequireRootOrigin,
  245. /// Invalid council election parameter - council_size
  246. InvalidCouncilElectionParameterCouncilSize,
  247. /// Invalid council election parameter - candidacy-limit
  248. InvalidCouncilElectionParameterCandidacyLimit,
  249. /// Invalid council election parameter - min-voting_stake
  250. InvalidCouncilElectionParameterMinVotingStake,
  251. /// Invalid council election parameter - new_term_duration
  252. InvalidCouncilElectionParameterNewTermDuration,
  253. /// Invalid council election parameter - min_council_stake
  254. InvalidCouncilElectionParameterMinCouncilStake,
  255. /// Invalid council election parameter - revealing_period
  256. InvalidCouncilElectionParameterRevealingPeriod,
  257. /// Invalid council election parameter - voting_period
  258. InvalidCouncilElectionParameterVotingPeriod,
  259. /// Invalid council election parameter - announcing_period
  260. InvalidCouncilElectionParameterAnnouncingPeriod,
  261. /// Invalid working group budget capacity parameter
  262. InvalidWorkingGroupBudgetCapacity,
  263. /// Invalid 'set lead proposal' parameter - proposed lead cannot be a councilor
  264. InvalidSetLeadParameterCannotBeCouncilor,
  265. /// Invalid 'slash stake proposal' parameter - cannot slash by zero balance.
  266. SlashingStakeIsZero,
  267. /// Invalid 'decrease stake proposal' parameter - cannot decrease by zero balance.
  268. DecreasingStakeIsZero,
  269. /// Insufficient funds for 'Update Working Group Budget' proposal execution
  270. InsufficientFundsForBudgetUpdate,
  271. /// Invalid number of accounts recieving funding request for 'Funding Request' proposal.
  272. InvalidFundingRequestProposalNumberOfAccount,
  273. /// Repeated account in 'Funding Request' proposal.
  274. InvalidFundingRequestProposalRepeatedAccount,
  275. }
  276. }
  277. // Storage for the proposals codex module
  278. decl_storage! {
  279. pub trait Store for Module<T: Trait> as ProposalCodex {
  280. /// Map proposal id to its discussion thread id
  281. pub ThreadIdByProposalId get(fn thread_id_by_proposal_id):
  282. map hasher(blake2_128_concat) T::ProposalId => T::ThreadId;
  283. }
  284. }
  285. decl_module! {
  286. /// Proposal codex substrate module Call
  287. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  288. /// Predefined errors
  289. type Error = Error<T>;
  290. fn deposit_event() = default;
  291. /// Exports 'Set Max Validator Count' proposal parameters.
  292. const SetMaxValidatorCountProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  293. = T::SetMaxValidatorCountProposalParameters::get();
  294. /// Exports 'Runtime Upgrade' proposal parameters.
  295. const RuntimeUpgradeProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  296. = T::RuntimeUpgradeProposalParameters::get();
  297. /// Exports 'Signal' proposal parameters.
  298. const SignalProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  299. = T::SignalProposalParameters::get();
  300. /// Exports 'Funding Request' proposal parameters.
  301. const FundingRequestProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  302. = T::FundingRequestProposalParameters::get();
  303. /// Exports 'Create Working Group Lead Opening' proposal parameters.
  304. const CreateWorkingGroupLeadOpeningProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  305. = T::CreateWorkingGroupLeadOpeningProposalParameters::get();
  306. /// Exports 'Fill Working Group Lead Opening' proposal parameters.
  307. const FillWorkingGroupOpeningProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  308. = T::FillWorkingGroupLeadOpeningProposalParameters::get();
  309. /// Exports 'Update Working Group Budget' proposal parameters.
  310. const UpdateWorkingGroupBudgetProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  311. = T::UpdateWorkingGroupBudgetProposalParameters::get();
  312. /// Exports 'Decrease Working Group Lead Stake' proposal parameters.
  313. const DecreaseWorkingGroupLeadStakeProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  314. = T::DecreaseWorkingGroupLeadStakeProposalParameters::get();
  315. /// Exports 'Slash Working Group Lead' proposal parameters.
  316. const SlashWorkingGroupLeadProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  317. = T::SlashWorkingGroupLeadProposalParameters::get();
  318. /// Exports 'Set Working Group Lead Reward' proposal parameters.
  319. const SetWorkingGroupLeadRewardProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  320. = T::SetWorkingGroupLeadRewardProposalParameters::get();
  321. /// Exports 'Terminate Working Group Lead' proposal parameters.
  322. const TerminateWorkingGroupLeadProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  323. = T::TerminateWorkingGroupLeadProposalParameters::get();
  324. /// Exports 'Amend Constitution' proposal parameters.
  325. const AmendConstitutionProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  326. = T::AmendConstitutionProposalParameters::get();
  327. /// Exports 'Cancel Working Group Lead Opening' proposal parameters.
  328. const CancelWorkingGroupLeadOpeningProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  329. = T::CancelWorkingGroupLeadOpeningProposalParameters::get();
  330. /// Exports 'Set Membership Price' proposal parameters.
  331. const SetMembershipPriceProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  332. = T::SetMembershipPriceProposalParameters::get();
  333. /// Exports `Set Council Budget Increment` proposal parameters.
  334. const SetCouncilBudgetIncrementProposalParameters:
  335. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::SetCouncilBudgetIncrementProposalParameters::get();
  336. /// Exports `Set Councilor Reward Proposal Parameters` proposal parameters.
  337. const SetCouncilorRewardProposalParameters:
  338. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::SetCouncilorRewardProposalParameters::get();
  339. /// Exports `Set Initial Invitation Balance` proposal parameters.
  340. const SetInitialInvitationBalanceProposalParameters:
  341. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::SetInitialInvitationBalanceProposalParameters::get();
  342. const SetInvitationCountProposalParameters:
  343. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::SetInvitationCountProposalParameters::get();
  344. const SetMembershipLeadInvitationQuotaProposalParameters:
  345. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::SetMembershipLeadInvitationQuotaProposalParameters::get();
  346. const SetReferralCutProposalParameters:
  347. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::SetReferralCutProposalParameters::get();
  348. const CreateBlogPostProposalParameters:
  349. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::CreateBlogPostProposalParameters::get();
  350. const EditBlogPostProoposalParamters:
  351. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::EditBlogPostProoposalParamters::get();
  352. const LockBlogPostProposalParameters:
  353. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::LockBlogPostProposalParameters::get();
  354. const UnlockBlogPostProposalParameters:
  355. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::UnlockBlogPostProposalParameters::get();
  356. const VetoProposalProposalParameters:
  357. ProposalParameters<T::BlockNumber, BalanceOf<T>> = T::VetoProposalProposalParameters::get();
  358. /// Create a proposal, the type of proposal depends on the `proposal_details` variant
  359. ///
  360. /// <weight>
  361. ///
  362. /// ## Weight
  363. /// `O (T + D + I)` where:
  364. /// - `T` is the length of the title
  365. /// - `D` is the length of the description
  366. /// - `I` is the length of any parameter in `proposal_details`
  367. /// - DB:
  368. /// - O(1) doesn't depend on the state or parameters
  369. /// # </weight>
  370. #[weight = Module::<T>::get_create_proposal_weight(
  371. &general_proposal_parameters,
  372. &proposal_details
  373. )
  374. ]
  375. pub fn create_proposal(
  376. origin,
  377. general_proposal_parameters: GeneralProposalParameters<T>,
  378. proposal_details: ProposalDetailsOf<T>,
  379. ) {
  380. Self::ensure_details_checks(&proposal_details)?;
  381. let proposal_parameters = Self::get_proposal_parameters(&proposal_details);
  382. // TODO: encode_proposal could take a reference instead of moving to prevent cloning
  383. // since the encode trait takes a reference to `self`.
  384. // (Note: this is an useful change since this could be a ~3MB copy in the case of
  385. // a Runtime Upgrade). See: https://github.com/Joystream/joystream/issues/2161
  386. let proposal_code = T::ProposalEncoder::encode_proposal(proposal_details.clone());
  387. let account_id =
  388. T::MembershipOriginValidator::ensure_member_controller_account_origin(
  389. origin,
  390. general_proposal_parameters.member_id
  391. )?;
  392. <proposals_engine::Module<T>>::ensure_create_proposal_parameters_are_valid(
  393. &proposal_parameters,
  394. &general_proposal_parameters.title,
  395. &general_proposal_parameters.description,
  396. general_proposal_parameters.staking_account_id.clone(),
  397. general_proposal_parameters.exact_execution_block,
  398. general_proposal_parameters.member_id,
  399. )?;
  400. let initial_thread_mode = ThreadMode::Open;
  401. <proposals_discussion::Module<T>>::ensure_can_create_thread(&initial_thread_mode)?;
  402. let discussion_thread_id = <proposals_discussion::Module<T>>::create_thread(
  403. general_proposal_parameters.member_id,
  404. initial_thread_mode,
  405. )?;
  406. let proposal_creation_params = ProposalCreationParameters {
  407. account_id,
  408. proposer_id: general_proposal_parameters.member_id,
  409. proposal_parameters,
  410. title: general_proposal_parameters.title.clone(),
  411. description: general_proposal_parameters.description.clone(),
  412. staking_account_id: general_proposal_parameters.staking_account_id.clone(),
  413. encoded_dispatchable_call_code: proposal_code,
  414. exact_execution_block: general_proposal_parameters.exact_execution_block,
  415. };
  416. let proposal_id =
  417. <proposals_engine::Module<T>>::create_proposal(proposal_creation_params)?;
  418. <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
  419. Self::deposit_event(RawEvent::ProposalCreated(proposal_id, general_proposal_parameters, proposal_details));
  420. }
  421. }
  422. }
  423. impl<T: Trait> Module<T> {
  424. // Ensure that the proposal details respects all the checks
  425. fn ensure_details_checks(details: &ProposalDetailsOf<T>) -> DispatchResult {
  426. match details {
  427. ProposalDetails::Signal(ref signal) => {
  428. ensure!(!signal.is_empty(), Error::<T>::SignalProposalIsEmpty);
  429. }
  430. ProposalDetails::RuntimeUpgrade(ref blob) => {
  431. ensure!(!blob.is_empty(), Error::<T>::RuntimeProposalIsEmpty);
  432. }
  433. ProposalDetails::FundingRequest(ref funding_requests) => {
  434. ensure!(
  435. !funding_requests.is_empty(),
  436. Error::<T>::InvalidFundingRequestProposalNumberOfAccount
  437. );
  438. ensure!(
  439. funding_requests.len() <= MAX_FUNDING_REQUEST_ACCOUNTS,
  440. Error::<T>::InvalidFundingRequestProposalNumberOfAccount
  441. );
  442. // Ideally we would use hashset but it's not available in substrate
  443. let mut visited_accounts = BTreeSet::new();
  444. for funding_request in funding_requests {
  445. let account = &funding_request.account;
  446. ensure!(
  447. !visited_accounts.contains(&account),
  448. Error::<T>::InvalidFundingRequestProposalRepeatedAccount
  449. );
  450. ensure!(
  451. funding_request.amount != Zero::zero(),
  452. Error::<T>::InvalidFundingRequestProposalBalance
  453. );
  454. ensure!(
  455. funding_request.amount <= <BalanceOf<T>>::from(MAX_SPENDING_PROPOSAL_VALUE),
  456. Error::<T>::InvalidFundingRequestProposalBalance
  457. );
  458. visited_accounts.insert(account);
  459. }
  460. }
  461. ProposalDetails::SetMaxValidatorCount(ref new_validator_count) => {
  462. // Since `set_validator_count` doesn't check that `new_validator_count`
  463. // isn't less than `minimum_validator_count` we need to do this here.
  464. // We shouldn't access the storage for creation checks but we do it here for the
  465. // reasons just explained **as an exception**.
  466. ensure!(
  467. *new_validator_count >= <staking::Module<T>>::minimum_validator_count(),
  468. Error::<T>::InvalidValidatorCount
  469. );
  470. ensure!(
  471. *new_validator_count <= MAX_VALIDATOR_COUNT,
  472. Error::<T>::InvalidValidatorCount
  473. );
  474. }
  475. ProposalDetails::CreateWorkingGroupLeadOpening(..) => {
  476. // Note: No checks for this proposal for now
  477. }
  478. ProposalDetails::FillWorkingGroupLeadOpening(..) => {
  479. // Note: No checks for this proposal for now
  480. }
  481. ProposalDetails::UpdateWorkingGroupBudget(..) => {
  482. // Note: No checks for this proposal for now
  483. }
  484. ProposalDetails::DecreaseWorkingGroupLeadStake(_, ref stake_amount, _) => {
  485. ensure!(
  486. *stake_amount != Zero::zero(),
  487. Error::<T>::DecreasingStakeIsZero
  488. );
  489. }
  490. ProposalDetails::SlashWorkingGroupLead(..) => {
  491. // Note: No checks for this proposal for now
  492. }
  493. ProposalDetails::SetWorkingGroupLeadReward(..) => {
  494. // Note: No checks for this proposal for now
  495. }
  496. ProposalDetails::TerminateWorkingGroupLead(..) => {
  497. // Note: No checks for this proposal for now
  498. }
  499. ProposalDetails::AmendConstitution(..) => {
  500. // Note: No checks for this proposal for now
  501. }
  502. ProposalDetails::CancelWorkingGroupLeadOpening(..) => {
  503. // Note: No checks for this proposal for now
  504. }
  505. ProposalDetails::SetMembershipPrice(..) => {
  506. // Note: No checks for this proposal for now
  507. }
  508. ProposalDetails::SetCouncilBudgetIncrement(..) => {
  509. // Note: No checks for this proposal for now
  510. }
  511. ProposalDetails::SetCouncilorReward(..) => {
  512. // Note: No checks for this proposal for now
  513. }
  514. ProposalDetails::SetInitialInvitationBalance(..) => {
  515. // Note: No checks for this proposal for now
  516. }
  517. ProposalDetails::SetInitialInvitationCount(..) => {
  518. // Note: No checks for this proposal for now
  519. }
  520. ProposalDetails::SetMembershipLeadInvitationQuota(..) => {
  521. // Note: No checks for this proposal for now
  522. }
  523. ProposalDetails::SetReferralCut(..) => {
  524. // Note: No checks for this proposal for now
  525. }
  526. ProposalDetails::CreateBlogPost(..) => {
  527. // Note: No checks for this proposal for now
  528. }
  529. ProposalDetails::EditBlogPost(..) => {
  530. // Note: No checks for this proposal for now
  531. }
  532. ProposalDetails::LockBlogPost(..) => {
  533. // Note: No checks for this proposal for now
  534. }
  535. ProposalDetails::UnlockBlogPost(..) => {
  536. // Note: No checks for this proposal for now
  537. }
  538. ProposalDetails::VetoProposal(..) => {
  539. // Note: No checks for this proposal for now
  540. }
  541. }
  542. Ok(())
  543. }
  544. // Returns the proposal parameters according to ProposalDetials
  545. fn get_proposal_parameters(
  546. details: &ProposalDetailsOf<T>,
  547. ) -> ProposalParameters<T::BlockNumber, BalanceOf<T>> {
  548. match details {
  549. ProposalDetails::Signal(..) => T::SignalProposalParameters::get(),
  550. ProposalDetails::RuntimeUpgrade(..) => T::RuntimeUpgradeProposalParameters::get(),
  551. ProposalDetails::FundingRequest(..) => T::FundingRequestProposalParameters::get(),
  552. ProposalDetails::SetMaxValidatorCount(..) => {
  553. T::SetMaxValidatorCountProposalParameters::get()
  554. }
  555. ProposalDetails::FillWorkingGroupLeadOpening(..) => {
  556. T::FillWorkingGroupLeadOpeningProposalParameters::get()
  557. }
  558. ProposalDetails::UpdateWorkingGroupBudget(..) => {
  559. T::UpdateWorkingGroupBudgetProposalParameters::get()
  560. }
  561. ProposalDetails::DecreaseWorkingGroupLeadStake(..) => {
  562. T::DecreaseWorkingGroupLeadStakeProposalParameters::get()
  563. }
  564. ProposalDetails::SlashWorkingGroupLead(..) => {
  565. T::SlashWorkingGroupLeadProposalParameters::get()
  566. }
  567. ProposalDetails::SetWorkingGroupLeadReward(..) => {
  568. T::SetWorkingGroupLeadRewardProposalParameters::get()
  569. }
  570. ProposalDetails::TerminateWorkingGroupLead(..) => {
  571. T::TerminateWorkingGroupLeadProposalParameters::get()
  572. }
  573. ProposalDetails::CreateWorkingGroupLeadOpening(..) => {
  574. T::CreateWorkingGroupLeadOpeningProposalParameters::get()
  575. }
  576. ProposalDetails::AmendConstitution(..) => T::AmendConstitutionProposalParameters::get(),
  577. ProposalDetails::SetMembershipPrice(..) => {
  578. T::SetMembershipPriceProposalParameters::get()
  579. }
  580. ProposalDetails::CancelWorkingGroupLeadOpening(..) => {
  581. T::CancelWorkingGroupLeadOpeningProposalParameters::get()
  582. }
  583. ProposalDetails::SetCouncilBudgetIncrement(..) => {
  584. T::SetCouncilBudgetIncrementProposalParameters::get()
  585. }
  586. ProposalDetails::SetCouncilorReward(..) => {
  587. T::SetCouncilorRewardProposalParameters::get()
  588. }
  589. ProposalDetails::SetInitialInvitationBalance(..) => {
  590. T::SetInitialInvitationBalanceProposalParameters::get()
  591. }
  592. ProposalDetails::SetInitialInvitationCount(..) => {
  593. T::SetInvitationCountProposalParameters::get()
  594. }
  595. ProposalDetails::SetMembershipLeadInvitationQuota(..) => {
  596. T::SetMembershipLeadInvitationQuotaProposalParameters::get()
  597. }
  598. ProposalDetails::SetReferralCut(..) => T::SetReferralCutProposalParameters::get(),
  599. ProposalDetails::CreateBlogPost(..) => T::CreateBlogPostProposalParameters::get(),
  600. ProposalDetails::EditBlogPost(..) => T::EditBlogPostProoposalParamters::get(),
  601. ProposalDetails::LockBlogPost(..) => T::LockBlogPostProposalParameters::get(),
  602. ProposalDetails::UnlockBlogPost(..) => T::UnlockBlogPostProposalParameters::get(),
  603. ProposalDetails::VetoProposal(..) => T::VetoProposalProposalParameters::get(),
  604. }
  605. }
  606. // Returns weight for the proposal creation according to parameters
  607. fn get_create_proposal_weight(
  608. general: &GeneralProposalParameters<T>,
  609. details: &ProposalDetailsOf<T>,
  610. ) -> Weight {
  611. let title_length = general.title.len();
  612. let description_length = general.description.len();
  613. match details {
  614. ProposalDetails::Signal(signal) => WeightInfoCodex::<T>::create_proposal_signal(
  615. signal.len().saturated_into(),
  616. title_length.saturated_into(),
  617. description_length.saturated_into(),
  618. ),
  619. ProposalDetails::RuntimeUpgrade(blob) => {
  620. WeightInfoCodex::<T>::create_proposal_runtime_upgrade(
  621. blob.len().saturated_into(),
  622. title_length.saturated_into(),
  623. description_length.saturated_into(),
  624. )
  625. }
  626. ProposalDetails::FundingRequest(params) => {
  627. WeightInfoCodex::<T>::create_proposal_funding_request(
  628. params.len().saturated_into(),
  629. description_length.saturated_into(),
  630. )
  631. }
  632. ProposalDetails::SetMaxValidatorCount(..) => {
  633. WeightInfoCodex::<T>::create_proposal_set_max_validator_count(
  634. title_length.saturated_into(),
  635. description_length.saturated_into(),
  636. )
  637. }
  638. ProposalDetails::CreateWorkingGroupLeadOpening(opening_params) => {
  639. WeightInfoCodex::<T>::create_proposal_create_working_group_lead_opening(
  640. opening_params.description.len().saturated_into(),
  641. title_length.saturated_into(),
  642. description_length.saturated_into(),
  643. )
  644. }
  645. ProposalDetails::FillWorkingGroupLeadOpening(..) => {
  646. WeightInfoCodex::<T>::create_proposal_fill_working_group_lead_opening(
  647. title_length.saturated_into(),
  648. description_length.saturated_into(),
  649. )
  650. }
  651. ProposalDetails::UpdateWorkingGroupBudget(..) => {
  652. WeightInfoCodex::<T>::create_proposal_update_working_group_budget(
  653. title_length.saturated_into(),
  654. description_length.saturated_into(),
  655. )
  656. }
  657. ProposalDetails::DecreaseWorkingGroupLeadStake(..) => {
  658. WeightInfoCodex::<T>::create_proposal_decrease_working_group_lead_stake(
  659. title_length.saturated_into(),
  660. description_length.saturated_into(),
  661. )
  662. }
  663. ProposalDetails::SlashWorkingGroupLead(..) => {
  664. WeightInfoCodex::<T>::create_proposal_slash_working_group_lead(
  665. description_length.saturated_into(),
  666. )
  667. }
  668. ProposalDetails::SetWorkingGroupLeadReward(..) => {
  669. WeightInfoCodex::<T>::create_proposal_set_working_group_lead_reward(
  670. title_length.saturated_into(),
  671. description_length.saturated_into(),
  672. )
  673. }
  674. ProposalDetails::TerminateWorkingGroupLead(..) => {
  675. WeightInfoCodex::<T>::create_proposal_terminate_working_group_lead(
  676. title_length.saturated_into(),
  677. description_length.saturated_into(),
  678. )
  679. }
  680. ProposalDetails::AmendConstitution(new_constitution) => {
  681. WeightInfoCodex::<T>::create_proposal_amend_constitution(
  682. new_constitution.len().saturated_into(),
  683. description_length.saturated_into(),
  684. )
  685. }
  686. ProposalDetails::SetMembershipPrice(..) => {
  687. WeightInfoCodex::<T>::create_proposal_set_membership_price(
  688. title_length.saturated_into(),
  689. description_length.saturated_into(),
  690. )
  691. }
  692. ProposalDetails::CancelWorkingGroupLeadOpening(..) => {
  693. WeightInfoCodex::<T>::create_proposal_cancel_working_group_lead_opening(
  694. description_length.saturated_into(),
  695. )
  696. }
  697. ProposalDetails::SetCouncilBudgetIncrement(..) => {
  698. WeightInfoCodex::<T>::create_proposal_set_council_budget_increment(
  699. title_length.saturated_into(),
  700. description_length.saturated_into(),
  701. )
  702. }
  703. ProposalDetails::SetCouncilorReward(..) => {
  704. WeightInfoCodex::<T>::create_proposal_set_councilor_reward(
  705. title_length.saturated_into(),
  706. description_length.saturated_into(),
  707. )
  708. }
  709. ProposalDetails::SetInitialInvitationBalance(..) => {
  710. WeightInfoCodex::<T>::create_proposal_set_initial_invitation_balance(
  711. title_length.saturated_into(),
  712. description_length.saturated_into(),
  713. )
  714. }
  715. ProposalDetails::SetInitialInvitationCount(..) => {
  716. WeightInfoCodex::<T>::create_proposal_set_initial_invitation_count(
  717. title_length.saturated_into(),
  718. description_length.saturated_into(),
  719. )
  720. }
  721. ProposalDetails::SetMembershipLeadInvitationQuota(..) => {
  722. WeightInfoCodex::<T>::create_proposal_set_membership_lead_invitation_quota(
  723. description_length.saturated_into(),
  724. )
  725. }
  726. ProposalDetails::SetReferralCut(..) => {
  727. WeightInfoCodex::<T>::create_proposal_set_referral_cut(
  728. title_length.saturated_into(),
  729. description_length.saturated_into(),
  730. )
  731. }
  732. ProposalDetails::CreateBlogPost(header, body) => {
  733. WeightInfoCodex::<T>::create_proposal_create_blog_post(
  734. title_length.saturated_into(),
  735. description_length.saturated_into(),
  736. header.len().saturated_into(),
  737. body.len().saturated_into(),
  738. )
  739. }
  740. ProposalDetails::EditBlogPost(_, header, body) => {
  741. let header_len = header.as_ref().map_or(0, |h| h.len());
  742. let body_len = body.as_ref().map_or(0, |b| b.len());
  743. WeightInfoCodex::<T>::create_proposal_edit_blog_post(
  744. title_length.saturated_into(),
  745. description_length.saturated_into(),
  746. header_len.saturated_into(),
  747. body_len.saturated_into(),
  748. )
  749. }
  750. ProposalDetails::LockBlogPost(..) => {
  751. WeightInfoCodex::<T>::create_proposal_lock_blog_post(
  752. title_length.saturated_into(),
  753. description_length.saturated_into(),
  754. )
  755. }
  756. ProposalDetails::UnlockBlogPost(..) => {
  757. WeightInfoCodex::<T>::create_proposal_unlock_blog_post(
  758. title_length.saturated_into(),
  759. description_length.saturated_into(),
  760. )
  761. .saturated_into()
  762. }
  763. ProposalDetails::VetoProposal(..) => {
  764. WeightInfoCodex::<T>::create_proposal_veto_proposal(
  765. title_length.saturated_into(),
  766. description_length.saturated_into(),
  767. )
  768. .saturated_into()
  769. }
  770. }
  771. }
  772. }
  773. impl<T: Trait> ProposalObserver<T> for Module<T> {
  774. fn proposal_removed(proposal_id: &<T as proposals_engine::Trait>::ProposalId) {
  775. <ThreadIdByProposalId<T>>::remove(proposal_id);
  776. let thread_id = Self::thread_id_by_proposal_id(proposal_id);
  777. proposals_discussion::ThreadById::<T>::remove(thread_id);
  778. }
  779. }